1. 程式人生 > >爬蟲自動抓取騰訊視訊評論 -- json的使用和資料解析

爬蟲自動抓取騰訊視訊評論 -- json的使用和資料解析

  這周和大家分享下騰訊視訊評論抓取爬蟲,實際抓下來的資料裡面除了評論還有其他不少有價值的資訊,有部分使用者資料可以使用的,不過具體就看大家自己怎麼用了。
  這個demo的具體原始碼在最後面,下文將對這個demo的實現過程進行說明。
  其實我挺期待有人評論下我的文章寫得怎麼樣的,會不會囉嗦或者沒啥價值的,這樣我也可以改進,所以看著不爽,評論下,我改進,哈哈哈。

頁面和請求分析

  我們要抓的評論主要是深度解讀,當然下面的精彩短評和最新短評原理一致。首先ctrl + u檢視原始碼,檢索相關評論資訊,沒抓到合意的內容,因此,結合頁面是動態變化的,因此初判資料是通過js載入的。
這裡寫圖片描述
  F12開啟DevTools,開啟過濾js,點選檢視更多,有如下request。
這裡寫圖片描述


  複製頁面請求網址,訪問得如下資料情況,抓如下title的值,通過python print出來,可以知道這個就是我們要的評論內容了
這裡寫圖片描述
這裡寫圖片描述
  那剛才說的精彩短評和最新評論呢,我們可以看出這個在重新整理頁面後就固定了,那我們應該在重新整理時就抓這個資料,清空devtool中的資料,F5重新整理頁面,抓到如下資料,也就是我們想要的一波。
這裡寫圖片描述
  接下來我們是分析請求連結的規律,我們不斷點檢視更多,直到沒有更多評論,然後來回點這幾個js檢視其header請求規律,發現如下兩處在變化,其他的都沒變。
這裡寫圖片描述

 不變部分分析,upcomment後面的序列,我們根據這個視訊的網址https://v.qq.com/x/cover/yoz60y87rdgl1vp/h002446mgco.html

, 可知這個是cover後面的那個序列,這個應該是視訊的id,既然id在網址中,那非常有利於抓整個網站的各個片源評論,具體見下圖。而reqnum則是抓取評論的個數,後面這部分直接去掉也能抓到正確的資料,實際去掉更方便處理資料。
這裡寫圖片描述
  然後關於commentid,這個明顯就是指各個commentid,分析後也可以發現實際上就是上一個連結的最後一個comment的id,那就實現迴圈賦值了,整合下來,連結模型就是:

這裡寫圖片描述

  但我們還有解決一個問題,起始條件和終止條件。一開始我們是不知道commentid的,那抓下重新整理頁面的第一個comment請求,發現是沒有commentid的,故應該是有預設值,既然如此,我們直接

https://video.coral.qq.com/filmreviewr/c/upcomment/yoz60y87rdgl1vp?commentid=&reqnum=3 也就是commentid置空,抓到起始資料,u所以rl template可以採用。
這裡寫圖片描述
  終止條件部分,我們抓最後一個請求,如下為請求的全部內容,我們可以看到,last id還是有的,hasnext為true,這些都不能作為終止條件,但commentid這一節內容為空了(實際內容裡面的retnum就是說明此次返回的comment數量,直接可以用),也就是沒有內容了,那肯定是可以做為終止條件的。這個請求很重要,因為給了我們一個巨集觀的認識,也是我們分析評論資料的原型。
這裡寫圖片描述

資料分析

  上面說的最後一個請求是分析的原型,抓出來情況是下面這個,是一個json,下面標紅部分就是我們此次比較關注的內容。reqnum是本次請求的評論個數,retnum是實際返回的個數,last上面有提及是作為組下個訪問請求的關鍵,而commentid裡面的內容則是我們想要的評論相關資訊了。
這裡寫圖片描述

  分析完整體架構,我們具體分析下commentid的內容,下面我抓取一個評論的資料,其他評論是一樣的。

{
    'targetid': '2013224236',        
    'id': '6294901728253477340',        #comment id
    'rootid': '0',
    'parent': '0',
    'userid': '69443701',               #user id
    'up': '2434',                       #點有用的個數
    'poke': 351,                        #點沒用的個數
    'rep': '352',        
    'orireplynum': '444',               #回覆個數
    'source': 0,
    'checktype': '1',
    'checkstatus': '1',
    'hotscale': '15',
    'isdeleted': '0',
    'address': '',
    'rank': '-1',
    'time': 1500821525,
    'title': '人點燭,鬼吹燈,胡八一要為革命事業添磚加瓦',    #評論標題
    'abstract': '我已經做......',           #簡介
    'content': '<p><span style="text-indent: 2em;">我已經做好了準備,做好了這篇影評發表之後,我會被罵個狗血淋頭,.....。</p>',                                     #評論具體內容
    'type': '2',
    'video': [],
    'picture': [],
    'richtype': 2304,
    'custom': '',
    'thirdid': '',
    'timeDifference': '07月23日 22:52:05',
    'replyuser': '',                        #這篇評論回覆誰的
    'replyuserid': '0',                     #被回覆人id
    'replyhwvip': 0,
    'replyhwlevel': 0,
    'replyhwannual': 0,
    'userinfo':                             #品論人的詳細資訊
    {
        'userid': 69443701,
        'nick': 'abc',
        'head': 'http://q3.qlogo.cn/g?b=qq&k=',
        'gender': 0,
        'uidex': 'ecec****************33c8403',
        'region': '::',
        'hwvip': '1',
        'hwlevel': '5',
        'hwannual': 1,
        'wbuserinfo': [],
        'identity': '',
        'viptype': 0,
        'thirdlogin': 0,
        'specialidentity': 1
    },
    'score': None,
    'uped': 0,
    'poked': 0
},

具體程式碼實現

我這個程式碼只實現抓單個指定視訊的資料,如果要自動抓多個的話,稍加調整即可。

#!/usr/local/bin/python3
#-*- coding: utf-8 -*-

import urllib.request
import http.cookiejar
import gzip
import io
import json

#For templates and configrations
url_temp  = 'https://video.coral.qq.com/filmreviewr/c/upcomment/%s?commentid=%s&reqnum=%d'

#For simulate the behaviour of web browser
host    = 'video.coral.qq.com'
refer  = 'https://v.qq.com/txyp/coralComment_yp_1.0.htm'
Headers = {
        'Host':            host,
        'Connection':      'keep-alive',
        'Cache-Control':    'max-age=0',
        'User-Agent':      'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.78 Safari/537.36',
        'Accept':          'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
        'Referer':          refer,
        'Accept-Encoding':  'gzip, deflate',
        'Accept-Language':  'zh-CN,zh;q=0.8,en;q=0.6',
        'Upgrade-Insecure-Requests':  '1',
        }

#cookie處理
def BuildAndInstall_Opener():

    cjar    = http.cookiejar.CookieJar()
    cprocs  = urllib.request.HTTPCookieProcessor(cjar)
    opener  = urllib.request.build_opener(cprocs)

    urllib.request.install_opener(opener)

#GZip解碼
def Decode_GzipString(data):

    buf = io.BytesIO(data)
    gf  = gzip.GzipFile(fileobj = buf)
    data= gf.read()
    return data

def Get_Data(url):

    req = urllib.request.Request(url, None, Headers)
    content = urllib.request.urlopen(req)

    '''get the content'''
    data = content.read()

    '''data process: Get full data'''
    encoding = content.getheader('Content-Encoding')
    if ( encoding == 'gzip' ):
        data = Decode_GzipString(data)

    if len(data) == 0:
        print(data)
        print('[DBG ERR ] Get Data Error ')
    else:
        #如果抓的資料不是很多,也可以直接採用正則。
        data = json.loads(data.decode('utf-8'))

    return data['data']

def Analyse_Comments(data):
    if len(data['commentid']) == 0 :
        print('='*50,'end of comment', '='*50)
        return '0'
    else:
        for comment in data['commentid']:
            if comment['isdeleted'] == '0':
                print('user:', comment['userinfo']['nick'], end = '')
                if comment['replyuser'] != '' :
                    print(' reply to ',comment['replyuser'], end = '')
                #輸出格式化,實際內容保留照片的html元素,方便後續處理。
                commstr = comment['content'].replace(u'<p>',u'\r\n\t')
                commstr = re.sub('<[a-zA-Z/]*?>', '',commstr)
                print('\r\ncomment:', commstr) 
            else:
                print(comment['title'], ' is been deleted.')
        return data['last']

if __name__ == '__main__':

    BuildAndInstall_Opener()
    LastCommentId = ''
    VideoId = input('Please input the videoid which you want to get its comments: \r\n')
    while True:
        url = url_temp % (VideoId, LastCommentId, 10)        
        data = Get_Data(url)
        if len(data) == 0:
            break
        LastCommentId = Analyse_Comments(data) 
        if LastCommentId == '0':
            break