化抓取抖音視頻:繞過反爬與實(shí)戰(zhàn)代碼詳解)
1. 項(xiàng)目概述與核心價(jià)值最近在數(shù)據(jù)分析和內(nèi)容研究領(lǐng)域一個(gè)高頻需求浮出水面如何高效、穩(wěn)定地獲取短視頻平臺(tái)上的公開內(nèi)容數(shù)據(jù)。無論是做競(jìng)品分析、輿情監(jiān)控還是進(jìn)行學(xué)術(shù)研究手動(dòng)一個(gè)個(gè)去翻看、下載視頻效率低得令人發(fā)指。于是自動(dòng)化抓取技術(shù)就成了剛需。今天要聊的就是利用 Selenium 這個(gè)老牌自動(dòng)化測(cè)試工具來抓取抖音網(wǎng)頁(yè)版視頻的完整方案。這可不是簡(jiǎn)單的“點(diǎn)一下按鈕”而是一套涉及模擬真人操作、繞過基礎(chǔ)反爬、處理動(dòng)態(tài)加載內(nèi)容的系統(tǒng)工程。Selenium 的核心價(jià)值在于它能驅(qū)動(dòng)真實(shí)的瀏覽器如 Chrome、Edge進(jìn)行操作。對(duì)于抖音這類嚴(yán)重依賴 JavaScript 動(dòng)態(tài)渲染內(nèi)容的單頁(yè)應(yīng)用SPA來說直接用 requests 庫(kù)請(qǐng)求接口往往困難重重因?yàn)槠浣涌趨?shù)加密復(fù)雜、變化頻繁。而 Selenium 模擬真人瀏覽所見即所得從某種程度上說它是在“降維打擊”。但隨之而來的挑戰(zhàn)也不少如何保證抓取穩(wěn)定性如何處理無限滾動(dòng)的瀑布流如何高效下載視頻文件以及最重要的如何在合規(guī)的框架內(nèi)進(jìn)行操作這篇文章我將結(jié)合多次實(shí)戰(zhàn)踩坑的經(jīng)驗(yàn)為你拆解每一個(gè)環(huán)節(jié)提供一套可直接復(fù)現(xiàn)、且考慮了長(zhǎng)期維護(hù)性的解決方案。2. 環(huán)境準(zhǔn)備與工具選型解析工欲善其事必先利其器。在開始寫代碼之前環(huán)境和工具的選擇直接決定了后續(xù)開發(fā)的效率和腳本的穩(wěn)定性。2.1 核心工具Selenium 與 WebDriver首先明確我們用的是 Selenium WebDriver這是 Selenium 的核心組件用于控制瀏覽器。別和已經(jīng)過時(shí)的 Selenium IDE錄制回放工具或 Selenium RC 搞混了。Python 環(huán)境下我們通過selenium包來調(diào)用 WebDriver。瀏覽器選擇首選 Chrome 或 EdgeChromium 內(nèi)核。因?yàn)樗鼈冇脩艋鶖?shù)大WebDriver 支持最成熟遇到問題也最容易找到解決方案。Firefox 也可以但在一些復(fù)雜的頁(yè)面交互和性能上我個(gè)人感覺 Chrome 系更穩(wěn)定。WebDriver 管理手動(dòng)下載 ChromeDriver 并與 Chrome 瀏覽器版本匹配是個(gè)老辦法但很麻煩。強(qiáng)烈推薦使用webdriver-manager這個(gè)第三方庫(kù)。它能自動(dòng)檢測(cè)你本地安裝的瀏覽器版本并下載匹配的 WebDriver省去了無數(shù)排查“版本不匹配”錯(cuò)誤的時(shí)間。pip install selenium webdriver-manager2.2 輔助工具庫(kù)除了 Selenium還需要幾個(gè)幫手requests用于最終下載視頻文件。Selenium 負(fù)責(zé)找到視頻地址下載這種 IO 密集型任務(wù)交給更專業(yè)的 requests。BeautifulSoup4雖然 Selenium 可以find_element但有時(shí)用 BeautifulSoup 來解析已經(jīng)獲取到的頁(yè)面源碼會(huì)更靈活。lxml作為 BeautifulSoup 的解析器速度比內(nèi)置的html.parser快很多。tqdm給下載進(jìn)度加個(gè)進(jìn)度條體驗(yàn)感好很多。安裝命令一并奉上pip install requests beautifulsoup4 lxml tqdm2.3 開發(fā)環(huán)境配置建議我強(qiáng)烈建議使用虛擬環(huán)境如venv或conda來管理項(xiàng)目依賴避免包沖突。IDE 選擇 VSCode 或 PyCharm 都可以關(guān)鍵是要配置好調(diào)試功能。因?yàn)榕老x腳本運(yùn)行時(shí)間長(zhǎng)出錯(cuò)點(diǎn)可能很深沒有調(diào)試器光靠print會(huì)非常痛苦。另外準(zhǔn)備一個(gè)穩(wěn)定的網(wǎng)絡(luò)環(huán)境。由于需要長(zhǎng)時(shí)間運(yùn)行瀏覽器并加載大量媒體資源網(wǎng)絡(luò)波動(dòng)可能導(dǎo)致腳本超時(shí)失敗。可以考慮使用有線網(wǎng)絡(luò)而非 Wi-Fi。注意所有操作應(yīng)僅針對(duì)抖音網(wǎng)頁(yè)版www.douyin.com上公開可見的內(nèi)容。任何嘗試訪問非公開內(nèi)容、突破平臺(tái)限制的行為不僅違反平臺(tái)規(guī)則也可能觸及法律紅線本項(xiàng)目堅(jiān)決杜絕。3. 核心思路與反爬策略應(yīng)對(duì)抖音網(wǎng)頁(yè)端的反爬機(jī)制是層層遞進(jìn)的我們的策略也需要“兵來將擋水來土掩”。3.1 為何選擇 Selenium 而非直接請(qǐng)求 API很多新手會(huì)想直接抓包找到視頻的mp4地址用 requests 下載不就好了這在幾年前可能還行得通。現(xiàn)在的抖音其數(shù)據(jù)接口XHR/Fetch 請(qǐng)求參數(shù)加密非常復(fù)雜像_signature、X-Bogus等參數(shù)需要逆向整個(gè) JavaScript 加密邏輯才能生成難度極大且對(duì)方一更新就得重來。而 Selenium 模擬真人操作讓瀏覽器自己去執(zhí)行 JS、渲染頁(yè)面我們直接從渲染好的 DOM 樹里提取視頻鏈接避開了最復(fù)雜的加密環(huán)節(jié)。這是一種“繞過”而非“破解”的思路。3.2 抖音網(wǎng)頁(yè)版頁(yè)面加載邏輯分析抖音網(wǎng)頁(yè)版的主頁(yè)或用戶頁(yè)視頻列表是典型的“瀑布流”形式。隨著鼠標(biāo)滾動(dòng)頁(yè)面會(huì)動(dòng)態(tài)加載新的視頻卡片。每個(gè)視頻卡片是一個(gè)獨(dú)立的div容器里面包含了視頻播放器、描述、點(diǎn)贊評(píng)論等信息。視頻播放器通常是一個(gè)video標(biāo)簽但其src屬性可能是空的或是一個(gè)模糊的預(yù)覽地址真正的視頻流地址需要通過觸發(fā)播放或監(jiān)聽網(wǎng)絡(luò)請(qǐng)求才能獲取。核心邏輯是滾動(dòng)頁(yè)面 - 觸發(fā)新內(nèi)容加載 - 定位視頻卡片 - 設(shè)法獲取清晰視頻源地址。3.3 關(guān)鍵反爬措施與應(yīng)對(duì)方案User-Agent 檢測(cè)這是最基本的。Selenium 啟動(dòng)的瀏覽器會(huì)有默認(rèn)的 UA其中包含類似HeadlessChrome或顯式的自動(dòng)化測(cè)試標(biāo)識(shí)。我們需要將其覆蓋為普通瀏覽器的 UA。from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() user_agent Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 options.add_argument(fuser-agent{user_agent})WebDriver 特征檢測(cè)抖音的 JS 可以檢測(cè)navigator.webdriver屬性。在普通瀏覽器中它是false或undefined而在 Selenium 控制的瀏覽器中通常是true。這是識(shí)別自動(dòng)化腳本的最直接特征。應(yīng)對(duì)方案使用options.add_experimental_option(excludeSwitches, [enable-automation])和options.add_experimental_option(useAutomationExtension, False)可以部分解決。但更徹底的方法是在啟動(dòng)瀏覽器后通過執(zhí)行 CDPChrome DevTools Protocol命令來覆蓋這個(gè)屬性。driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); })行為模式檢測(cè)如果鼠標(biāo)以像素級(jí)的精確度直線移動(dòng)或者頁(yè)面滾動(dòng)是勻速的、定時(shí)的很容易被識(shí)別為非人類。需要引入隨機(jī)性和人性化延遲。IP 請(qǐng)求頻率限制即使模擬瀏覽器短時(shí)間內(nèi)請(qǐng)求過多頁(yè)面依然可能被限制訪問。需要控制爬取速度并考慮使用代理 IP 池本項(xiàng)目暫不深入但你需要有這個(gè)意識(shí)。4. 實(shí)戰(zhàn)代碼從啟動(dòng)瀏覽器到獲取視頻鏈接理論說完我們開始動(dòng)手。下面我將分步驟給出關(guān)鍵代碼并解釋每一行背后的意圖。4.1 瀏覽器初始化與隱形設(shè)置目標(biāo)是啟動(dòng)一個(gè)“看起來像真人”的瀏覽器。from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import random def init_driver(): chrome_options webdriver.ChromeOptions() # 1. 設(shè)置用戶代理 chrome_options.add_argument(user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36) # 2. 禁用自動(dòng)化提示欄和擴(kuò)展 chrome_options.add_experimental_option(excludeSwitches, [enable-automation]) chrome_options.add_experimental_option(useAutomationExtension, False) # 3. 可選無頭模式后臺(tái)運(yùn)行不顯示界面。調(diào)試階段建議關(guān)閉即注釋掉下面這行。 # chrome_options.add_argument(--headless) # 4. 其他優(yōu)化參數(shù) chrome_options.add_argument(--disable-blink-featuresAutomationControlled) chrome_options.add_argument(--disable-gpu) chrome_options.add_argument(--no-sandbox) # Linux環(huán)境下有時(shí)需要 chrome_options.add_argument(--disable-dev-shm-usage) # Docker或小內(nèi)存環(huán)境需要 # 使用 webdriver-manager 自動(dòng)管理驅(qū)動(dòng) service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionschrome_options) # 5. 執(zhí)行CDP命令覆蓋webdriver屬性 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); }) return driver關(guān)鍵點(diǎn)解釋--disable-blink-featuresAutomationControlled禁用一些可能暴露自動(dòng)化狀態(tài)的 Blink 特性。無頭模式 (--headless) 雖然節(jié)省資源但在應(yīng)對(duì)復(fù)雜反爬時(shí)有界面的瀏覽器往往更穩(wěn)定因?yàn)楦咏鎸?shí)用戶。建議前期調(diào)試用有頭模式。CDP 命令是在瀏覽器頁(yè)面加載任何腳本之前先注入我們的腳本將navigator.webdriver屬性覆蓋掉這是目前最有效的隱藏手段之一。4.2 模擬登錄與訪問目標(biāo)頁(yè)面抖音大部分內(nèi)容需要登錄才能無限瀏覽。我們可以讓 Selenium 控制瀏覽器完成掃碼登錄。def login_douyin(driver): 訪問抖音并等待用戶手動(dòng)掃碼登錄 login_url https://www.douyin.com driver.get(login_url) print(請(qǐng)掃描頁(yè)面二維碼登錄抖音...) # 等待登錄成功的一個(gè)標(biāo)志頁(yè)面跳轉(zhuǎn)或出現(xiàn)用戶頭像 # 這里以等待“首頁(yè)”或“推薦”標(biāo)簽出現(xiàn)為例實(shí)際可根據(jù)頁(yè)面變化調(diào)整 try: WebDriverWait(driver, 120).until( # 給2分鐘時(shí)間掃碼 EC.presence_of_element_located((By.XPATH, //*[contains(text(), 推薦) or contains(text(), 首頁(yè))])) ) print(登錄成功) time.sleep(3) # 登錄后稍作等待 except Exception as e: print(登錄等待超時(shí)或失敗:, e) # 這里可以加入重試或退出邏輯實(shí)操心得登錄環(huán)節(jié)自動(dòng)化難度高且風(fēng)險(xiǎn)大可能觸發(fā)驗(yàn)證碼。讓腳本暫停人工掃碼登錄是最穩(wěn)定、最安全的方式。登錄后瀏覽器會(huì)保存 cookies后續(xù)操作都在已登錄狀態(tài)下進(jìn)行。4.3 模擬滾動(dòng)與動(dòng)態(tài)內(nèi)容加載登錄后我們進(jìn)入目標(biāo)頁(yè)面如某個(gè)用戶主頁(yè)或“推薦”頁(yè)開始滾動(dòng)加載視頻。def scroll_to_load(driver, scroll_times10): 模擬人工滾動(dòng)頁(yè)面觸發(fā)內(nèi)容加載 last_height driver.execute_script(return document.documentElement.scrollHeight) for i in range(scroll_times): # 隨機(jī)滾動(dòng)距離模擬真人 scroll_distance random.randint(300, 800) driver.execute_script(fwindow.scrollBy(0, {scroll_distance});) # 隨機(jī)等待時(shí)間非常重要 wait_time random.uniform(1.5, 4.0) time.sleep(wait_time) # 計(jì)算新高度 new_height driver.execute_script(return document.documentElement.scrollHeight) # 如果高度沒有變化可能已到底部或加載失敗 if new_height last_height: print(f滾動(dòng) {i1} 次后頁(yè)面高度未變化可能已無新內(nèi)容。) # 可以再嘗試滾動(dòng)一兩次或者跳出循環(huán) time.sleep(2) driver.execute_script(window.scrollBy(0, 500);) time.sleep(2) break last_height new_height print(f已完成第 {i1} 次滾動(dòng)當(dāng)前頁(yè)面高度: {new_height}) # 滾動(dòng)完成后再等待一段時(shí)間讓所有內(nèi)容加載完畢 time.sleep(3)為什么需要隨機(jī)等待和隨機(jī)滾動(dòng)距離固定的時(shí)間間隔和滾動(dòng)距離是機(jī)器行為的典型特征。加入隨機(jī)性是為了更好地模擬人類瀏覽時(shí)的不確定性降低被檢測(cè)的風(fēng)險(xiǎn)。4.4 定位視頻卡片與提取信息頁(yè)面滾動(dòng)加載足夠多的內(nèi)容后我們需要從 DOM 樹中找出所有的視頻卡片。def extract_video_cards(driver): 從當(dāng)前頁(yè)面中提取所有視頻卡片元素 # 抖音視頻卡片的類名或結(jié)構(gòu)可能會(huì)變需要定期檢查更新 # 常見的卡片容器選擇器示例 card_selectors [ div[data-e2euser-post-item], # 用戶主頁(yè)視頻卡片 div[class*DivItemContainer], # 通用卡片類名部分 div[class*xgplayer-container], # 圍繞播放器的容器 ] video_cards [] for selector in card_selectors: cards driver.find_elements(By.CSS_SELECTOR, selector) if cards: print(f使用選擇器 {selector} 找到了 {len(cards)} 個(gè)卡片。) video_cards.extend(cards) # 找到一個(gè)可用的選擇器后可以跳出避免重復(fù) # break # 去重根據(jù)元素ID或位置 unique_cards [] seen_ids set() for card in video_cards: card_id card.id or card.get_attribute(outerHTML)[:100] # 簡(jiǎn)易去重 if card_id not in seen_ids: seen_ids.add(card_id) unique_cards.append(card) print(f去重后共找到 {len(unique_cards)} 個(gè)獨(dú)立的視頻卡片。) return unique_cards注意事項(xiàng)抖音前端的類名 (class) 和數(shù)據(jù)屬性 (>def get_video_url_from_card(card_element): 嘗試從視頻卡片元素中解析出視頻地址 try: # 嘗試1: 查找 video 標(biāo)簽的 src video_tag card_element.find_element(By.TAG_NAME, video) video_url video_tag.get_attribute(src) if video_url and video_url.startswith(http) and video_url.endswith(.mp4): return video_url # 嘗試2: 查找其他可能包含地址的屬性 for attr in [data-src, data-video-url, srcset]: potential_url video_tag.get_attribute(attr) if potential_url and http in potential_url and .mp4 in potential_url: # 可能需要從 srcset 中解析出第一個(gè) URL return potential_url.split(,)[0].strip().split( )[0] except Exception as e: print(f從video標(biāo)簽提取地址失敗: {e}) return None方案二監(jiān)聽網(wǎng)絡(luò)請(qǐng)求推薦更可靠的方法是在觸發(fā)視頻播放例如將鼠標(biāo)懸停在視頻上或點(diǎn)擊播放按鈕時(shí)攔截瀏覽器發(fā)出的網(wǎng)絡(luò)請(qǐng)求從中過濾出.mp4文件的請(qǐng)求。這需要用到 Selenium 的“性能日志”或“網(wǎng)絡(luò)日志”功能但設(shè)置較為復(fù)雜。一個(gè)更簡(jiǎn)單的替代方案是利用瀏覽器開發(fā)者工具的“網(wǎng)絡(luò)”面板手動(dòng)分析一次視頻播放的請(qǐng)求找到其 URL 模式然后用 Selenium 模擬相同操作后直接從頁(yè)面源碼或元素屬性中尋找匹配該模式的字符串。例如抖音的無水印視頻地址通常包含playwm或play等關(guān)鍵詞并且域名可能是aweme.snssdk.com或v3-dy-o.zjcdn.com等。方案三使用預(yù)留接口需謹(jǐn)慎一些分析發(fā)現(xiàn)抖音視頻卡片上可能有一個(gè)a標(biāo)簽其href指向一個(gè)中間頁(yè)面如/video/xxxxxxxxx/。訪問這個(gè)中間頁(yè)面其源碼里可能會(huì)包含更清晰的視頻地址。但這增加了額外的請(qǐng)求可能觸發(fā)風(fēng)控。我的常用混合策略首先嘗試從video標(biāo)簽直接提取。如果失敗則嘗試模擬鼠標(biāo)懸停在視頻卡片上ActionChains觸發(fā)預(yù)覽播放然后從卡片元素中查找更新后的地址。將找到的地址中的playwm帶水印替換為play無水印有時(shí)能直接得到無水印地址這個(gè)規(guī)律并非永遠(yuǎn)有效且平臺(tái)可能修復(fù)。def maybe_get_no_watermark(url): 嘗試將水印地址轉(zhuǎn)換為無水印地址 if url and playwm in url: return url.replace(playwm, play) return url將獲取到的地址保存下來。5. 視頻下載與信息存儲(chǔ)獲取到視頻地址后下載就相對(duì)簡(jiǎn)單了。5.1 使用 Requests 下載文件import requests from tqdm import tqdm import os def download_video(video_url, save_path, filename): 下載視頻文件到本地 if not video_url: print(視頻地址無效跳過下載。) return False # 設(shè)置請(qǐng)求頭模擬瀏覽器 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.douyin.com/, # 重要很多視頻服務(wù)器會(huì)檢查Referer } try: response requests.get(video_url, headersheaders, streamTrue) response.raise_for_status() # 檢查請(qǐng)求是否成功 # 確保保存目錄存在 os.makedirs(save_path, exist_okTrue) file_full_path os.path.join(save_path, f{filename}.mp4) total_size int(response.headers.get(content-length, 0)) with open(file_full_path, wb) as file, tqdm( descfilename, totaltotal_size, unitB, unit_scaleTrue, unit_divisor1024, ) as bar: for chunk in response.iter_content(chunk_size1024): if chunk: file.write(chunk) bar.update(len(chunk)) print(f視頻已下載: {file_full_path}) return True except Exception as e: print(f下載視頻失敗: {e}, URL: {video_url}) return False關(guān)鍵點(diǎn)Referer請(qǐng)求頭至關(guān)重要。很多視頻資源服務(wù)器會(huì)校驗(yàn)請(qǐng)求來源如果不攜帶正確的Referer通常是抖音域名直接返回 403 禁止訪問。5.2 結(jié)構(gòu)化存儲(chǔ)視頻信息除了視頻文件我們通常還想保存視頻的描述、點(diǎn)贊數(shù)、作者等信息。import json import csv from datetime import datetime def save_video_info(info_list, save_path, modejson): 保存視頻元信息支持JSON和CSV格式 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) if mode json: filename os.path.join(save_path, fdouyin_videos_{timestamp}.json) with open(filename, w, encodingutf-8) as f: json.dump(info_list, f, ensure_asciiFalse, indent2) print(f視頻信息已保存至 JSON 文件: {filename}) elif mode csv: filename os.path.join(save_path, fdouyin_videos_{timestamp}.csv) if info_list: keys info_list[0].keys() with open(filename, w, newline, encodingutf-8-sig) as f: # utf-8-sig 解決Excel中文亂碼 writer csv.DictWriter(f, fieldnameskeys) writer.writeheader() writer.writerows(info_list) print(f視頻信息已保存至 CSV 文件: {filename})建議將視頻文件和一個(gè)記錄了所有元信息的文件JSON 或 CSV一起保存方便后續(xù)管理和分析。6. 完整流程整合與優(yōu)雅退出將上述所有函數(shù)整合到一個(gè)主流程中并確保腳本能優(yōu)雅處理中斷和退出。def main(): # 初始化瀏覽器 driver init_driver() all_video_info [] try: # 1. 登錄 login_douyin(driver) # 2. 跳轉(zhuǎn)到目標(biāo)頁(yè)面例如某個(gè)用戶主頁(yè) target_user_url https://www.douyin.com/user/MS4wLjABAAAA... # 替換為目標(biāo)用戶URL driver.get(target_user_url) time.sleep(5) # 等待頁(yè)面加載 # 3. 滾動(dòng)加載內(nèi)容 print(開始滾動(dòng)加載視頻列表...) scroll_to_load(driver, scroll_times15) # 滾動(dòng)15次可根據(jù)需要調(diào)整 # 4. 提取視頻卡片 print(開始提取視頻卡片...) video_cards extract_video_cards(driver) # 5. 遍歷卡片獲取信息并下載 save_dir ./douyin_videos for idx, card in enumerate(video_cards[:20]): # 限制前20個(gè)避免過多 try: print(f\n--- 處理第 {idx1} 個(gè)視頻 ---) # 獲取視頻描述示例 description 無法獲取 try: desc_elem card.find_element(By.CSS_SELECTOR, [data-e2evideo-desc]) description desc_elem.text[:100] # 截取前100字符 except: pass # 獲取視頻地址 video_url get_video_url_from_card(card) if not video_url: print(f 第 {idx1} 個(gè)視頻未找到地址跳過。) continue # 嘗試獲取無水印地址 clean_url maybe_get_no_watermark(video_url) final_url clean_url if clean_url else video_url # 下載視頻 filename fvideo_{idx1}_{int(time.time())} success download_video(final_url, save_dir, filename) # 保存信息 if success: video_info { index: idx1, description: description, video_url: final_url, filename: filename .mp4, fetch_time: datetime.now().isoformat() } all_video_info.append(video_info) # 處理完一個(gè)視頻后隨機(jī)休息一下 time.sleep(random.uniform(2, 5)) except Exception as e: print(f處理第 {idx1} 個(gè)視頻卡片時(shí)發(fā)生錯(cuò)誤: {e}) continue # 6. 保存所有元信息 if all_video_info: save_video_info(all_video_info, save_dir, modejson) save_video_info(all_video_info, save_dir, modecsv) except KeyboardInterrupt: print(\n用戶中斷正在退出...) except Exception as e: print(f主流程發(fā)生未知錯(cuò)誤: {e}) finally: # 確保瀏覽器被關(guān)閉 print(關(guān)閉瀏覽器...) driver.quit() print(程序退出。) if __name__ __main__: main()7. 常見問題排查與實(shí)戰(zhàn)心得即使按照上述步驟操作在實(shí)際運(yùn)行中你依然會(huì)遇到各種問題。這里記錄一些典型的“坑”和解決方法。7.1 元素找不到或選擇器失效問題NoSuchElementException報(bào)錯(cuò)或者extract_video_cards返回空列表。排查頁(yè)面未加載完成在查找元素前增加顯式等待 (WebDriverWait)。不要只用time.sleep。from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By try: element WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 你的選擇器)) ) except TimeoutException: print(元素加載超時(shí))選擇器過時(shí)抖音前端更新了。重新用開發(fā)者工具檢查元素更新extract_video_cards函數(shù)中的選擇器。優(yōu)先使用>from selenium.webdriver.common.action_chains import ActionChains actions ActionChains(driver) actions.move_to_element(card).pause(1).perform() time.sleep(1.5) # 等待視頻加載 # 然后再嘗試獲取 video 標(biāo)簽的 src監(jiān)聽網(wǎng)絡(luò)進(jìn)階這是最可靠的方法。需要啟用 Chrome 的性能日志。from selenium.webdriver.common.desired_capabilities import DesiredCapabilities caps DesiredCapabilities.CHROME caps[goog:loggingPrefs] {performance: ALL} # 在 init_driver 時(shí)傳入 desired_capabilitiescaps # 啟動(dòng)后 driver.get_log(performance) 獲取日志從中過濾出 .mp4 請(qǐng)求。這種方法獲取的地址最直接但代碼復(fù)雜度高需要解析 JSON 日志。嘗試其他屬性除了src檢查video標(biāo)簽的currentSrc屬性JavaScript 屬性需用execute_script獲取或者父級(jí)div上是否藏有地址。7.3 下載被拒絕403 Forbidden問題requests下載時(shí)返回 403 狀態(tài)碼。解決檢查請(qǐng)求頭確保User-Agent和Referer必須是抖音域名已正確設(shè)置。檢查Cookie有時(shí)下載鏈接需要攜帶登錄后的 Cookie。你可以從 Selenium 的driver.get_cookies()中獲取并傳遞給requests的cookies參數(shù)。但注意 Cookie 有域和路徑限制直接傳遞可能不生效。嘗試使用session用requests.Session()對(duì)象保持會(huì)話有時(shí)比單次請(qǐng)求更有效。7.4 腳本運(yùn)行速度慢或不穩(wěn)定優(yōu)化建議減少不必要的等待將固定的time.sleep替換為針對(duì)特定元素的WebDriverWait只在必要時(shí)等待。并行處理可以考慮使用多線程一個(gè)線程負(fù)責(zé)滾動(dòng)加載另一個(gè)線程負(fù)責(zé)解析和下載已加載的卡片。但要注意線程安全和資源競(jìng)爭(zhēng)。資源清理定期清理 Selenium 的日志和緩存避免內(nèi)存泄漏。對(duì)于長(zhǎng)時(shí)間運(yùn)行的腳本可以設(shè)置每處理一定數(shù)量視頻后重啟一次瀏覽器代價(jià)是丟失登錄狀態(tài)。使用更快的選擇器By.CSS_SELECTOR通常比By.XPATH快。確保選擇器盡可能精確避免*這樣的通配符。7.5 關(guān)于合規(guī)性與道德的最后提醒這是我反復(fù)強(qiáng)調(diào)也必須放在最后的心得僅抓取公開數(shù)據(jù)只處理登錄后你能正常瀏覽到的公開視頻。任何嘗試抓取非公開、好友、私密賬號(hào)內(nèi)容的行為都是錯(cuò)誤且危險(xiǎn)的。尊重robots.txt查看https://www.douyin.com/robots.txt。雖然 Selenium 模擬瀏覽器但大規(guī)模抓取仍可能違反其協(xié)議??刂谱ト☆l率在腳本中插入足夠的、隨機(jī)的延遲模擬人類瀏覽速度。不要對(duì)服務(wù)器造成壓力。明確數(shù)據(jù)用途抓取的數(shù)據(jù)應(yīng)用于個(gè)人學(xué)習(xí)、研究或合法合規(guī)的分析。切勿用于商業(yè)倒賣、 spam、誹謗等非法用途。版權(quán)意識(shí)視頻內(nèi)容版權(quán)屬于創(chuàng)作者。未經(jīng)允許不得大規(guī)模轉(zhuǎn)載、傳播或用于商業(yè)目的。自動(dòng)化工具是一把雙刃劍。技術(shù)本身無罪但使用技術(shù)的人需要心中有尺。希望這份詳細(xì)的指南能幫助你在合法的范圍內(nèi)高效地完成數(shù)據(jù)收集工作同時(shí)也能讓你對(duì) Selenium 和現(xiàn)代 Web 反爬機(jī)制有更深入的理解。在實(shí)際操作中保持耐心多觀察、多調(diào)試你會(huì)發(fā)現(xiàn)每個(gè)問題背后都藏著前端工程和網(wǎng)絡(luò)安全的知識(shí)點(diǎn)。