2022年 11月 7日

Python爬虫获取豆瓣电影短评

Python爬虫获取豆瓣电影短评

参考:使用Python爬虫获取豆瓣影评,并用词云显示
该作者提供了基本的思路,但是在运行程序过程中发现了一些问题并进行一些修改:

  1. 导入了re后,却没有写正则表达式,最后也爬取不出结果。因为我是初学者,不清楚其item.findall一句是什么意思,因此我重新写了正则表达式和相关的函数。
  2. 如果原作者的这个函数生成eachCommentList当中每个元素都是str类型,那么写入txt文档是ok的,但是如果是列表,则会在写入文件时报错。
  3. 本文只写爬虫部分,词云部分较简单,可参考笔者其他博客。

全代码展示

先展示一下全部的代码,编译环境:PyCharm。
爬取的网站是:当幸福来敲门 短评

import urllib.request
from bs4 import BeautifulSoup
import re

findcomment = re.compile(r'<span class="short">(.*)</span>')  # 根据网页的源代码写一个正则表达式,对应评论的文本

def GetTxt(start):
    url = 'https://movie.douban.com/subject/1849031/comments?start=' + \
             str(start) + '&limit=20&sort=new_score&status=P'
    head = {  # 模拟浏览器头部信息,向服务器发送消息。Firefox浏览器头部信息如下:
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0"
    }
    request = urllib.request.Request(url, headers=head)
    response = urllib.request.urlopen(request)
    html_data = response.read().decode('utf-8')
    commentlist = []
    soup = BeautifulSoup(html_data, 'html.parser')
    comment_div_lits = soup.find_all('div', class_='comment')
    for item in comment_div_lits:
        eachcomment = re.findall(findcomment, str(item))  # 如果不把item转换为str类型findall函数会报错
        print(eachcomment)  # 检查一下每一条爬取的是否正确
        commentlist.append(eachcomment)
    f = open('content.txt', 'a', encoding='utf-8')
    for comment in commentlist:
        if comment != []:  # 爬取过程发现有些是空列表
            f.write(comment[0])
    f.close()


def main():
    for i in range(1, 201, 20):  # 原本想多爬取一些,但是网站只允许看到前200条,之后需要登录
        print(i)
        GetTxt(i)


if __name__ == '__main__':
    main()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

接下来尽可能详细地逐段讲解.

导入库

urllib是访问url,获取数据必须的库。在Python2中似乎是urllib2,但在Python3中,分成了urllib.request和urllib.error。
BeautifulSoup库我也不是很明白,但是可以理解成解析一个网页结构的功能,之后我们可以根据网页的源代码提取所需要的部分。(之后系统学了爬虫我会修正补充关于beautifulsoup这一部分)。
re用来写正则表达式

import urllib.request
from bs4 import BeautifulSoup
import re
  • 1
  • 2
  • 3

正则表达式

因为我只需要影评部分,所以只需要在网站源代码找到相应的即可,如图:
Firefox浏览器 右键->检查 即可查看源代码。鼠标放在语句上,会相应地在网站页面标亮其对应的内容,非常方便。千万不能找错,我第一次写成了comment-content那个,不是text了。
在这里插入图片描述

findcomment = re.compile(r'<span class="short">(.*)</span>')  # 根据网页的源代码写一个正则表达式,对应评论的文本
  • 1

如果还需要其他结构的正则表达式,就去找相应的源代码。有一个博客非常值得借鉴,我读了之后对比着看就知道哪一块是什么了:
Python爬虫超详细讲解(零基础入门,老年人都看的懂)
(确实是零基础入门,因为我这老年废柴看懂了…)

核心——获取url的相关信息

思路:函数的变量是一个整数,利用一个循环,不断访问页面(当然是第1页、第2页…第n页),从而摘取每一页上面的评论信息

def GetTxt(start):
    url = 'https://movie.douban.com/subject/1849031/comments?start=' + \
             str(start) + '&limit=20&sort=new_score&status=P'
    head = {  # 模拟浏览器头部信息,向服务器发送消息。Firefox浏览器头部信息如下:
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0"
    }
    request = urllib.request.Request(url, headers=head)
    response = urllib.request.urlopen(request)
    html_data = response.read().decode('utf-8')
    commentlist = []
    soup = BeautifulSoup(html_data, 'html.parser')
    comment_div_lits = soup.find_all('div', class_='comment')
    for item in comment_div_lits:
        eachcomment = re.findall(findcomment, str(item))  # 如果不把item转换为str类型findall函数会报错
        print(eachcomment)  # 检查一下每一条爬取的是否正确
        commentlist.append(eachcomment)
    f = open('content.txt', 'a', encoding='utf-8')
    for comment in commentlist:
        if comment != []:  # 爬取过程发现有些是空列表
            f.write(comment[0])
    f.close()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

拆分开来讲解:

    url = 'https://movie.douban.com/subject/1849031/comments?start=' + \
             str(start) + '&limit=20&sort=new_score&status=P'
  • 1
  • 2

这就是我们要寻找的评论的网址,第一串字符,是第一页的网址,随后我们加上循环的正数start,从而访问下一个页面。分析网页看到,一页有20条,那么很显然start=0之后,下一个值是20,在下一个40,以此类推。可以手动输入20,40等,再加上第二串字符,看看是否是相应的页面。
在这里插入图片描述接下来要伪装自己,假装自己是浏览器访问了页面,否则会418报错。也就是说写一个头部信息:

    head = {  # 模拟浏览器头部信息,向服务器发送消息。Firefox浏览器头部信息如下:
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0"
    }
  • 1
  • 2
  • 3

浏览器头部信息应该是可以随便写,但如果你想查看你的浏览器的,可以点击此处。

    for item in comment_div_lits:
        eachcomment = re.findall(findcomment, str(item))  # 如果不把item转换为str类型findall函数会报错
        print(eachcomment)  # 检查一下每一条爬取的是否正确
        commentlist.append(eachcomment)
  • 1
  • 2
  • 3
  • 4

对于每一个comment_div_lits的项,一定要转化为str类型,否则会报错

Traceback (most recent call last):
  File "C:\Users\Lenovo\Desktop\1 Project\爬虫\ceshi.py", line 37, in <module>
    main()
  File "C:\Users\Lenovo\Desktop\1 Project\爬虫\ceshi.py", line 33, in main
    GetTxt(i)
  File "C:\Users\Lenovo\Desktop\1 Project\爬虫\ceshi.py", line 20, in GetTxt
    eachcomment = re.findall(findcomment, item)  # 如果不把item转换为str类型findall函数会报错
  File "E:\Python\lib\re.py", line 241, in findall
    return _compile(pattern, flags).findall(string)
TypeError: expected string or bytes-like object
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

我们可以打印出每一条评论,观察爬取的是否正确。从而注意到,一些评论没有爬取成功,获得的是一个空列表[]。因此在接下来写入文件的时候,必须要讨论是否是[]的情况,否则index会报错。

    f = open('content.txt', 'a', encoding='utf-8')
    for comment in commentlist:
        if comment != []:  # 爬取过程发现有些是空列表
            f.write(comment[0])
    f.close()
  • 1
  • 2
  • 3
  • 4
  • 5

主函数调用

最后主函数调用上面的函数即可,如前所述,我们的网址应该分别是从0,20,40开始,即设置一个变量以20为步长循环。本来想爬4000条数据,但是从跟201开始就不让未登录用户看到了。

def main():
    for i in range(1, 201, 20):  # 原本想多爬取一些,但是网站只允许看到前200条,之后需要登录
        # print(i)
        GetTxt(i)


if __name__ == '__main__':
    main()

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

结果展示

在这里插入图片描述

补充

上面的代码运行完发现写入txt的全都是一坨,巨难看,所以希望换个行写,把f.write()更改一下。
注意:在print里,只需要print('\n')就能换行,但是f.write('\n')只能打印出\n来,要写作f.write('\r\n')

    f = open('content.txt', 'a', encoding='utf-8')
    for comment in commentlist:
        if comment != []:  # 爬取过程发现有些是空列表
            f.write(comment[0] + '\r\n')
    f.close()
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

错漏之处敬请批评指正!