爬蟲自動抓取騰訊視訊評論 -- 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
然後關於commentid,這個明顯就是指各個commentid,分析後也可以發現實際上就是上一個連結的最後一個comment的id,那就實現迴圈賦值了,整合下來,連結模型就是:
但我們還有解決一個問題,起始條件和終止條件。一開始我們是不知道commentid的,那抓下重新整理頁面的第一個comment請求,發現是沒有commentid的,故應該是有預設值,既然如此,我們直接
終止條件部分,我們抓最後一個請求,如下為請求的全部內容,我們可以看到,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