爬蟲實戰之《流浪地球》豆瓣影評分析(一)

  1. 背景與挖掘目標
  2. 獲取豆瓣評論數據
  3. 分析好評與差評的關鍵信息
  4. 分析評論數量及評分與時間的關係
  5. 分析評論者的城市分佈情況

1. 背景與挖掘目標

豆瓣(douban)是一個社區網站。網站由楊勃(網名“阿北”) 創立於2005年3月6日。該網站以書影音起家,提供關於書籍、電影、音樂等作品的信息,無論描述還是評論都由用戶提供(User-generated content,UGC),是Web 2.0網站中具有特色的一個網站。
網站還提供書影音推薦、線下同城活動、小組話題交流等多種服務功能,它更像一個集品味系統(讀書、電影、音樂)、表達系統(我讀、我看、我聽)和交流系統(同城、小組、友鄰)於一體的創新網絡服務,一直致力於幫助都市人羣發現生活中有用的事物。

2019年2月5日電影《流浪地球》正式在中國內地上映。根據劉慈欣同名小說改編,影片故事設定在2075年,講述了太陽即將毀滅,已經不適合人類生存,而面對絕境,人類將開啓“流浪地球”計劃,試圖帶着地球一起逃離太陽系,尋找人類新家園的故事。
《流浪地球》舉行首映的時候,口碑好得出奇,所有去看片的業界大咖都發出了同樣讚歎。文化學者戴錦華說:“中國科幻電影元年開啓了。”導演徐崢則說,“里程碑式的電影,絕對是世界級別的。
可是公映之後,《流浪地球》的豆瓣評分卻從8.4一路跌到了7.9。影片頁面排在第一位的,是一篇一星影評《流浪地球,不及格》。文末有2.8萬人點了“有用”,3.6萬人點了“沒用”。
關於《流浪地球》的觀影評價,已經變成了一場逐漸失控的輿論混戰,如“槍稿”作者灰狼所說,“關於它的輿論,已經演化成‘政治正確、水軍橫行、自來水滅差評、道德綁架、戰狼精神。’”

挖掘目標:

  1. 獲取豆瓣評論數據
  2. 分析好評與差評的關鍵信息
  3. 分析評論數量及評分與時間的關係
  4. 分析評論者的城市分佈情況

2. 獲取豆瓣評論數據

目標網址:https://movie.douban.com/subject/26266893/comments?status=P

數據爬取 selenium
短評解析 XPath
數據轉化 DataFrame
翻頁 selenium

獲取首頁數據

通過selenium和chromedriver獲取數據

from selenium import webdriver
driver = webdriver.Chrome()

url = 'https://movie.douban.com/subject/26266893/comments?status=P'
driver.get(url)

獲取用戶名

通過driver.page_source獲取網頁源碼
我們使用Xpath解析網頁
通過下列代碼獲取names並命名

names = dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/a/text()')

獲取評價打分

通過谷歌開發者工具可以看到,class的屬性決定評星。allstar10是一星,allstar50是五星。所以我們想要獲取評價就要獲取class的屬性
在這裏插入圖片描述
同理:

dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/span[2]/@class')

然而發現結果是:
在這裏插入圖片描述
顯然,這其中有未打分的(comment-time),要對這種進行篩選。

獲取評分時間

通過谷歌開發者工具發現,時間是在span的title屬性裏面

times = dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/span[3]/@title')

獲取短評內容

messages = dom.xpath('//div[@class="comment-item"]//div[@class="comment"]/p/span[@class="short"]/text()')

獲取贊同量

votes = dom.xpath('//div[@class="comment-item"]//div[@class="comment"]/h3/span[@class="comment-vote"]/span[@class="votes"]/text()') # 贊同量

獲取用戶主頁網

user_url = names = dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/a/@href') # 用戶主頁網址

一直到上面這,都爬的好好的,然後當我想用request進入某個主頁網址的時候,遇到了418反爬
在這裏插入圖片描述
這裏我選擇的是用cookie。 登錄獲取一下cookies ,然後轉換成字典(因爲個人隱私,我刪了幾個數字):

cookies_str = 'bid=qKOdCLIajIs; _pk_ses.100001.4cf6=*; ap_v=0,6.0; __utma=30149280.688453215.1585988569.1585988569.1585988569.1; __utmc=30149280; __utmz=30149280.1585988569.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utma=223695111.1438858643.1585988569.1585988569.1585988569.1; __utmb=223695111.0.10.185988569; __utmc=223695111; __utmz=223695111.1585988569.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __utmb=30149280.1.10.1585988569; ps=y; dbcl2="214558448:XIwFTrlzX1s"; ck=I1oq; _pk_id.100001.4cf6=279739e4a763236.1585988569.1.1585989402.1585988569.; push_noty_num=0; push_doumail_num=0'
cookies = {}
# 拆分
for i in cookies_str.split(';'):
    key,value = i.split('=',1)
    cookies[key] = value

cookies

好的,加了cookie之後發現還是418,去翻了翻博客,決定加一下頭文件:

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36'}

然後再爬,終於成功了
在這裏插入圖片描述

獲取用戶居住地和入會時間信息

address = dom_url.xpath('//div[@class="user-info"]/a/text()')  # 用戶居住地
load_time = dom_url.xpath('//div[@class="basic-info"]//div[@class="pl"]/text()') # 用戶加入時間

因爲一個頁面所以的評價都有一個主頁,所以用for做個遍歷。
同時,導入時間庫,爬一次睡兩秒防止反爬,部分代碼:

import time
cities = [] #用戶居住地址列表
load_times = [] # 用戶加入時間列表
for i in user_url:
    web_data = requests.get(i,cookies=cookies,headers=headers)
    dom_url = etree.HTML(web_data.text,etree.HTMLParser(encoding='utf-8'))
    address = dom_url.xpath('//div[@class="basic-info"]//div[@class="user-info"]/a/text()')  # 用戶居住地
    load_time = dom_url.xpath('//div[@class="basic-info"]//div[@class="pl"]/text()') # 用戶加入時間
    cities.append(address)
    load_times.append(load_time)
    time.sleep(2)   # 2秒訪問一次反爬

結果:cities
load_times
算是初步爬取成功

單頁數據整理

獲取到的數據要進行整理,去掉一些沒用的數據,去掉缺失值,然後dataframe表格.

1. 評分數據ratings

如果沒有評分,就保留一個空字符串;有評分打印評分。
先導入re模塊。我們需要的是一個int型數據,使用==int()轉換類型,加個[0]==來獲取引號裏面的值,,就有:

import re
["" if 'rating' not in i else int(re.findall('\d{2}', i)[0]) for i in ratings]  # 評分數據整理
2. load_times

我們需要的是後面那個元素的日期.
可以使用strip將前後的元素剔除,如果沒有加入時間使用”“代替,不需要最後兩個字,所以加一個==[:-2]==

load_times = ["" if i==[] else i[1].strip()[:-2] for i in load_times] # 入會時間整理
3. cities

我們需要放在一個列表中,而不是嵌套列表

cities = ["" if i==[] else i[0] for i in cities] # 居住地的數據整理

最後導入庫準備建表:

import pandas as pd
data = pd.DataFrame({
    '用戶名':names,
    '居住城市':cities,
    '加入時間':load_times,
    '評分':ratings,
    '發表時間':times,
    '短評正文':messages,
    '贊同數量':votes
})

但是報錯:arrays must all be same length
查找了一番,發現times只有19個數據,其他的都20個數據.
然後改了一下times的查找方式:

times = dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/span[@class="comment-time "]/@title')# 評論發佈時間

然後數據就保存好了,但是這個數據是一個頁面的數據,
所以要把這個data的構建定義成一個函數:

def get_web_data(dom=None,cookies=None):
    names = dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/a/text()') # 用戶名
    ratings= dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/span[2]/@class') # 評分
    times = dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/span[@class="comment-time "]/@title')# 評論發佈時間
    messages = dom.xpath('//div[@class="comment-item"]//div[@class="comment"]/p/span[@class="short"]/text()') # 短評正文
    user_url = dom.xpath('//div[@class="comment-item"]//span[@class="comment-info"]/a/@href') # 用戶主頁網址
    votes = dom.xpath('//div[@class="comment-item"]//div[@class="comment"]/h3/span[@class="comment-vote"]/span[@class="votes"]/text()') # 贊同量
    cities = [] #用戶居住地址列表
    load_times = [] # 用戶加入時間列表
    for i in user_url:
        web_data = requests.get(i,cookies=cookies,headers=headers)
        dom_url = etree.HTML(web_data.text,etree.HTMLParser(encoding='utf-8'))
        address = dom_url.xpath('//div[@class="basic-info"]//div[@class="user-info"]/a/text()')  # 用戶居住地
        load_time = dom_url.xpath('//div[@class="basic-info"]//div[@class="pl"]/text()') # 用戶加入時間
        cities.append(address)
        load_times.append(load_time)
        time.sleep(2)   # 2秒訪問一次反爬
    ratings = ["" if 'rating' not in i else int(re.findall('\d{2}', i)[0]) for i in ratings]  # 評分數據整理
    load_times = ["" if i==[] else i[1].strip()[:-2] for i in load_times] # 入會時間整理
    cities = ["" if i==[] else i[0] for i in cities] # 居住地的數據整理
    data = pd.DataFrame({
        '用戶名':names,
        '居住城市':cities,
        '加入時間':load_times,
        '評分':ratings,
        '發表時間':times,
        '短評正文':messages,
        '贊同數量':votes
    })
    return data

接下來就是把這個函數應用於每一頁,所以我們需要判斷網頁是否已經加載。
設置一個死循環whlie ture,除非滿足到最後一頁,否轉一直進行單頁讀取,還要設置一個10秒沒有加載好就報錯的警示。
噹噹前頁面最後一個評論的主頁可以被點擊,就默認這個頁面被加載進來,否轉就終止程序.

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

  wait.until(
        EC.element_to_be_clickable(
            (By.CSS_SELECTOR,'#comments > div:nth-child(20) > div.comment > h3 > span.comment-info > a')
        )
    )

最後一頁,可以通過selector來判斷,如果沒有 #paginator > a.next 則是尾頁,或者有 #paginator > span 是尾頁,但是首頁也有,所以選擇第一種。

代碼:

all_data = pd.DataFrame()
wait = WebDriverWait(driver, 10)  # 10秒內能加載就操作,不能就報錯
while True:
    
    wait.until(
        EC.element_to_be_clickable(
            (By.CSS_SELECTOR,'#comments > div:nth-child(20) > div.comment > h3 > span.comment-info > a')
        )
    )
    
    dom = etree.HTML(driver.page_source,etree.HTMLParser(encoding='utf-8'))
    data = get_web_data(dom=dom,cookies=cookies)
    all_data = pd.concat([all_data,data],axis=0) # 按列拼接
    
    # 當後頁無法點擊,終止循環
    #paginator > span
    #paginator > a.next
    
    if driver.find_element_by_css_selector('#paginator > a.next')=[]: # 判斷是否還有“後頁”按鈕
        break
        
    confirm_bnt = wait.until(
        EC.element_to_be_clickable(
            (By.CSS_SELECTOR,"#paginator > a.next")
        )
    )
    confirm_bnt.click() # 翻頁
    

大概十幾二十分鐘,就爬取完了之後就可以保存了
all_data.to_csv('doubanliulangdiqiu.csv', index=None.encoding='GB18030')
編碼方式可以自己根據需要決定。
爲了爬取所有數據,還是自己登錄一下比較好

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章