python爬B站評論(一通百通)


1.前言

由於前段時間在B站看到我關注的一個程序員UP主爬取了自己所有視頻下的所有評論並錄入到數據庫里,進行了一波分析。

我就覺得挺有意思的,而且那時候我還不太會爬蟲。正巧,趕上這機會,學習學習爬蟲。

參考資源:https://www.cnblogs.com/awesometang/p/11991755.html

2.分析

樣例視頻:https://www.bilibili.com/video/BV1V44y1T7mY?spm_id_from=444.41.0.0

首先要先看看B站的評論是用哪種方式顯示出來的。

用F12是正常能看到網頁中的各個元素的,但是打開網頁源代碼卻沒有任何有關評論的信息。所以猜測大概率是通過Ajax動態加載的。

那就需要去抓包得到評論數據了。

image-20220318151159792

image-20220318151046647

3.抓包

我嘗試過直接在網頁中使用F12來查看請求,確實可以正常找到視頻的 評論 內容,但是無法抓到完整的 評論的評論 內容。

image-20220318152657826

image-20220318152944010

所以,F12的功能似乎並不夠滿足我們的需求,需要用一個專業的抓包工具——Fiddler(自行下載)

image-20220318153245631

這里不做詳細使用教程

3.1 抓評論的包

  1. 先打開Fiddler

  2. 清空已攔截到的所有請求,然后掛在旁邊

    image-20220318153657102

  3. 刷新B站視頻頁面,把頁面拉到 評論區 位置,然后就別動了(為了保證獲取評論的請求能被Fiddler攔截到)

  4. 這時候再看Fiddler的頁面,能看到已經攔截到了非常多的請求。

    image-20220318154015007

  5. 經過我們在F12里查看到的,我們可以知道評論的數據是 Json數據 。所以我們在Fiddler攔截到的請求里找Json圖標的就行。

    image-20220318154222570

  6. 隨便雙擊一個請求,將右側的視圖欄選擇到 Json

    image-20220318154413253

  7. 開始找評論。只看Json圖標的請求。直到看到以下頁面(可能是壓縮狀態,需要自己展開看)

    image-20220318154608069

這里我就明說了,評論的請求是帶 main 的,所以可以直接在Fiddler中按 CTRL + F 搜索 main

找到一個 main?callback= 。這個就是評論的請求

image-20220318154832881

然后我們復制這個請求的 url 並在瀏覽器中對其進行訪問。

image-20220318160523018

image-20220318160651171

出現了一個問題,我們需要的那個請求的url無法訪問,后來嘗試才知道,需要刪除這一小段(我也不知道為什么)

image-20220318161416014

再次嘗試訪問:

image-20220318161535158

那么現在,我們就已經能夠爬取到一段的評論了。可是。。。

image-20220318161825185

搞了這么久,只能拿到20個評論???

因為B站的評論不是一次性全部獲取到的,你拉到底,它才會幫你請求后一段的評論數據。整這樣👿

image-20220318162215713

沒關系,和前面的步驟一樣。

清空已攔截的請求-->拉到底-->尋找評論請求

那每一段的評論都不一樣,那么每一個請求指定是有某些參數是不一樣的。我們可以多抓幾個,找到規律。

image-20220318162743406

這里面可以看出來,只有 next 的值是不一樣的。

但為什么是 0、2、3 呢,不夠規律啊。那我們在看看 next=1 時是什么數據。

image-20220318163159891

發現 next=1next=0 的數據是一樣的。那 0 可能是默認請求的,實際請求的就是 1 。

這樣看的話,規律就找到了。

不斷遞增 next 的值,就可以把所有的評論都拿到手。

image-20220318163400868

3.2 抓評論的評論的包

由於在網頁中使用F12是拿不到 評論的評論 的包,所以我們使用 Fiddler 再試一次。

原理和前面一樣。

清空已攔截的請求-->點擊“點擊查看”-->尋找評論的評論請求

image-20220318163750231

這時再看 Fiddler ,已經攔截到一些請求了

其中有一個和前面很相似的一個 url !!! reply?callback=main?callback=

那就一定是它了。

image-20220318164025500

再用剛才的分析法,找到每一頁 評論的評論 的請求規律。

image-20220318164358780

這里面可以看出 pn 應該就是我們要找的那個規律。

可是,最后面那串數字怎么也不一樣。

我們嘗試刪除那一段奇怪的數字

image-20220318164604582

居然也能訪問,而且數據都正常,那么后面那一小串奇怪的文字咱就不要了。(加上也無妨,不影響

3.3 評論和評論的評論是怎么關聯的

學過數據庫肯定會知道,要渲染某條評論和這條評論的評論,那么肯定有通過某個東西將他們關聯到一起。

這個東西肯定是在保存評論到數據庫中時用某個字段聯系到一起的。

我們需要找到這個聯系,才能知道每個評論和它的子評論。

不弄清關聯的話,就無法弄清楚每條評論和它的子評論了。也就無法爬取所有的 評論的評論 了。

我們就找 第一個評論和它的子評論 來研究。

image-20220318170048112

現在,我們已經知道了所有的請求方式和他們之間的聯系了。開爬!

4.爬評論

代碼寫的有點糙,可以自行優化。

# 發送請求
import requests
# 將數據存入數據庫
import MySQLdb
# 每次請求停1s,太快會被B站攔截。
import time

# 連接數據庫
conn = MySQLdb.connect(host="localhost", user='root', password='admin', db='scholldatabase', charset='utf8')
cursor = conn.cursor()
# 預編譯語句
sql = "insert into bilibili(rpid,root,name,avatar,content) values (%s,%s,%s,%s,%s)"


# 爬蟲類(面向對象)
class JsonProcess:
    def __init__(self):
        self.Json_data = ''
        # 請求頭
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36 Edg/99.0.1150.39'
        }
	
    # 發送爬取請求
    def spider(self, URL):
        url = URL
        response = requests.get(url, headers=self.headers, verify=False)
        response.encoding = 'utf-8'
        self.Json_data = response.json()['data']['replies']


# 爬取子評論
def getSecondReplies(root):
    reply = JsonProcess()
    # 頁數
    pn = 1
    # 不知道具體有多少頁的評論,所以使用死循環一直爬
    while True:
        url = f'https://api.bilibili.com/x/v2/reply/reply?jsonp=jsonp&pn={pn}&type=1&oid=979849123&ps=10&root={root}&_=1647581648753'
        # 沒爬一次就睡1秒
        time.sleep(1)
        reply.spider(url)
        # 如果當前頁為空(爬到頭了),跳出子評論
        if reply.Json_data is None:
            break
        # 組裝數據,存入數據庫
        for node in reply.Json_data:
            rpid = node['rpid']
            name = node['member']['uname']
            avatar = node['member']['avatar']
            content = node['content']['message']
            data = (rpid, root, name, avatar, content)
            try:
                cursor.execute(sql, data)
                conn.commit()
            except:
                pass
            print(rpid, ' ', name, ' ', content, ' ', avatar, ' ', root)
        # 每爬完一次,頁數加1
        pn += 1

        
# 爬取根評論
def getReplies(jp, i):
    # 不知道具體有多少頁的評論,所以使用死循環一直爬
    while True:
        url = f'https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&next={i}&type=1&oid=979849123&mode=3&plat=1&_=1647577851745'
        jp.spider(url)
        # 如果當前頁為空(爬到頭了),跳出循環,程序結束。
        if jp.Json_data is None:
            break
        # 組裝數據,存入數據庫。
        for node in jp.Json_data:
            print('===================')
            rpid = node['rpid']
            name = node['member']['uname']
            avatar = node['member']['avatar']
            content = node['content']['message']
            data = (rpid, '0', name, avatar, content)
            try:
                cursor.execute(sql, data)
                conn.commit()
            except:
                pass
            print(rpid, ' ', name, ' ', content, ' ', avatar)
            # 如果有子評論,爬取子評論
            if node['replies'] is not None:
                print('>>>>>>>>>')
                getSecondReplies(rpid)
        # 每爬完一頁,頁數加1
        i += 1


if __name__ == '__main__':
    JP = JsonProcess()
    getReplies(JP, 1)
    print('\n================存儲完成================\n')
    conn.close()


免責聲明!

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



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