Python爬蟲---爬取抖音短視頻


目錄

 

前言

最近一直想要寫一個抖音爬蟲來批量下載抖音的短視頻,但是經過幾天的摸索我發現了一個很嚴重的問題......抖音實在是難爬!從一開始的網頁分析中就有着很多的坑,但是這幾天的摸索也不是一無所獲,我鼓搗出來了一個問題版的抖音爬蟲(操作較為復雜),所以我也想通過這篇博客來記錄下我分析網頁的過程,也想請教一下路過大佬們,歡迎各位大佬指出問題!

抖音爬蟲制作

選定網頁

想要爬取抖音上面的視頻,就要先找到可以刷小視頻的地址,於是我就開始在網上尋找網頁版的抖音。經過一番尋找,發現抖音根本就沒有網頁版的這個板塊,打開的網頁大多都是如下圖所示提示你下載app的網頁:

想要爬取小視頻的內容,沒有網頁地址可不行。於是我又想到了另一種尋找網頁的方法:

首先我打開了手機抖音,選定了一個喜歡的抖音號,使用復制鏈接的方法來嘗試是否可以在網頁中打開:

將鏈接粘貼到記事本中,發現它是長這個樣子的

https: //v.douyin.com/wGf4e1/

將這個網址在瀏覽器中打開,發現這個網址可以正常顯示

向下滑動,也可以看到這個賬號發布的視頻

ok,到現在為止,我已經選定了將這個頁面作為我獲取數據的起始頁面

選定起始頁之后,我的下一步想法是要去獲取這些小視頻的單獨的網頁地址,於是我又點擊了下面的這些小視頻。這里惡心的地方出現了,無論我點擊哪一個小視頻,彈出來的都是強迫你下載app的界面

於是我又想嘗試上面獲取到網頁的操作來獲取視頻的地址,再次打開手機上的抖音,在這個漫威的賬號下隨便打開一個視頻,點擊右下角的分享,復制它的鏈接:

這個鏈接地址長這個樣子:

#黑寡婦 北美終極預告!黑暗過往揭曉,2020即將強勢開啟漫威電影宇宙第四階段! https://v.douyin.com/wGqCNG/ 復制此鏈接,打開【抖音短視頻】,直接觀看視頻!

我再把這段地址復制到瀏覽器中打開:

打開的確實是視頻頁,點擊播放按鈕也可以播放視頻,所以這就是我們需要記住的第二個頁面。

分析網頁

現在又出現了一個比較麻煩的事情,在瀏覽器中輸入網址過后,跳轉到了視頻的播放頁,但是此時的播放頁地址經過了重定向生成了非常長的一串地址,乍一看毫無規律可講

這是重定向之后的網址:

正常來說請求第一種鏈接https://v.douyin.com/wGqCNG/和第二種重定向之后的鏈接都可以獲取到信息,但是我發現第一種鏈接地址是找不到規律的,所以我猜測第二種網址的規律會更加的好找,先把鏈接地址復制到記事本中:

https://www.iesdouyin.com/share/video/6802189485015633160/?region=CN &mid=6802184753988471559&u_code=388k48lba520&titleType=title&utm_source=copy_link&utm_campaign=client_share&utm_medium=android &app=aweme

看了這么長一串的鏈接,鏈接包含的內容也是非常多的,對分析規律有着很大的干擾,於是我試着精簡一下這個鏈接(刪掉鏈接里面的一些內容,看看是否還能找到頁面)經過了一次又一次的嘗試,我所得到的最簡單的網址如下:

https: //www.iesdouyin.com/share/video/6802189485015633160/?mid=6802184753988471559

這個網址依舊可以打開視頻頁,如果在刪掉一點東西,出來的就是抖音的宣傳頁,所以這個網址就是我所需要的最簡單網址

就一個網址當然是分析不出規律的,於是我又用同樣的方法來得到兩個新網址:

https://www.iesdouyin.com/share/video/6818885848784702728/?region=CN &mid=6818885858780203783&u_code=388k48lba520&titleType=title&utm_source=copy_link&utm_campaign=client_share &utm_medium=android&app=aweme https://www.iesdouyin.com/share/video/6820605884050181379/?region=CN&mid=6820605916115864328&u_code=388k48lba520&titleType=title&utm_source=copy_link&utm_campaign=client_share &utm_medium=android&app=aweme

精簡網址之后,將三個網址放在一起觀察:

https: //www.iesdouyin.com/share/video/6802189485015633160/?mid=6802184753988471559 https://www.iesdouyin.com/share/video/6818885848784702728/?mid=6818885858780203783 https://www.iesdouyin.com/share/video/6820605884050181379/?mid=6820605916115864328

不難發現,這三個網址的區別就在於數字的不同

接下來猜測:這串數字會不會是每個視頻的Id值?

隨后我打開漫威影業抖音號,右擊檢查,按下ctrl+f搜索內容,在搜索框內分別搜索https://www.iesdouyin.com/share/video/6802189485015633160/?mid=6802184753988471559鏈接中的68021894850156331606802189485015633160兩個值

第一個值順利找到,就是我們所猜測的id值,但是搜索第二個值卻得不到任何的返回

這就叫我非常苦惱了,我開始想其他的辦法來獲取到這個值,我嘗試了抓包和其他的一些方法,但是都沒有找到這個值的相關信息。

我經過了一番思考,突然間冒出了一個想法:這個值是不是隨機生成的?

然后我做了一個小小的嘗試,我將這個值改成了隨便的一個數

https: //www.iesdouyin.com/share/video/6802189485015633160/?mid=18987

然而神奇的是,這個網址請求出了數據(去掉這個mid鍵不出數據,將mid隨機賦值卻可以得到數據emmm)

提取id構造網址

經過剛剛的分析,我發現我們想要提取的數據就只有一個id值,然后再用Id值替換掉網址中的數字就可以得到相應的視頻頁面了

id是這段代碼的最重要的部分,經過前面曲折又困難的網頁分析之后,我認為提取id只需要從網頁中用表達式提取數據就可以了,但是我沒想到的是這一步也是比較困難的

我先是在主頁右擊了檢查,然后仔細的觀察了elements里面的元素,我發現id就儲存在elements之中

接着我就想辦法獲得這個頁面中的id信息,我嘗試了直接請求,發現輸出的數據中沒有Id信息; 我又加上了請求頭,依舊沒有id值輸出(這個頁面的元素是動態加載的,雖然不能一次獲取全部Id,但是也不至於一個沒有) ;然后我想到了selenium自動化測試模塊,使用webdriver打開網址打印源碼,可是輸出還是沒變

我查了百度的一些方法,也做了一些嘗試,發現這個頁面所做的反爬確實很難破解。於是我就只能換一條路去嘗試

在谷歌開發者工具中,點擊network選項卡,刷新界面 隨着我刷新的操作,在XHR選項卡下出現了一個名字很奇怪的數據包

點擊他的preview選項卡,點出他的下拉菜單,這里面存儲的正是小視頻的Id信息。但是需要注意的是,這個包里面只存儲了0-20序列的共21條信息

但是從這個主頁中我可以知道,它一共發布了64個短視頻,所以我推斷他還有三個數據包沒有加載出來 ---->我滾動下拉條,觀察有沒有數據包

滾動到最后,驗證了我的猜想,這個頁面有四個數據包且為動態加載

好了,我們已經分析出來了這個網頁的構造,先不要管一共有幾個數據包,先以一個數據包為例提取id信息:

復制網頁鏈接作為他的請求網址

將user-agaent加到請求頭中(這個網頁必須要加請求頭,不然獲取不到數據)

請求得到數據:

import requests import json class Douyin: def page_num(self,max_cursor): #網址后面的隨機參數(我實在分析不出規律) random_field = 'RVb7WBAZG.rGG9zDDDoezEVW-0&dytk=a61cb3ce173fbfa0465051b2a6a9027e' #網址的主體 url = 'https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid=MS4wLjABAAAAF5ZfVgdRbJ3OPGJPMFHnDp2sdJaemZo3Aw6piEtkdOA&count=21&max_cursor=0&aid=1128&_signature=' + random_field #請求頭 headers = { 'user-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36', } response = requests.get(url,headers=headers).text #轉換成json數據 resp = json.loads(response) #遍歷 for data in resp["aweme_list"]: # id值 video_id = data['aweme_id'] # 視頻簡介 video_title = data['desc'] # 構造視頻網址 video_url = 'https://www.iesdouyin.com/share/video/{}/?mid=1' # 填充內容 video_douyin = video_url.format(video_id) print(video_id) print(video_title) print(video_douyin) if __name__ == '__main__': douyin = Douyin() douyin.page_num()

print一下i相關信息:

當前乍一看好像這個代碼沒有問題,但是在執行四五次之后,會出現請求不到數據或返回False的情況,一開始我以為是ip被限制的原因,但是加上了ip池之后也是一樣的結果,后來我才發現:請求不到數據是因為之前請求的url被禁用了,要在抖音詳情頁刷新一下,再把新的數據包的網址復制過來才能重新得到數據(我也不知道這是什么類型的反爬,希望知道的老哥可以告訴我一下)

*我之所以把這個網址分成兩部分來寫,是因為當網址請求不到數據的時候,改變的是末尾的_signature=random_field字段,在請求不到數據的時候只需重新復制一下這個字段就可以了,會簡化一點點代碼

拼接數據包鏈接

上面提取Id的時候講到,我們先拿一個數據包做例子,但是我們要爬的這個用戶的全部視頻,所以就要將它所有的數據包地址都訪問一遍

想要得到這些數據包的地址,就需要分析他們的網址構造,我把這四個數據包的網址全部復制到記事本中,逐個分析他們構造規律

不難看出,這四個數據包的區別就在於max_cursor后面的值的不同,而這個值正好就包包含在它前一個數據包之中,這就說明我們可以從前一個數據包中提取到下一個數據包的max_curso值,從而構造出下一個數據包的鏈接地址

可是第四個數據包也包含着max_cursor的值,我們該在何時停止構造下一個數據包呢?

我把最后一個數據包的max_cursor值復制下來並替換到構造的數據包鏈接中,發現可以跳轉到一個新的網址,這個網址中也有max_cursor的值,但是這個值為0

也可以多找幾個網址測試,最后的結果指向都是0,所以我們可以通過if語句來判斷這個值為0的時候就終止循環
構造網址代碼:

import requests import json class Douyin: def page_num(self,max_cursor): #網址后面的隨機參數(我實在分析不出規律) random_field = 'pN099BAV-oInkBpv2D3.M6TdPe&dytk=a61cb3ce173fbfa0465051b2a6a9027e' #網址的主體 url = 'https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid=MS4wLjABAAAAF5ZfVgdRbJ3OPGJPMFHnDp2sdJaemZo3Aw6piEtkdOA&count=21&max_cursor=' + str(max_cursor) + '&aid=1128&_signature=' + random_field #請求頭 headers = { 'user-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36', } response = requests.get(url,headers=headers).text #轉換成json數據 resp = json.loads(response) #提取到max_cursor max_cursor = resp['max_cursor'] #判斷停止構造網址的條件 if max_cursor==0: return 1 else: print(url) douyin.page_num(max_cursor) if __name__ == '__main__': douyin = Douyin() douyin.page_num(max_cursor=0)

輸出構造后的網址:

獲取視頻地址

現在我們已經可以成功的進入獲取到視頻頁面了,復雜的網頁分析基本已經結束了,后續操作就變得簡單了
先打開小視頻的頁面,右擊檢查,查看元素

經過檢查發現,這個源代碼中並沒有視頻地址,點擊了一下播放按鍵,視頻的地址才會加載出來

這個網址中只包含了一個小視頻

看到這種動態加載的機制,我們首先就應該想到selenium自動化測試工具,步驟是先用selenium打開視頻頁面,再點擊播放按鈕,將此時已經刷新完的網頁源代碼保存,

再從中提取到視頻的真正網址,最后再將調試好的webdriver設置為無界面模式
實現代碼:

from selenium import webdriver from lxml import etree from selenium.webdriver.chrome.options import Options import requests import json import time class Douyin: def page_num(self,max_cursor): #網址后面的隨機參數(我實在分析不出規律) # 設置谷歌無界面瀏覽器 chrome_options = Options() chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') # chromdriver地址 path = r'/home/jmhao/chromedriver' #隨機碼 random_field = 'IU4uXRAbf-iiAwnGoS-puCFOLk&dytk=a61cb3ce173fbfa0465051b2a6a9027e' #網址的主體 url = 'https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid=MS4wLjABAAAAF5ZfVgdRbJ3OPGJPMFHnDp2sdJaemZo3Aw6piEtkdOA&count=21&max_cursor=' + str(max_cursor) + '&aid=1128&_signature=' + random_field #請求頭 headers = { 'user-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36', } response = requests.get(url,headers=headers).text #轉換成json數據 resp = json.loads(response) #提取到max_cursor max_cursor = resp['max_cursor'] #遍歷 for data in resp["aweme_list"]: # id值 video_id = data['aweme_id'] # 視頻簡介 video_title = data['desc'] # 構造視頻網址 video_url = 'https://www.iesdouyin.com/share/video/{}/?mid=1' # 填充內容 video_douyin = video_url.format(video_id) driver = webdriver.Chrome(executable_path=path, options=chrome_options) # 打開視頻界面 driver.get(video_douyin) # 點擊播放按鈕 driver.find_element_by_class_name('play-btn').click() time.sleep(2) # 將網頁源碼存放到變量中 information = driver.page_source # 退出 driver.quit() html = etree.HTML(information) # 提取視頻地址 video_adress = html.xpath("//video[@class='player']/@src") print(video_adress) #判斷停止構造網址的條件 if max_cursor==0: return 1 else: #否則循環構造網址 douyin.page_num(max_cursor) if __name__ == '__main__': douyin = Douyin() douyin.page_num(max_cursor=0)

打印一下視頻的真實網址:

下載視頻

視頻的真實網址我們已經獲得了,接下來只剩下最后一步的操作了---->下載視頻
視頻下載的操作就非常簡單了:

for i in video_adress: #請求視頻 video = requests.get(i,headers=headers).content with open('douyin/' + video_title,'wb') as f: print('正在下載:',video_title) f.write(video)

全部代碼

from selenium import webdriver from lxml import etree from selenium.webdriver.chrome.options import Options import requests import json import time class Douyin: def page_num(self,max_cursor): #網址后面的隨機參數(我實在分析不出規律) # 設置谷歌無界面瀏覽器 chrome_options = Options() chrome_options.add_argument('--headless') chrome_options.add_argument('--disable-gpu') # chromdriver地址 path = r'/home/jmhao/chromedriver' #隨機碼 random_field = '.E3AgBAdou1.AOcbGzS2IvxNwJ&dytk=a61cb3ce173fbfa0465051b2a6a9027e' #網址的主體 url = 'https://www.iesdouyin.com/web/api/v2/aweme/post/?sec_uid=MS4wLjABAAAAF5ZfVgdRbJ3OPGJPMFHnDp2sdJaemZo3Aw6piEtkdOA&count=21&max_cursor' + str(max_cursor) + '&aid=1128&_signature=' + random_field #請求頭 headers = { 'user-agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.129 Safari/537.36', } response = requests.get(url,headers=headers).text #轉換成json數據 resp = json.loads(response) #提取到max_cursor max_cursor = resp['max_cursor'] #遍歷 for data in resp["aweme_list"]: # id值 video_id = data['aweme_id'] # 視頻簡介 video_title = data['desc'] # 構造視頻網址 video_url = 'https://www.iesdouyin.com/share/video/{}/?mid=1' # 填充內容 video_douyin = video_url.format(video_id) driver = webdriver.Chrome(executable_path=path, options=chrome_options) # 打開視頻界面 driver.get(video_douyin) # 點擊播放按鈕 driver.find_element_by_class_name('play-btn').click() time.sleep(2) # 將網頁源碼存放到變量中 information = driver.page_source # 退出 driver.quit() html = etree.HTML(information) # 提取視頻地址 video_adress = html.xpath("//video[@class='player']/@src") for i in video_adress: # 請求視頻 video = requests.get(i, headers=headers).content with open('douyin/' + video_title, 'wb') as f: print('正在下載:', video_title) f.write(video) #判斷停止構造網址的條件 if max_cursor==0: return 1 else: douyin.page_num(max_cursor) return url if __name__ == '__main__': douyin = Douyin() douyin.page_num(max_cursor=0)

實現結果

可以看到這些視頻已經下載到本地了,我們在打開本地文件夾看一下

隨便打開一個視頻文件:

可以播放!至此我這個問題版的抖音爬蟲就做完了

待解決的問題

  • 如何獲取主頁中所有的id地址
  • 為什么請求的url后綴會一直變化,該怎么破解

其實所有的問題都指向了一個地方:該怎么獲取小視頻的id 如果大佬們有更好的方法可以獲取id值,希望大佬們可以不吝提出建議讓我把這個爬蟲的功能再完善!感謝各位!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM