1. 程式人生 > >Python 抖音視訊和評論爬蟲

Python 抖音視訊和評論爬蟲

引言

平時工作閒的時候,會刷刷抖音。於是想爬取一下抖音的視訊。網上搜索資料,發現多數都是爬取固定使用者的主頁視訊。我想要的效果是爬取首頁的隨機視訊和評論,於是自己抓包分析,實現效果。在做專案的過程中遇到了一些問題,在此記錄下來。

專案地址

抖音爬蟲
如果有幫助的話,記得給個star哦

思路

  1. 爬取首頁隨機視訊的作者、ID、名稱、點贊數、評論數、分享數、背景音樂作者、名稱和無水印視訊下載地址
  2. 爬取對應視訊的前十條評論的作者、內容、點贊數、評論時間。如果有回覆別人的情況,還要爬取被回覆的使用者名稱、內容、點贊數和評論時間
  3. 下載無水印視訊(已經實現,但考慮到儲存空間不足,已註釋,暫不使用)

問題

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.

專案裡還實現了一個附加功能:從抖音的分享連結中下載無水印視訊。這個功能的實現,下篇文章會講到。