最近,在工作之余自学python,越发膨胀,觉得自己厉害了一丢丢,前段时间看过秒先生,画面还挺好,立意嘛就图一乐呵,于是想用影评分析一下,看看大家都怎么看待妙先生这部电影。
分析网站
如图所示,我们要读取的是该页面的短评部分,文章是有分页,从浏览器地址栏我们看到有start参数,在第二页时参数值为20,因此我们可以知道,豆瓣影评的分页大小为20 初始值为0,可以看每段短评都有展开字样,这里我们就不再展开,只分析短评部分(省事)。
开始编码
1.进入主页面,使用beautifulsoap 解析获取到的html标签以及数据
url = "https://movie.douban.com/subject/34888476/reviews"
headers = getHeader()
request = urllib3.PoolManager()
response = request.request("GET", url, headers=headers)
htmlText = response.data
2.获取评论总数,然后根据评论总数和分页参数构建请求
# 获取每页具体影评
def getRemarks(url):
headers=getHeader()
global all_remark
html = getXHRHtml(url)
bs = BeautifulSoup(html, "html.parser")
remarks = bs.find(attrs={'class': 'review-list'}).find_all(attrs={'class':'short-content'})
for item in remarks:
text= item.text
text='|'.join(text.split())
strArr= text.split(sep="|")
all_remark=all_remark+strArr[1]
# 获取评论总条数
remarks_title = bs.find(attrs={'id':'content'}).find('h1').text
remarks_count = remarks_title[remarks_title.index('(')+1:remarks_title.index(')')]
remarks_count = int(remarks_count,base=10)
# 已知每页显示20条 ,翻页
for start in range(0,remarks_count,20):
url_temp=f"https://movie.douban.com/subject/34888476/reviews?start={start}"
# 使用多线程加载分页的异步数据,挨个加载效率太低
t = Thread(target=getRemarks, args=[url_temp])
t.start()
threads.append(t)
# 等待所有线程结束
for thread in threads:
thread.join()
print(all_remark)
这里我们通过多线程请求,避免在网页响应的处理等待。在具体获取分页的影评时我们要通过selenium.webdriver库通过浏览器去获取异步加载的数据。
最后通过解析过来的数据,我们将其生成一个词云图,分析一下大家都在讨论什么。
词云我是借鉴的这位大佬的 文章
最终结果,如图
最后得出结论我太菜了,只知道大家一致认为 妙先生里没有好人。
全部代码
import urllib3
from bs4 import BeautifulSoup
import numpy as np
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import time
from threading import Thread
import re
import jieba
import collections
from PIL import Image
import wordcloud
import matplotlib.pyplot as plt
# 全局变量
# 创建进程列表
threads = []
all_remark=""
# 获取模拟头
def getHeader():
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36'}
return headers
# 异步获取页面数据
def getXHRHtml(url):
# 使用Google浏览器
googledriver=r"C:\Users\weiyongjian\AppData\Local\Google\Chrome\Application\chromedriver.exe"
# 不弹窗口
options = Options()
options.headless = True
google=webdriver.Chrome(options=options,executable_path=googledriver)
try:
google.get(url)
google.refresh()
time.sleep(4)
except Exception as e:
pass
html=google.page_source
google.close()
return html
# 获取具体影评
def getRemarks(url):
headers=getHeader()
global all_remark
html = getXHRHtml(url)
bs = BeautifulSoup(html, "html.parser")
remarks = bs.find(attrs={'class': 'review-list'}).find_all(attrs={'class':'short-content'})
for item in remarks:
text= item.text
text='|'.join(text.split())
strArr= text.split(sep="|")
all_remark=all_remark+strArr[1]
# 进入影评页拿取影评信息
def getRemark(url):
headers = getHeader()
request = urllib3.PoolManager()
response = request.request("GET", url, headers=headers)
htmlText = response.data
bs = BeautifulSoup(htmlText, "html.parser")
# 获取评论总条数
remarks_title = bs.find(attrs={'id':'content'}).find('h1').text
remarks_count = remarks_title[remarks_title.index('(')+1:remarks_title.index(')')]
remarks_count = int(remarks_count,base=10)
# 已知每页显示20条 ,翻页
for start in range(0,remarks_count,20):
url_temp=f"https://movie.douban.com/subject/34888476/reviews?start={start}"
# 使用多线程加载分页的异步数据,挨个加载效率太低
t = Thread(target=getRemarks, args=[url_temp])
t.start()
threads.append(t)
# 等待所有线程结束
for thread in threads:
thread.join()
print(all_remark)
# 处理结果生成词云
# 文本预处理
pattern = re.compile(u'\t|\n|\.|-|:|;|\)|\(|\?|"|》|《|”') # 定义正则表达式匹配模式
string_data = re.sub(pattern, '', all_remark) # 将符合模式的字符去除
# 文本分词
seg_list_exact = jieba.cut(string_data, cut_all=False) # 精确模式分词
object_list = []
remove_words = [u'的', u',', u'和', u'是', u'随着', u'对于', u'对', u'等', u'能', u'都', u'。', u' ', u'、', u'中', u'在', u'了',
u'通常', u'如果', u'我们', u'需要', u'我', u'有', u'展开'] # 自定义去除词库
for word in seg_list_exact: # 循环读出每个分词
if word not in remove_words: # 如果不在去除词库中
object_list.append(word) # 分词追加到列表
# 词频统计
word_counts = collections.Counter(object_list) # 对分词做词频统计
word_counts_top10 = word_counts.most_common(10) # 获取前10最高频的词
print(word_counts_top10) # 输出检查
# 词频展示
mask = np.array(Image.open('wordcloud.png')) # 定义词频背景
wc = wordcloud.WordCloud(
font_path='C:/Windows/Fonts/simhei.ttf', # 设置字体格式
mask=mask, # 设置背景图
max_words=200, # 最多显示词数
max_font_size=100 # 字体最大值
)
wc.generate_from_frequencies(word_counts) # 从字典生成词云
image_colors = wordcloud.ImageColorGenerator(mask) # 从背景图建立颜色方案
wc.recolor(color_func=image_colors) # 将词云颜色设置为背景图方案
plt.imshow(wc) # 显示词云
plt.axis('off') # 关闭坐标轴
plt.show() # 显示图像
if __name__ == '__main__':
url = "https://movie.douban.com/subject/34888476/reviews"
getRemark(url)