用python寫一個豆瓣短評通用爬蟲(登錄、爬取、可視化)


原創技術公眾號:bigsai,本文在1024發布,祝大家節日快樂,心想事成。

@

前言

在本人上的一門課中,老師對每個小組有個任務要求,介紹和完成一個小模塊、工具知識的使用。然而我所在的組剛好遇到的是python爬蟲的小課題。

心想這不是很簡單嘛,搞啥呢?想着去搞新的時間精力可能不太夠,索性自己就把豆瓣電影的評論(短評)搞一搞吧。

之前有寫過哪吒那篇類似的,但今天這篇要寫的像姨母般詳細。本篇主要實現的是對任意一部電影短評(熱門)的抓取以及可視化分析。 也就是你只要提供鏈接和一些基本信息,他就可以
在這里插入圖片描述

分析

對於豆瓣爬蟲,what shold we 考慮?怎么分析呢?豆瓣電影首頁

這個首先的話嘗試就可以啦,打開任意一部電影,這里以姜子牙為例。打開姜子牙你就會發現它是非動態渲染的頁面,也就是傳統的渲染方式,直接請求這個url即可獲取數據。但是翻着翻着頁面你就會發現:未登錄用戶只能訪問優先的界面,登錄的用戶才能有權限去訪問后面的頁面。

image-20201022195020410

所以這個流程應該是 登錄——> 爬蟲——>存儲——>可視化分析

這里提一下環境和所需要的安裝裝,環境為python3,代碼在win和linux可成功跑,如果mac和linux不能跑友字體亂碼問題還請私我。其中pip用到包如下,直接用清華 鏡像下載不然很慢很慢(夠貼心不)。

pip install requests -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install numpy -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install xlrd -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install xlwt -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install bs4 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install lxml -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install wordcloud -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install jieba -i https://pypi.tuna.tsinghua.edu.cn/simple

登錄

豆瓣的登錄地址

進去后有個密碼登錄欄,我們要分析在登錄的途中發生了啥,打開F12控制台是不夠的,我們還要使用Fidder抓包。

在這里插入圖片描述

打開F12控制台然后點擊登錄,多次試探之后發現登錄接口也很簡單:

在這里插入圖片描述

查看請求的參數發現就是普通請求,無加密,當然這里可以用fidder進行抓包,這里我簡單測試了一下用錯誤密碼進行測試。如果失敗的小伙伴可以嘗試手動登陸再退出這樣再跑程序。

image-20201022195625220

這樣編寫登錄模塊的代碼:

url='https://accounts.douban.com/j/mobile/login/basic'
header={'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
'Referer': 'https://accounts.douban.com/passport/login_popup?login_source=anony',
        'Origin': 'https://accounts.douban.com',
 'content-Type':'application/x-www-form-urlencoded',
 'x-requested-with':'XMLHttpRequest',
 'accept':'application/json',
 'accept-encoding':'gzip, deflate, br',
 'accept-language':'zh-CN,zh;q=0.9',
 'connection': 'keep-alive'
 ,'Host': 'accounts.douban.com'
 }
data={
    'ck':'',
    'name':'',
    'password':'',
    'remember':'false',
    'ticket':''
}
def login(username,password):
    global  data
    data['name']=username
    data['password']=password
    data=urllib.parse.urlencode(data)
    print(data)
    req=requests.post(url,headers=header,data=data,verify=False)
    cookies = requests.utils.dict_from_cookiejar(req.cookies)
    print(cookies)
    return cookies

這塊高清之后,整個執行流程大概為:
在這里插入圖片描述

爬取

成功登錄之后,我們就可以攜帶登錄的信息訪問網站為所欲為的爬取信息了。雖然它是傳統交互方式,但是每當你切換頁面時候會發現有個ajax請求。
在這里插入圖片描述
這部分接口我們可以直接拿到評論部分的數據,就不需要請求整個頁面然后提取這部分的內容了。而這部分的url規律和之前分析的也是一樣,只有一個start表示當前的條數在變化,所以直接拼湊url就行。

也就是用邏輯拼湊url一直到不能正確操作為止。

https://movie.douban.com/subject/25907124/comments?percent_type=&start=0&其他參數省略
https://movie.douban.com/subject/25907124/comments?percent_type=&start=20&其他參數省略
https://movie.douban.com/subject/25907124/comments?percent_type=&start=40&其他參數省略

對於每個url訪問之后如何提取信息呢?
我們根據css選擇器進行篩選數據,因為每個評論他們的樣式相同,在html中就很像一個列表中的元素一樣。

再觀察我們剛剛那個ajax接口返回的數據剛好是下面紅色區域塊,所以我們直接根據class搜素分成若干小組進行曹祖就可以。

在這里插入圖片描述

在具體的實現上,我們使用requests發送請求獲取結果,使用BeautifulSoup去解析html格式文件。
而我們所需要的數據也很容易分析對應部分。

image-20201022210917778

實現的代碼為:

import requests
from  bs4 import BeautifulSoup
url='https://movie.douban.com/subject/25907124/comments?percent_type=&start=0&limit=20&status=P&sort=new_score&comments_only=1&ck=C7di'

header = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
}
req = requests.get(url,headers=header,verify=False)
res = req.json() # 返回的結果是一個json
res = res['html']
soup = BeautifulSoup(res, 'lxml')
node = soup.select('.comment-item')
for va in node:
    name = va.a.get('title')
    star = va.select_one('.comment-info').select('span')[1].get('class')[0][-2]
    comment = va.select_one('.short').text
    votes=va.select_one('.votes').text
    print(name, star,votes, comment)

這個測試的執行結果為:

image-20201022220333519

儲存

數據爬取完就要考慮存儲,我們將數據儲存到cvs中。

使用xlwt將數據寫入excel文件中,xlwt基本應用實例:

import xlwt

#創建可寫的workbook對象
workbook = xlwt.Workbook(encoding='utf-8')
#創建工作表sheet
worksheet = workbook.add_sheet('sheet1')
#往表中寫內容,第一個參數 行,第二個參數列,第三個參數內容
worksheet.write(0, 0, 'bigsai')
#保存表為test.xlsx
workbook.save('test.xlsx')

使用xlrd讀取excel文件中,本案例xlrd基本應用實例:

import xlrd
#讀取名稱為test.xls文件
workbook = xlrd.open_workbook('test.xls')
# 獲取第一張表
table =  workbook.sheets()[0]  # 打開第1張表
# 每一行是個元組
nrows = table.nrows
for i in range(nrows):
    print(table.row_values(i))#輸出每一行

到這里,我們對登錄模塊+爬取模塊+存儲模塊就可把數據存到本地了,具體整合的代碼為:

import requests
from bs4 import BeautifulSoup
import urllib.parse

import xlwt
import xlrd

# 賬號密碼
def login(username, password):
    url = 'https://accounts.douban.com/j/mobile/login/basic'
    header = {
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
        'Referer': 'https://accounts.douban.com/passport/login_popup?login_source=anony',
        'Origin': 'https://accounts.douban.com',
        'content-Type': 'application/x-www-form-urlencoded',
        'x-requested-with': 'XMLHttpRequest',
        'accept': 'application/json',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'zh-CN,zh;q=0.9',
        'connection': 'keep-alive'
        , 'Host': 'accounts.douban.com'
    }
    # 登陸需要攜帶的參數
    data = {
        'ck' : '',
        'name': '',
        'password': '',
        'remember': 'false',
        'ticket': ''
    }
    data['name'] = username
    data['password'] = password
    data = urllib.parse.urlencode(data)
    print(data)
    req = requests.post(url, headers=header, data=data, verify=False)
    cookies = requests.utils.dict_from_cookiejar(req.cookies)
    print(cookies)
    return cookies

def getcomment(cookies, mvid):  # 參數為登錄成功的cookies(后台可通過cookies識別用戶,電影的id)
    start = 0
    w = xlwt.Workbook(encoding='ascii')  # #創建可寫的workbook對象
    ws = w.add_sheet('sheet1')  # 創建工作表sheet
    index = 1  # 表示行的意思,在xls文件中寫入對應的行數
    while True:
        # 模擬瀏覽器頭發送請求
        header = {
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
        }
        # try catch 嘗試,一旦有錯誤說明執行完成,沒錯誤繼續進行
        try:
            # 拼湊url 每次star加20
            url = 'https://movie.douban.com/subject/' + str(mvid) + '/comments?start=' + str(
                start) + '&limit=20&sort=new_score&status=P&comments_only=1'
            start += 20
            # 發送請求
            req = requests.get(url, cookies=cookies, headers=header)
            # 返回的結果是個json字符串 通過req.json()方法獲取數據
            res = req.json()
            res = res['html']  # 需要的數據在`html`鍵下
            soup = BeautifulSoup(res, 'lxml')  # 把這個結構化html創建一個BeautifulSoup對象用來提取信息
            node = soup.select('.comment-item')  # 每組class 均為comment-item  這樣分成20條記錄(每個url有20個評論)
            for va in node:  # 遍歷評論
                name = va.a.get('title')  # 獲取評論者名稱
                star = va.select_one('.comment-info').select('span')[1].get('class')[0][-2]  # 星數好評
                votes = va.select_one('.votes').text  # 投票數
                comment = va.select_one('.short').text  # 評論文本
                print(name, star, votes, comment)
                ws.write(index, 0, index)  # 第index行,第0列寫入 index
                ws.write(index, 1, name)  # 第index行,第1列寫入 評論者
                ws.write(index, 2, star)  # 第index行,第2列寫入 評星
                ws.write(index, 3, votes)  # 第index行,第3列寫入 投票數
                ws.write(index, 4, comment)  # 第index行,第4列寫入 評論內容
                index += 1
        except Exception as e:  # 有異常退出
            print(e)
            break
    w.save('test.xls')  # 保存為test.xls文件


if __name__ == '__main__':
    username = input('輸入賬號:')
    password = input('輸入密碼:')
    cookies = login(username, password)
    mvid = input('電影的id為:')
    getcomment(cookies, mvid)

執行之后成功存儲數據:

image-20201022221256503

可視化分析

我們要對評分進行統計、詞頻統計。還有就是生成詞雲展示。而對應的就是matplotlibWordCloud庫。

實現的邏輯思路:讀取xls的文件,將評論使用分詞處理統計詞頻,統計出現最多的詞語制作成直方圖和詞語。將評星🌟數量做成餅圖展示一下,主要代碼均有注釋,具體的代碼為:

其中代碼為:

import matplotlib.pyplot as plt
import matplotlib
import jieba
import jieba.analyse
import xlwt
import xlrd
from wordcloud import WordCloud
import numpy as np
from collections import Counter
# 設置字體 有的linux字體有問題
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False


# 類似comment 為評論的一些數據 [  ['1','名稱','star星','贊同數','評論內容']  ,['2','名稱','star星','贊同數','評論內容'] ]元組
def anylasescore(comment):
    score = [0, 0, 0, 0, 0, 0]  # 分別對應0 1 2 3 4 5分出現的次數
    count = 0  # 評分總次數
    for va in comment:  # 遍歷每條評論的數據  ['1','名稱','star星','贊同數','評論內容']
        try:
            score[int(va[2])] += 1  # 第3列 為star星 要強制轉換成int格式
            count += 1
        except Exception as e:
            continue
    print(score)
    label = '1分', '2分', '3分', '4分', '5分'
    color = 'blue', 'orange', 'yellow', 'green', 'red'  # 各類別顏色
    size = [0, 0, 0, 0, 0]  # 一個百分比數字 合起來為100
    explode = [0, 0, 0, 0, 0]  # explode :(每一塊)離開中心距離;
    for i in range(1, 5):  # 計算
        size[i] = score[i] * 100 / count
        explode[i] = score[i] / count / 10
    pie = plt.pie(size, colors=color, explode=explode, labels=label, shadow=True, autopct='%1.1f%%')
    for font in pie[1]:
        font.set_size(8)
    for digit in pie[2]:
        digit.set_size(8)
    plt.axis('equal')  # 該行代碼使餅圖長寬相等
    plt.title(u'各個評分占比', fontsize=12)  # 標題
    plt.legend(loc=0, bbox_to_anchor=(0.82, 1))  # 圖例
    # 設置legend的字體大小
    leg = plt.gca().get_legend()
    ltext = leg.get_texts()
    plt.setp(ltext, fontsize=6)
    plt.savefig("score.png")
    # 顯示圖
    plt.show()


def getzhifang(map):  # 直方圖二維,需要x和y兩個坐標
    x = []
    y = []
    for k, v in map.most_common(15):  # 獲取前15個最大數值
        x.append(k)
        y.append(v)
    Xi = np.array(x)  # 轉成numpy的坐標
    Yi = np.array(y)

    width = 0.6
    plt.rcParams['font.sans-serif'] = ['SimHei']  # 用來正常顯示中文標簽
    plt.figure(figsize=(8, 6))  # 指定圖像比例: 8:6
    plt.bar(Xi, Yi, width, color='blue', label='熱門詞頻統計', alpha=0.8, )

    plt.xlabel("詞頻")
    plt.ylabel("次數")
    plt.savefig('zhifang.png')
    plt.show()
    return


def getciyun_most(map):  # 獲取詞雲
    # 一個存對應中文單詞,一個存對應次數
    x = []
    y = []
    for k, v in map.most_common(300):  # 在前300個常用詞語中
        x.append(k)
        y.append(v)
    xi = x[0:150]  # 截取前150個
    xi = ' '.join(xi)  # 以空格 ` `將其分割為固定格式(詞雲需要)
    print(xi)
    # backgroud_Image = plt.imread('')  # 如果需要個性化詞雲
    # 詞雲大小,字體等基本設置
    wc = WordCloud(background_color="white",
                   width=1500, height=1200,
                   # min_font_size=40,
                   # mask=backgroud_Image,
                   font_path="simhei.ttf",
                   max_font_size=150,  # 設置字體最大值
                   random_state=50,  # 設置有多少種隨機生成狀態,即有多少種配色方案
                   )  # 字體這里有個坑,一定要設這個參數。否則會顯示一堆小方框wc.font_path="simhei.ttf"   # 黑體
    # wc.font_path="simhei.ttf"
    my_wordcloud = wc.generate(xi)  #需要放入詞雲的單詞 ,這里前150個單詞
    plt.imshow(my_wordcloud)  # 展示
    my_wordcloud.to_file("img.jpg")  # 保存
    xi = ' '.join(x[150:300])  # 再次獲取后150個單詞再保存一張詞雲
    my_wordcloud = wc.generate(xi)
    my_wordcloud.to_file("img2.jpg")

    plt.axis("off")


def anylaseword(comment):
    # 這個過濾詞,有些詞語沒意義需要過濾掉
    list = ['這個', '一個', '不少', '起來', '沒有', '就是', '不是', '那個', '還是', '劇情', '這樣', '那樣', '這種', '那種', '故事', '人物', '什么']
    print(list)
    commnetstr = ''  # 評論的字符串
    c = Counter()  # python一種數據集合,用來存儲字典
    index = 0
    for va in comment:
        seg_list = jieba.cut(va[4], cut_all=False)  ## jieba分詞
        index += 1
        for x in seg_list:
            if len(x) > 1 and x != '\r\n':  # 不是單個字 並且不是特殊符號
                try:
                    c[x] += 1  # 這個單詞的次數加一
                except:
                    continue
        commnetstr += va[4]
    for (k, v) in c.most_common():  # 過濾掉次數小於5的單詞
        if v < 5 or k in list:
            c.pop(k)
            continue
        # print(k,v)
    print(len(c), c)
    getzhifang(c)  # 用這個數據進行畫直方圖
    getciyun_most(c)  # 詞雲
    # print(commnetstr)


def anylase():
    data = xlrd.open_workbook('test.xls')  # 打開xls文件
    table = data.sheets()[0]  # 打開第i張表
    nrows = table.nrows  # 若干列的一個集合
    comment = []

    for i in range(nrows):
        comment.append(table.row_values(i))  # 將該列數據添加到元組中
    # print(comment)
    anylasescore(comment)
    anylaseword(comment)


if __name__ == '__main__':
    anylase()

我們再來查看一下執行的效果:

這里我選了姜子牙千與千尋 電影的一些數據,兩個電影評分比例對比為:

image-20201023081251237

從評分可以看出明顯千與千尋好評度更高,大部分人願意給他五分。基本算是最好看的動漫之一了,再來看看直方圖的詞譜:

image-20201023081534644

很明顯千與千尋的作者更出名,並且有很大的影響力,以至於大家紛紛提起他。再看看兩者詞雲圖:

宮崎駿、白龍、婆婆,真的是滿滿的回憶,好了不說了,有啥想說的歡迎討論!

如果感覺不錯,點贊、一鍵三連 原創公眾號:bigsai,分享知識和干貨!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM