牛刀小試-《你好,李煥英》影評詞云
最近,賈玲導演的電影《你好,李煥英》在春節(jié)檔一眾電影中脫穎而出,成為了一匹黑馬,更是中國影史上第三部票房破50億的電影。

我們從豆瓣上看到,該影片當前評分是8.1分,接近30%的評價都是5顆星。今天我們就來從豆瓣上爬取一部分影評,結(jié)合jieba和pyecharts做個分詞和詞云圖。

打開開發(fā)者工具
首先,在豆瓣上找到電影《你好,李煥英》,下拉到影評區(qū),可以看到共有三十多萬條短評和七千多影評。短評數(shù)量太多了,我們還是數(shù)量較少的影評下手吧。接下來,在瀏覽器中按F12或者右鍵點擊檢查打開開發(fā)者工具,然后選擇network(網(wǎng)絡)選項,然后點擊上圖中影評區(qū)的“全部7494條”,就可以在開發(fā)者工具中看到一系列的請求。

尋找目標URL

將影評區(qū)拖到最底部,將會看到影評被分成來幾百個頁面,然后我們清空開發(fā)者工具中的捕獲的請求,點擊下一頁,在請求列表中找到第一條,這就是我們翻頁的通用請求了:每頁20條,使用start參數(shù)判斷起始位置,這樣循環(huán)300多次就可以獲取到全部的影評了。
等等,貌似還少了什么?由于影評內(nèi)容較長,評論只顯示了一部分,要想看到全部內(nèi)容,還需要點擊一下展開全文。
點一下展開,發(fā)現(xiàn)瀏覽器發(fā)出一條這樣的請求,剛好返回的json就是完整的評論內(nèi)容。依次點擊后面的幾條影評,也都是同樣格式的請求,只有full前面的數(shù)字發(fā)生了變化,可以斷定這串數(shù)字就是這條影評的id了。那么如何知道每一條影評的id是什么呢?
檢查目標元素
鼠標放到展開按鈕上,右鍵點擊檢查元素,我們可以發(fā)現(xiàn),頁面上的每一條影評都對應了一個div,這個div有一個data-cid的參數(shù),它的值剛好就是上面我們請求中的id,所以我們只要在每次請求頁面時,遍歷review-list中所有的div,獲取到data-cid的值,再把影評id循環(huán)代入到上面的url中請求完整影評內(nèi)容即可。
爬取完影評評論并保存到本地,然后使用jieba分詞將評論文章分割成單詞,然后去除停用詞并統(tǒng)計詞頻。
大功告成
最后一步就是利用我們前面提到的pyecharts中的wordcloud來制作詞云了。選擇一張照片作為詞云的形狀,然后導入單詞及其詞頻作為權(quán)重,輸出文件即可。
詳細代碼:
- import requests
- import random, time, json
- from bs4 import BeautifulSoup as bs
- import jieba
- import pandas as pd
- from pyecharts import charts, options
- url1 = 'https://movie.douban.com/subject/34841067/reviews?start={}'
- url2 = 'https://movie.douban.com/j/review/{}/full'
- header = '''Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
- Accept-Encoding: gzip, deflate, br
- Accept-Language: zh-CN,zh;q=0.9
- Connection: keep-alive
- Cookie: bid=RWSCuDu3-hA; douban-fav-remind=1; __gads=ID=f22d1fef6a644a7a-228e1b3a32c50048:T=1607935481:RT=1607935481:S=ALNI_MZwRU5qCsyehoDFgRdf7D5PRBqqCg; ll="108288"; _pk_ref.100001.4cf6=%5B%22%22%2C%22%22%2C1614764251%2C%22https%3A%2F%2Fwww.baidu.com%2Flink%3Furl%3DlzDNd94NFnBQCDIqNI00Il5NwjZoARpWz1lQy5MGKdL26rV5yrb1N1HIoGzoKu5k%26wd%3D%26eqid%3Dda5556d4000016f000000003603f58d7%22%5D; ap_v=0,6.0; __utmc=30149280; __utmz=30149280.1614764252.6.6.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __utma=223695111.1843127241.1614764252.1614764252.1614764252.1; __utmc=223695111; __utmz=223695111.1614764252.1.1.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __yadk_uid=rcNjENFDHY62mSmlZqJtXPKJZUfQkM75; _vwo_uuid_v2=D771633F1FBA119FD38FD79DCE082F26D|35b2c7beff079fe25b62163affe94ce8; _pk_id.100001.4cf6=2e0a301ce93b85e0.1614764251.1.1614764408.1614764251.; __utma=30149280.1170719909.1607935481.1614764252.1614766647.7; __utmt=1; __utmb=30149280.2.9.1614766647; dbcl2="152966201:ETujHWfkU2g"; ck=1WtR
- Host: movie.douban.com
- Referer: https://accounts.douban.com/
- Sec-Fetch-Dest: document
- Sec-Fetch-Mode: navigate
- Sec-Fetch-Site: same-site
- Sec-Fetch-User: ?1
- Upgrade-Insecure-Requests: 1
- User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36'''
- headers = {x.split(': ')[0]: x.split(': ')[1] for x in header.split('\n')}
- session = requests.session()
- session.headers = headers
- result1 = []
- for i in range(0, 365):
- print(i)
- try:
- r = session.get(url1.format(i))
- r.close()
- content = bs(r.text)
- result1.extend(x.get('data-cid') for x in content.select('div.review-list > div'))
- except:
- pass
- time.sleep(random.randrange(3, 20))
- result2 = []
- for j in result1:
- print(result1.index(j))
- try:
- r = session.get(url2.format(j))
- r.close()
- data = json.loads(r.text)
- result2.append(data)
- except:
- pass
- time.sleep(random.randrange(10, 20))
- review = []
- for i in result2:
- html = bs(i['html'])
- review.append(html.text)
- for i in ['賈玲', '賈曉玲', '沈騰', '張小斐', '李煥英', '光林', '陳赫', '沈光林', '冷特']:
- jieba.add_word(i)
- words = []
- for x in review:
- cut = jieba.lcut(x)
- words.extend(cut)
- with open('stopwords.txt', 'r')as f:
- stop = [x.strip() for x in f.readlines()]
- f.close()
- df = pd.DataFrame(words, columns=['words'])
- df_count = pd.DataFrame(df.groupby('words').size())
- count = df_count.loc[[x for x in df_count.index.values if x not in stop]].sort_values(0, ascending=False)
- cloud = charts.WordCloud(init_opts=options.InitOpts(width='2000px', height='2000px'))
- cloud.add('你好,李煥英',
- data_pair=[(x, count.loc[x].values[0] / 100) for x in count.index.values],
- mask_image='jialing.jpeg',
- word_size_range=[10, 300])
- cloud.render('你好李煥英.html')