知乎有很多好玩的微信8.0狀態(tài)視頻,用Python一鍵下載
于是我就想把這些視頻下載下來,也玩一玩。本文講述如何使用 Python 一鍵下載知乎某個回答下的所有視頻。
思路:分析知乎回答頁面 -> 定位視頻 -> 尋找視頻播放的 url -> 下載。其實就兩步:找到 url,然后下載。
尋找 url
一個回答下面可能有多個視頻,先分析一個視頻,打開谷歌瀏覽器的開發(fā)者工具窗口,找到 network,勾選 preserve log、disable cache,選擇 xhr,刷新,很容易找到如下圖所示的接口:

從上圖接口返回的數(shù)據(jù)就可以獲取視頻播放的 url、標(biāo)題、格式等信息,這就夠了,復(fù)制 play_url,放在瀏覽器上看一下,發(fā)現(xiàn)可以直接下載,說明那么這個 url 就是我們需要的。

接下來,寫代碼,獲取接口返回的數(shù)據(jù):
def?get(url:?str)?->?list:
????"""
????獲取知乎視頻的?url
????返回格式
????[{'url':'',?'title','format':'',},{}]
????"""
????data?=?[]
????headers?=?{
????????"user-agent":?"Mozilla/5.0?(Macintosh;?Intel?Mac?OS?X?11_1_0)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/87.0.4280.88?Safari/537.36",
????}
????with?requests.get(url,?headers=headers,?timeout=10)?as?rep:
????????if?rep.status_code?==?200:
????????????ids?=?re.findall(r"www.zhihu.com/zvideo/(\d{1,})",?rep.text)
????????????ids?=?list(set(ids))??#?去掉重復(fù)元素
????????else:
????????????print(f"網(wǎng)絡(luò)連接失敗,狀態(tài)碼?{?rep.status_code?}")
????????????return?[]
????if?not?ids:
????????print("視頻獲取失敗,可能是這個頁面沒有視頻")
????????return?[]
????for?id?in?ids:
????????print(id)
????????with?requests.get(
????????????f"https://www.zhihu.com/api/v4/zvideos/{id}/card",
????????????headers=headers,
????????????timeout=10,
????????)?as?rep:
????????????if?rep.status_code?==?200:
????????????????ret_data?=?rep.json()
????????????????playlist?=?ret_data["video"]["playlist"]
????????????????title?=?ret_data.get("title")
????????????????temp?=?playlist.get("ld")?or?playlist.get("sd")
????????????????if?temp:
????????????????????sigle_video?=?{}
????????????????????sigle_video["url"]?=?temp.get("play_url")
????????????????????sigle_video["title"]?=?title
????????????????????sigle_video["format"]?=?temp.get("format")
????????????????????data.append(sigle_video)
????????????else:
????????????????print(f"網(wǎng)絡(luò)連接失敗,狀態(tài)碼?{?rep.status_code?}")
????????????????return?[]
????return?data
下載視頻
這個比較簡單了,直接請求視頻播放的 url,將流式的內(nèi)容保存到文件中,最多再加個進度條的展示。部分視頻獲取的 title 為空,這時就使用時間戳來命名文件。
請看代碼:
def?download(?file_url,?file_name=None,?file_type=None,?save_path="download",?headers=None,?timeout=15,):?
????"""
????:param?file_url:?下載資源鏈接
????:param?file_name:?保存文件名,默認為當(dāng)前日期時間
????:param?file_type:?文件類型(擴展名)
????:param?save_path:?保存路徑,默認為download,后面不要"/"
????:param?headers:?http請求頭
????"""
????if?file_name?is?None?or?file_name?==?"":
????????file_name?=?str(datetime.now())
????if?file_type?is?None:
????????if?"."?in?file_url:
????????????file_type?=?file_url.split(".")[-1]
????????else:
????????????file_type?=?"uknown"
????file_name?=?file_name?+?"."?+?file_type
????if?headers?is?None:
????????headers?=?{
????????????"User-Agent":?"Mozilla/5.0?(iPhone;?CPU?iPhone?OS?9_1?like?Mac?OS?X)?AppleWebKit/601.1.46?(KHTML,?like?Gecko)?Version/9.0?Mobile/13B137?Safari/601.1"
????????}
????if?os.path.exists(save_path):
????????pass
????else:
????????os.mkdir(save_path)
????#?下載提示
????if?os.path.exists(f"{save_path}/{file_name}"):
????????print(f"\033[33m{file_name}已存在,不再下載!\033[0m")
????????return?True
????print(f"Downloading?{file_name}")
????try:
????????with?requests.get(
????????????file_url,?headers=headers,?stream=True,?timeout=timeout
????????)?as?rep:
????????????file_size?=?int(rep.headers["Content-Length"])
????????????if?rep.status_code?!=?200:
????????????????print("\033[31m下載失敗\033[0m")
????????????????return?False
????????????label?=?"{:.2f}MB".format(file_size?/?(1024?*?1024))
????????????with?click.progressbar(length=file_size,?label=label)?as?progressbar:
????????????????with?open(f"{save_path}/{file_name}",?"wb")?as?f:
????????????????????for?chunk?in?rep.iter_content(chunk_size=1024):
????????????????????????if?chunk:
????????????????????????????f.write(chunk)
????????????????????????????progressbar.update(1024)
????????????print(f"\033[32m{file_name}下載成功\033[0m")
????except?Exception?as?e:
????????print("下載失敗:?",?e)
????return?True
執(zhí)行代碼下載:
import?os,?sys
import?re
import?click
import?requests
from?datetime?import?datetime
def?get(url:?str)?->?list:
????#見上文
????...
def?download(?file_url,?file_name=None,?file_type=None,?save_path="download",?headers=None,?timeout=15,):?
????#見上文
????...
if?__name__?==?"__main__":
????videos?=?get(sys.argv[1])
????for?video?in?videos:
????????download(file_url?=?video['url'],file_name=?video['title']?,file_type=?video['format'],save_path='./download')
執(zhí)行結(jié)果如下圖所示:
最后的話
網(wǎng)站可能會發(fā)生變更,因此本文的代碼可能隨著時間變化而無法使用,請自行調(diào)節(jié)一些正則表達式和參數(shù)。爬取的思路是通用的,無非就是找到視頻的流式數(shù)據(jù),進行保存。思路有了,編寫代碼就是體力活了。
此外,如果你只是想要一些酷炫、搞笑、可愛的視頻資源,玩一下微信 8.0 的狀態(tài),請在公眾號「Python七號」回復(fù)「視頻」,即可獲取微信 8.0 的狀態(tài)視頻合集的下載鏈接:

該關(guān)鍵詞不定期更新一些有趣好玩的視頻。后面會更新其他網(wǎng)站用Python 爬取視頻的方法,敬請關(guān)注「Python七號」,設(shè)為星標(biāo),第一時間接收更新。
資料
有沒有微信8.0狀態(tài)沙雕又可愛的視頻或圖片?: https://www.zhihu.com/question/441253090
留言討論
