Python 抖音視訊和評論爬蟲
引言
平時工作閒的時候,會刷刷抖音。於是想爬取一下抖音的視訊。網上搜索資料,發現多數都是爬取固定使用者的主頁視訊。我想要的效果是爬取首頁的隨機視訊和評論,於是自己抓包分析,實現效果。在做專案的過程中遇到了一些問題,在此記錄下來。
專案地址
抖音爬蟲
如果有幫助的話,記得給個star哦
思路
- 爬取首頁隨機視訊的作者、ID、名稱、點贊數、評論數、分享數、背景音樂作者、名稱和無水印視訊下載地址
- 爬取對應視訊的前十條評論的作者、內容、點贊數、評論時間。如果有回覆別人的情況,還要爬取被回覆的使用者名稱、內容、點贊數和評論時間
- 下載無水印視訊(已經實現,但考慮到儲存空間不足,已註釋,暫不使用)
問題
1.Fiddler抓包,手機上設定代理後,無法上網
按照網上教程,用Fiddler抓取iphone手機包,發現不設定代理,可以正常上網,設定了代理之後,就上不了網了。https的請求都會失敗,提示錯誤資訊為Failure SSLHandshake: Received fatal alert: unknown_ca 和You may need to configure your browser or application to trust the Charles Root Certificate. 但是相關設定都正確:電腦上安裝了Fiddler 的根證書,並且設定了始終信任,然後手機上也安裝了描述檔案,一切都按正常程式走的, 但是錯誤始終無法解決。
原因:
雖然Fiddler的根證書已經在安裝列表中顯示,但它是被關閉的。在iOS 10.3之前,當你將安裝一個自定義證書,iOS會預設信任,不需要進一步的設定。而iOS 10.3之後,安裝新的自定義證書預設是不受信任的。如果要信任已安裝的自定義證書,需要手動開啟開關以信任證書。
解決:
設定->通用->關於本機->證書信任設定-> 找到DO_NOT_TRUST_FiddlerRoot然後信任該證書即可。
2.介面url引數加密
抓包分析,找到了首頁視訊流介面url:https://aweme-eagle.snssdk.com/aweme/v1/feed/
url引數如下(樣例):
Name | Value | Description |
---|---|---|
iid | 51050168070 | 裝置資訊 |
idfa | 887748FC-0DA1-4984-B87F-F2FC9AC5D14B | 裝置資訊 |
device_type | iPhone5,2 | 裝置資訊 |
os_version | 10.3.3 | 裝置資訊 |
screen_width | 640 | 裝置資訊 |
vid | AECABC99-0F66-4086-86BC-EC4E01B4DEA1 | 裝置資訊 |
device_id | 59415024289 | 裝置資訊 |
os_api | 18 | 裝置資訊 |
device_platform | iphone | 裝置資訊 |
openudid | 75a4bc255848cd7901e166e5c168b23e3e9394a8 | 裝置資訊 |
version_code | 3.1.0 | app資訊 |
aid | 1128 | app資訊 |
app_name | aweme | app資訊 |
build_number | 31006 | app資訊 |
app_version | 3.1.0 | app資訊 |
channel | App Store | app資訊 |
pass-region | 1 | 常量 |
js_sdk_version | 1.3.0.1 | 常量 |
ac | mobile | 常量 |
count | 6 | 視訊數 |
feed_style | 0 | 常量 |
filter_warn | 0 | 常量 |
max_cursor | 0 | 常量 |
min_cursor | 0 | 常量 |
pull_type | 0 | 常量 |
type | 0 | 常量 |
volume | 0.06 | 常量 |
mas | 0161b6c4a20babcf6829e30950a9f3a577adb04abc0c6da0eeca91 | 加密引數 |
as | a105e18ff4e32b1a102320 | 加密引數 |
ts | 1542462004 | 加密引數 |
其中mas、as、ts為加密引數,其餘的裝置和app一旦確定下來,就都是常量了。三個加密引數需要一步一步分析JS檔案才能得出這三個加密引數如何生成的。
解決:
有萬能的GitHub,在此感謝AppSign提供加密簽名服務,參照該文件,很快可以得到三個加密引數,和其他引數一起給介面就可以拿到返回的視訊流資料了。
同樣,評論介面加密引數也是一樣的,相同的辦法就可以拿到評論資料了。
評論url:https://aweme.snssdk.com/aweme/v2/comment/list/
url引數如下(樣例):
Name | Value | Description |
---|---|---|
iid | 51050168070 | 裝置資訊 |
idfa | 887748FC-0DA1-4984-B87F-F2FC9AC5D14B | 裝置資訊 |
device_type | iPhone5,2 | 裝置資訊 |
os_version | 10.3.3 | 裝置資訊 |
screen_width | 640 | 裝置資訊 |
vid | AECABC99-0F66-4086-86BC-EC4E01B4DEA1 | 裝置資訊 |
device_id | 59415024289 | 裝置資訊 |
os_api | 18 | 裝置資訊 |
device_platform | iphone | 裝置資訊 |
openudid | 75a4bc255848cd7901e166e5c168b23e3e9394a8 | 裝置資訊 |
version_code | 3.1.0 | app資訊 |
aid | 1128 | app資訊 |
app_name | aweme | app資訊 |
build_number | 31006 | app資訊 |
app_version | 3.1.0 | app資訊 |
channel | App Store | app資訊 |
pass-region | 1 | 常量 |
js_sdk_version | 1.3.0.1 | 常量 |
ac | mobile | 常量 |
aweme_id | 6624665048084122888 | 視訊ID |
count | 10 | 評論數 |
cursor | 0 | 常量 |
insert_ids | 0 | 常量 |
mas | 0161b6c4a20babcf6829e30950a9f3a577adb04abc0c6da0eeca91 | 加密引數 |
as | a105e18ff4e32b1a102320 | 加密引數 |
ts | 1542462004 | 加密引數 |
3.資料庫關閉問題
原本打算程式放到伺服器上一直跑的,想了一下由於沒有使用代理池,長時間跑可能會被封IP,而且一直跑對AppSign提供加簽的伺服器也有一定的壓力,最後決定每天爬取1萬條視訊資料。那麼這就涉及到資料庫關閉的問題。
因為我採用的是爬取和儲存是分開的執行緒,而且考慮到減少加簽伺服器的壓力,爬取執行緒速度較慢,儲存執行緒
速度快,並且設定兩個執行緒的daemon=True(會隨著主執行緒的結束而結束)。這時候就不能通過queue.join()來控制主執行緒是否阻塞。因為queue的get速度比put速度要快,這樣主程式執行一段時間就會因為queue中待處理的資料為0,從而使queue.join()放通,導致主程式結束,子執行緒也會隨之結束,爬取視訊達不到1萬條。
解決:
在爬取執行緒的1萬條資料完成之後手動給queue傳送一個結束標誌,儲存執行緒收到結束標誌後不做處理,將其傳送給主執行緒,主執行緒裡迴圈檢查queue中資料是否為結束標誌,如果是,則關閉資料庫連線,跳出迴圈,結束主程式,子執行緒也同時結束,程式終止。程式碼如下:
def put_into_queue(params, queue): # 獲取介面返回的視訊和評論資料,放進佇列
i = 0
while i < 10000: # 每天抓取10000個左右視訊,因為get_video_info()一次返回6個視訊資料,最後爬取的視訊數不是1萬整
video_params = get_video_params(params)
for video_data in get_video_info(video_params):
if video_data['result'] == 'success':
i += 1
print('today video num:', i)
video_data['type'] = 'video'
queue.put_nowait(video_data)
comment_params = get_comment_params(params, video_data['video_id'])
for comment_data in get_comment_info(comment_params):
if comment_data['result'] == 'success':
comment_data['type'] = 'comment'
queue.put_nowait(comment_data)
elif comment_data['result'] == 'error':
continue
# queue.put_nowait(comment_data)
# break
elif video_data['result'] == 'error':
continue
# queue.put_nowait(video_data)
# break
time.sleep(10) # 加密簽名為github開源服務,作者要求禁止高併發請求訪問公用伺服器,所以降低請求頻率
data = {}
data = {'result': 'success', 'type': 'finished'} # 抓取完成標誌
queue.put_nowait(data)
def get_from_queue(queue, db): # 獲取佇列裡的視訊和評論資料,儲存到資料庫和下載視訊
while True:
try:
data = queue.get_nowait()
if data['result'] == 'success':
if data['type'] == 'video':
# download(data['filename'], data['download_url']) # 1w個視訊大約需要20G,因儲存空間不足,暫不下載
db.save_one_data_to_video(data)
elif data['type'] == 'comment':
db.save_one_data_to_comment(data)
elif data['type'] == 'finished': # 抓取完成後子執行緒退出迴圈
queue.put_nowait(data) # 告訴主執行緒抓取完成
break
# elif data['result'] == 'error':
# queue.put_nowait(data)
# break
except:
print("queue is empty wait for a while")
time.sleep(2)
if __name__ == '__main__':
......
......
......
queue = Queue()
Thread(target=put_into_queue, args=(params, queue), daemon=True).start()
Thread(target=get_from_queue, args=(queue, db), daemon=True).start()
while True: # 該迴圈是用來判斷何時關閉資料庫
try:
data = queue.get_nowait()
# if data['result'] == 'error':
# db.close()
# break
if data['type'] == 'finished':
db.close()
break
except:
print('spidering...')
time.sleep(10)
總結
本專案大部分的時間花費在解決問題1和2上。最開始抓不到包,怎麼都解決不了,查了好長時間才發現是這個問題。三個加密引數也卡了很長時間,最後發現github上有提供加簽服務的。以後遇到些複雜的問題,先去github上看看有沒有現成的東西。
P.S.
專案裡還實現了一個附加功能:從抖音的分享連結中下載無水印視訊。這個功能的實現,下篇文章會講到。