抖音上面有許多精彩有趣的視頻扳抽,如果我們想把這些短視頻下載下來(lái)離線觀看的話,該怎么寫爬蟲呢殖侵?現(xiàn)在全國(guó)人民都在抗擊新型肺炎贸呢,可惜我不是醫(yī)生,也幫不上忙拢军,與其整天刷手機(jī)楞陷,惶惶終日,不如寫寫代碼茉唉,學(xué)點(diǎn)兒東西固蛾。
主要思路
在google的幫助下结执,終于找到一個(gè)思路。
利用fiddler和模擬器來(lái)抓取抖音和后臺(tái)之間的https數(shù)據(jù)包艾凯,將其中的視頻的地址提取出來(lái)献幔,再利用python對(duì)這些視頻進(jìn)行下載保存。
思路很簡(jiǎn)單趾诗,但是這個(gè)環(huán)境的搭建并不容易蜡感,浪費(fèi)了不少時(shí)間。
開發(fā)環(huán)境
- win10 x64
- Fiddler 一個(gè)http代理工具沧竟,用于進(jìn)行軟件測(cè)試
- 雷電模擬器
- vscode 用于編輯代碼
- python3.7 (Anaconda 3)
fiddler的安裝和配置
從fiddler的官網(wǎng)(https://www.telerik.com/fiddler)下載最新版本安裝铸敏。
以管理員權(quán)限運(yùn)行
在Tools->Options中進(jìn)行如下配置
點(diǎn)擊界面右上角的Online會(huì)顯示出代理的IP地址。
重啟fiddler.
雷電模擬器的安裝和配置
在雷電模擬器的官網(wǎng)(https://www.ldmnq.com/)上下載安裝悟泵。
打開雷電模擬器杈笔,安裝抖音app
配置網(wǎng)絡(luò)
在設(shè)置->WLAN,找到已經(jīng)連接的wifi糕非,長(zhǎng)按蒙具,點(diǎn)擊修改網(wǎng)絡(luò)
勾選高級(jí)設(shè)置
設(shè)置靜態(tài)的IP地址和代理IP和端口,注意手機(jī)的IP地址要與本機(jī)的地址在同一網(wǎng)段內(nèi)朽肥,代理的地址為前文中fiddler中顯示的地址禁筏,一般為本地的地址。如下圖所示衡招。
設(shè)置網(wǎng)絡(luò)后重啟模擬器篱昔。
安裝https證書
在瀏覽器中打開https://[proxy_ip:proxy_port],打開如下頁(yè)面始腾。
下載并安裝證書州刽,安裝過(guò)程中需要為證書命名并設(shè)置開機(jī)密碼。然后重啟浪箭。
Fiddler抓包
如果上面的環(huán)境搭建過(guò)程沒(méi)有問(wèn)題的話穗椅,在fiddler中就可以看到app的中數(shù)據(jù)包了。
比如在抖音中打開"私人飛機(jī)花生哥"的主頁(yè)奶栖,下面列出了他的作品匹表。在fiddler中可以看到app向后臺(tái)的發(fā)現(xiàn)的https請(qǐng)求包。
aweme_list中含有視頻的播放和下載地址宣鄙。
通過(guò)觀察不難發(fā)現(xiàn)袍镀,隨著向上滑動(dòng),會(huì)有多次這個(gè)請(qǐng)求冻晤,每次都會(huì)返回一些視頻信息苇羡,如果一直滑到底的話,這個(gè)作者的所有作品信息都可以拿到了明也。
下面我們利用這一點(diǎn)宣虾,利用fiddler將視頻這些請(qǐng)求以文本形式保存下來(lái)饮亏。
在菜單Rules->Customize Rules扮碧,打開fidder腳本編輯器。
跳轉(zhuǎn)到OnBeforeRequest函數(shù),該函數(shù)可攔截requests愿棋。
添加如下js代碼隧膘。
//過(guò)濾無(wú)關(guān)請(qǐng)求芬膝,只關(guān)注特定請(qǐng)求
if (oSession.fullUrl.Contains("aweme.snssdk.com/aweme/v1/aweme/post"))
{
var fso;
var file;
fso = new ActiveXObject("Scripting.FileSystemObject");
//文件保存路徑绒尊,可自定義
file = fso.OpenTextFile("D:\\douyin\\requests.txt",8 ,true, true);
file.writeLine("Request url: " + oSession.url);
file.writeLine("Request header:" + "\n" + oSession.oRequest.headers);
file.writeLine("Request body: " + oSession.GetRequestBodyAsString());
file.writeLine("\n");
file.close();
}
定位到OnBeforeResponse函數(shù),該函數(shù)用于攔截response甫菠,添加如下js代碼挠铲。
//過(guò)濾無(wú)關(guān)請(qǐng)求,只關(guān)注特定請(qǐng)求
if (oSession.fullUrl.Contains("aweme.snssdk.com/aweme/v1/aweme/post"))
{
//oSession.utilDecodeResponse();//消除保存的請(qǐng)求可能存在亂碼的情況
var fso;
var file;
fso = new ActiveXObject("Scripting.FileSystemObject");
//文件保存路徑寂诱,可自定義
file = fso.OpenTextFile("D:\\douyin\\response.txt",8 ,true, true);
file.writeLine(oSession.GetResponseBodyAsString());
file.writeLine("+++++++++");
file.close();
}
這樣會(huì)將所有url中含有aweme.snssdk.com/aweme/v1/aweme/post的請(qǐng)求和響應(yīng)都以追加形式保存為文本文件拂苹。其中響應(yīng)以''+++++++++''進(jìn)行分割。
下面測(cè)試一下痰洒,刪除requests.txt和response.txt文件瓢棒,在模擬器將這個(gè)作者主頁(yè)滑動(dòng)到最下面,fiddler會(huì)將所有的請(qǐng)求都保存在requests.txt中丘喻,將所有的響應(yīng)都保存在response.txt文件中脯宿。
response為一個(gè)json格式,其內(nèi)容為
{
...
"aweme_list": [
{
"aweme_id": "6754926284817698060",
"desc": "什么是熱障泉粉? @全球飛機(jī)榜 @抖音小助手 #干貨都在這",
...
"video": {
...
"download_addr": {
"uri": "v0200ff80000bmv538fff778hm097jog",
"url_list": [
"https://aweme.snssdk.com/aweme/v1/play/?video_id=v0200ff80000bmv538fff778hm097jog\u0026line=0\u0026ratio=540p\u0026watermark=1\u0026media_type=4\u0026vr_type=0\u0026improve_bitrate=0\u0026logo_name=aweme\u0026quality_type=11\u0026source=PackSourceEnum_PUBLISH",
"https://api.amemv.com/aweme/v1/play/?video_id=v0200ff80000bmv538fff778hm097jog\u0026line=1\u0026ratio=540p\u0026watermark=1\u0026media_type=4\u0026vr_type=0\u0026improve_bitrate=0\u0026logo_name=aweme\u0026quality_type=11\u0026source=PackSourceEnum_PUBLISH"
],
}
...
}
}
...
}
不難發(fā)現(xiàn)其中含有短視頻的描述信息和下載地址连霉。
下載視頻
這里使用python從response.txt文件中提取出每個(gè)response,將其中的視頻地址提取出來(lái)嗡靡,結(jié)合利用requests.txt中的頭信息跺撼,將這些視頻抓取到本地。
代碼如下叽躯。
#-*- coding:utf-8 -*-
# written by wlj @22:46 2020/1/28
import requests
from requests.packages import urllib3
import json
urllib3.disable_warnings()
def download_video(url,title):
#這里requests使用的headers信息财边,其中含有用戶鑒權(quán)信息和cookie
#這是從requests.txt文件中拷貝的
t = """
Accept-Encoding: gzip
X-SS-REQ-TICKET: 1580217939813
sdk-version: 1
Cookie: odin_tt=42827ad8b1bbc13c0fe69f622c36c075f3d53deb6647e704c8638b16dd45cb822cd9f30f0427ca56160ca283338839d5e18de02ace41a64011d118065abb7c87; install_id=99098965031; ttreq=1$1aac280ead74ddb2d334de2b131dbca9e5eb1df4
X-Gorgon: 03006cc04001df5db027cc24af666ff154cd415902cc8cae7ff2
X-Khronos: 1580217939
Host: aweme.snssdk.com
Connection: Keep-Alive
User-Agent: okhttp/3.10.0.1
"""
#構(gòu)造headers字典
headers = {}
for line in t.strip().split('\n'):
k,v = line.strip().split(': ')
headers[k] = v
#過(guò)濾掉title中非法字符
illege_chars = ['<','>','/','\\','|',':','"','*','?']
for char in illege_chars:
title = title.replace(char,'_')
#下載視頻文件肌括,保存為mp4
res1 = requests.get(url = url, headers=headers,stream=True,verify=False)
with open('%s.mp4' % title, "wb") as f:
for chunk in res1.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
def main():
#從reponse.txt文件中分離中各個(gè)reponse
#注意fiddler保存的文件不是utf-8編碼点骑,需要將其轉(zhuǎn)化為utf-8編碼,才能正確讀取谍夭,推薦使用Notepad++
data = open('response.txt','r',encoding='utf-8').read().split("+++++++++")[:-1]
for x in data:
x = json.loads(x)
#從中提取視頻信息黑滴,包括視頻的描述信息和下載地址
video_list = [(item['video']['download_addr']['url_list'][0],item['desc']) for item in x['aweme_list']]
for item in video_list:
#調(diào)用download_video下載視頻
download_video(item[0],item[1])
main()