網(wǎng)頁爬蟲實戰(zhàn):從抖音視頻抓取到反爬策略解析)
1. 項目概述為什么用Selenium抓取抖音視頻最近在做一個數(shù)據(jù)分析項目需要一批特定主題的短視頻作為素材。市面上現(xiàn)成的數(shù)據(jù)集要么不夠新要么標(biāo)簽不符合我的需求。于是我自然而然地把目光投向了抖音——這個巨大的內(nèi)容寶庫。手動下載效率太低而且容易出錯。直接調(diào)用官方API對于個人開發(fā)者和小型研究項目來說門檻和風(fēng)險都太高。幾番權(quán)衡之后我決定采用一個相對“溫和”且可控的方案使用Selenium模擬真人操作從抖音網(wǎng)頁版抓取視頻。你可能會問為什么是Selenium在爬蟲領(lǐng)域Scrapy、requests這些庫不是更直接嗎這里的關(guān)鍵在于抖音這類現(xiàn)代單頁應(yīng)用SPA的動態(tài)特性。它的內(nèi)容尤其是視頻流是通過JavaScript異步加載的傳統(tǒng)的靜態(tài)HTML解析方法很難直接獲取到視頻的真實地址。而Selenium的核心價值在于它能驅(qū)動一個真實的瀏覽器如Chrome完整地執(zhí)行頁面上的所有JavaScript代碼讓頁面呈現(xiàn)出最終用戶看到的樣子。這樣我們就能像真人一樣通過瀏覽器的開發(fā)者工具DevTools去“觀察”到網(wǎng)絡(luò)請求從而定位到那個最關(guān)鍵的.mp4文件地址。這本質(zhì)上是一種“所見即所得”的抓取思路雖然效率上不如直接分析接口那么高但勝在穩(wěn)定、直觀且能繞過一些簡單的反爬機(jī)制如需要執(zhí)行JS才能生成的令牌。這個項目適合誰呢如果你是一名數(shù)據(jù)分析師、機(jī)器學(xué)習(xí)愛好者需要構(gòu)建自己的視頻數(shù)據(jù)集或者是一名開發(fā)者想學(xué)習(xí)如何處理動態(tài)網(wǎng)頁和媒體內(nèi)容亦或是僅僅對自動化技術(shù)感興趣想了解如何讓程序模擬人的瀏覽行為那么接下來的內(nèi)容會對你很有幫助。我會從環(huán)境搭建、核心思路、代碼實現(xiàn)到避坑技巧完整地走一遍這個流程。需要提前說明的是本文所有技術(shù)討論均基于學(xué)習(xí)與研究目的旨在探討技術(shù)實現(xiàn)原理實際操作中請務(wù)必嚴(yán)格遵守相關(guān)平臺的服務(wù)條款尊重內(nèi)容創(chuàng)作者的版權(quán)。2. 核心思路與方案設(shè)計2.1 技術(shù)選型Selenium ChromeDriver的組合邏輯選擇Selenium作為主力工具背后有幾個核心考量。首先瀏覽器環(huán)境真實性。抖音的反爬策略會檢測請求頭、Cookie、甚至瀏覽器指紋。使用Selenium驅(qū)動的Chrome瀏覽器其發(fā)出的網(wǎng)絡(luò)請求和產(chǎn)生的瀏覽器指紋與真人操作幾乎無異這為我們提供了天然的掩護(hù)。其次強(qiáng)大的交互模擬能力。抓取過程需要滾動頁面、點(diǎn)擊按鈕如“展開更多評論”、處理彈窗如登錄框這些交互Selenium都能通過定位元素并執(zhí)行點(diǎn)擊、輸入等命令來完美模擬。最后對動態(tài)內(nèi)容的完全渲染。這是解決核心問題的鑰匙只有讓頁面JS執(zhí)行完畢視頻元素和對應(yīng)的源文件地址才會暴露在DOM樹或網(wǎng)絡(luò)請求中。為什么不直接用requests庫模擬接口因為抖音的接口參數(shù)往往經(jīng)過復(fù)雜的加密且頻繁變動逆向工程成本極高。而Selenium的策略是“繞過”接口分析直接獲取最終結(jié)果視頻文件對于快速實現(xiàn)、驗證想法的場景來說路徑更短。當(dāng)然這套方案的代價是資源消耗大每個實例都是一個完整的瀏覽器進(jìn)程和速度相對較慢需要等待頁面加載和渲染。因此它更適合對實時性要求不高、但需要高成功率的中小規(guī)模抓取任務(wù)。配套工具方面ChromeDriver是連接Selenium代碼與Chrome瀏覽器的橋梁版本必須與本地安裝的Chrome瀏覽器嚴(yán)格對應(yīng)否則會無法啟動。瀏覽器開發(fā)者工具DevTools是我們的“眼睛”后續(xù)定位視頻地址全靠它。代碼層面我將使用Python因為它擁有最成熟的Selenium綁定庫和豐富的數(shù)據(jù)處理生態(tài)。2.2 抓取流程的整體架構(gòu)設(shè)計整個抓取流程可以抽象為一個狀態(tài)機(jī)其核心目標(biāo)是安全、穩(wěn)定地獲取到視頻的MP4直鏈。我設(shè)計的流程如下初始化與啟動配置Selenium WebDriver設(shè)置無頭模式Headless后臺運(yùn)行、反檢測參數(shù)并啟動瀏覽器。導(dǎo)航與等待訪問目標(biāo)抖音用戶主頁或特定話題Challenge頁面使用“顯式等待”策略智能等待關(guān)鍵元素如視頻列表容器加載完成這是保證腳本健壯性的關(guān)鍵。頁面滾動與內(nèi)容加載抖音采用無限滾動加載。我們需要模擬滾動操作并判斷何時停止例如達(dá)到目標(biāo)數(shù)量或頁面不再加載新內(nèi)容。視頻鏈接提取這是技術(shù)核心。滾動加載出視頻后我們需要從頁面中解析出每一個視頻帖子的獨(dú)立鏈接即每個視頻的詳情頁URL。深入詳情頁獲取媒體源逐個訪問上一步獲取的詳情頁鏈接。在此頁面內(nèi)通過Selenium執(zhí)行腳本或監(jiān)聽網(wǎng)絡(luò)請求從視頻播放器標(biāo)簽video的src屬性中或者從DevTools Network面板中過濾出的media類型請求里提取出最高清版本的.mp4文件直鏈。下載與存儲使用requests庫配合從瀏覽器中繼承的請求頭特別是Referer和User-Agent下載MP4文件并按照規(guī)則如用戶ID/視頻ID命名存儲。循環(huán)與終止循環(huán)處理所有目標(biāo)視頻鏈接完成后優(yōu)雅關(guān)閉瀏覽器驅(qū)動。這個流程中第4步和第5步是難點(diǎn)和重點(diǎn)。直接在當(dāng)前列表頁可能無法獲取到高清源跳轉(zhuǎn)到詳情頁是更可靠的方案。同時整個流程必須植入足夠的隨機(jī)延遲和人類行為模擬如隨機(jī)滾動幅度以避免觸發(fā)頻率限制。3. 環(huán)境準(zhǔn)備與關(guān)鍵配置3.1 基礎(chǔ)環(huán)境搭建步驟首先確保你的Python環(huán)境建議3.8及以上已就緒。然后通過pip安裝核心庫pip install selenium webdriver-manager這里特別推薦webdriver-manager這個庫。它的一大功勞是能自動下載和管理與你的Chrome瀏覽器版本匹配的ChromeDriver省去了手動查找和配置的麻煩。接下來是初始化WebDriver的代碼這里包含了幾個至關(guān)重要的配置項from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.options import Options def create_driver(): chrome_options Options() # 1. 啟用無頭模式后臺運(yùn)行不顯示GUI chrome_options.add_argument(--headlessnew) # 新版Chrome推薦使用‘new’ # 2. 禁用自動化控制標(biāo)志降低被檢測風(fēng)險 chrome_options.add_experimental_option(excludeSwitches, [enable-automation]) chrome_options.add_experimental_option(useAutomationExtension, False) # 3. 修改navigator.webdriver屬性進(jìn)一步偽裝 chrome_options.add_argument(--disable-blink-featuresAutomationControlled) # 4. 設(shè)置用戶代理User-Agent模擬真實瀏覽器 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) # 5. 其他優(yōu)化參數(shù) chrome_options.add_argument(--disable-gpu) # 某些系統(tǒng)上需要 chrome_options.add_argument(--no-sandbox) # Linux環(huán)境下常需 chrome_options.add_argument(--disable-dev-shm-usage) # 解決共享內(nèi)存問題 # 使用webdriver-manager自動管理驅(qū)動 service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionschrome_options) # 執(zhí)行CDP命令覆蓋WebDriver屬性關(guān)鍵反檢測步驟 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }); }) return driver注意無頭模式雖然節(jié)省資源但在調(diào)試初期建議先注釋掉--headless參數(shù)讓瀏覽器窗口顯示出來。這樣你能直觀地看到腳本的操作過程方便定位問題。3.2 反爬策略的針對性配置抖音這類平臺會對自動化腳本進(jìn)行檢測。上述代碼中的幾個點(diǎn)就是為此準(zhǔn)備的excludeSwitches和useAutomationExtension移除Chrome瀏覽器中“正受到自動測試軟件控制”的提示。--disable-blink-featuresAutomationControlled和execute_cdp_cmd這兩者是組合拳。前者禁用某些可能暴露自動化特征的Blink功能后者直接在頁面加載前注入JavaScript將navigator.webdriver屬性重寫為undefined。很多網(wǎng)站通過檢測這個屬性是否為true來判斷是否為自動化瀏覽器。User-Agent設(shè)置一個常見的、更新的桌面版Chrome UA避免使用默認(rèn)的測試UA。這些措施能應(yīng)對基礎(chǔ)的反爬但要知道高級的反爬系統(tǒng)如行為指紋識別可能結(jié)合鼠標(biāo)移動軌跡、點(diǎn)擊頻率、頁面停留時間等多維度判斷。因此在核心操作邏輯中引入隨機(jī)性至關(guān)重要。4. 核心抓取流程實現(xiàn)4.1 導(dǎo)航目標(biāo)頁面與智能等待假設(shè)我們的目標(biāo)是抓取某個特定用戶主頁下的所有視頻。首先導(dǎo)航到該頁面例如https://www.douyin.com/user/MS4wLjABAAAA...。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 driver create_driver() user_url https://www.douyin.com/user/目標(biāo)用戶_SECRET_UID driver.get(user_url) # 關(guān)鍵使用顯式等待等待視頻列表容器出現(xiàn) try: # 抖音網(wǎng)頁版視頻列表通常在一個類名為‘Eie04v01’或類似結(jié)構(gòu)的div中需實時探查 video_list_container WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.CSS_SELECTOR, div[data-e2euser-post-list])) # 示例選擇器需更新 ) print(頁面核心內(nèi)容加載成功) except Exception as e: print(f等待頁面超時或元素未找到: {e}) driver.quit()這里使用了WebDriverWait配合expected_conditions進(jìn)行顯式等待。它與簡單的time.sleep(10)強(qiáng)制等待有本質(zhì)區(qū)別。顯式等待會每隔一段時間默認(rèn)0.5秒檢查一次條件是否滿足一旦滿足就立即繼續(xù)執(zhí)行最大程度節(jié)省時間并提高穩(wěn)定性。presence_of_element_located表示元素出現(xiàn)在DOM中即可不一定需要可見或可點(diǎn)擊。實操心得抖音的頁面結(jié)構(gòu)可能頻繁變動>def scroll_and_collect_links(driver, max_scrolls20): collected_links set() # 使用集合避免重復(fù) last_height driver.execute_script(return document.documentElement.scrollHeight) scroll_attempt 0 while scroll_attempt max_scrolls: # 1. 模擬滾動到底部 driver.execute_script(window.scrollTo(0, document.documentElement.scrollHeight);) time.sleep(random.uniform(2.0, 4.0)) # 隨機(jī)等待模擬人類閱讀時間 # 2. 等待新內(nèi)容加載 new_height driver.execute_script(return document.documentElement.scrollHeight) if new_height last_height: # 高度未變可能已加載完畢或遇到“暫時沒有更多了” print(頁面滾動到底部可能已無新內(nèi)容。) # 可以再嘗試等待一下或點(diǎn)擊“加載更多”按鈕如果存在 time.sleep(3) new_height driver.execute_script(return document.documentElement.scrollHeight) if new_height last_height: break # 確認(rèn)加載完畢退出循環(huán) last_height new_height # 3. 在當(dāng)前視圖中提取視頻詳情頁鏈接 # 抖音視頻鏈接通常包含 /video/ 路徑 video_elements driver.find_elements(By.CSS_SELECTOR, a[href*/video/]) for elem in video_elements: href elem.get_attribute(href) if href and douyin.com/video/ in href: # 清理鏈接去除可能的查詢參數(shù)獲取標(biāo)準(zhǔn)格式 clean_link href.split(?)[0] collected_links.add(clean_link) print(f發(fā)現(xiàn)視頻鏈接: {clean_link}) scroll_attempt 1 print(f已完成第 {scroll_attempt} 次滾動已收集 {len(collected_links)} 個唯一鏈接。) # 4. 隨機(jī)休息降低請求頻率 time.sleep(random.uniform(1.0, 2.5)) return list(collected_links) # 執(zhí)行滾動收集 video_detail_links scroll_and_collect_links(driver, max_scrolls15) print(f總共收集到 {len(video_detail_links)} 個視頻詳情頁鏈接。)這段代碼的邏輯是滾動 → 等待加載 → 解析當(dāng)前DOM中的鏈接 → 去重存儲。max_scrolls參數(shù)用于控制最大滾動次數(shù)防止無限循環(huán)。random.uniform引入的隨機(jī)等待時間是模擬人類行為、規(guī)避反爬的重要一環(huán)。4.3 從詳情頁提取視頻直鏈這是最具技術(shù)挑戰(zhàn)性的一步。視頻文件通常不會直接以video src...的形式靜態(tài)寫在HTML里而是由前端播放器動態(tài)加載。我們有幾種策略策略一從video標(biāo)簽直接提取如果幸運(yùn)的話在部分頁面結(jié)構(gòu)下視頻源地址可能直接存在于video標(biāo)簽的src屬性中。我們可以嘗試查找def get_video_url_from_video_tag(driver, page_url): driver.get(page_url) time.sleep(random.uniform(3, 5)) # 等待頁面和視頻播放器初始化 try: # 嘗試尋找video標(biāo)簽 video_tag WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.TAG_NAME, video)) ) video_src video_tag.get_attribute(src) if video_src and video_src.endswith(.mp4): return video_src except Exception as e: print(f從video標(biāo)簽獲取失敗: {e}) return None策略二監(jiān)聽網(wǎng)絡(luò)請求更可靠的方法更通用的方法是在頁面加載過程中從瀏覽器開發(fā)者工具的網(wǎng)絡(luò)請求記錄中篩選出視頻media類型的請求。Selenium本身不直接提供網(wǎng)絡(luò)請求監(jiān)聽功能但我們可以通過啟用Chrome的性能日志Performance Log或使用DevTools Protocol (CDP) 來實現(xiàn)。這里介紹使用CDP命令Network.enable來監(jiān)聽請求的方法def get_video_url_via_cdp(driver, page_url): # 啟用網(wǎng)絡(luò)跟蹤 driver.execute_cdp_cmd(Network.enable, {}) mp4_urls [] # 定義請求事件監(jiān)聽器Python中通過CDP命令回調(diào)較復(fù)雜此處簡化為腳本注入后獲取 # 更實用的方法是先導(dǎo)航到一個空白頁設(shè)置監(jiān)聽再導(dǎo)航到目標(biāo)頁最后讀取日志。 # 但Selenium Python對CDP事件回調(diào)支持不直接一個變通方案是 # 1. 導(dǎo)航到目標(biāo)頁 driver.get(page_url) time.sleep(4) # 確保視頻請求發(fā)生 # 2. 嘗試從當(dāng)前頁面的全局變量或播放器實例中獲取抖音可能將地址存儲在JS變量中 # 這是一個探索性過程需要分析抖音前端代碼 script // 嘗試尋找視頻播放器組件 let videoPlayers document.querySelectorAll(video); for (let player of videoPlayers) { if (player.src player.src.includes(.mp4)) { return player.src; } // 有些播放器使用source標(biāo)簽 let source player.querySelector(source[typevideo/mp4]); if (source source.src) { return source.src; } } // 嘗試從常見的全局變量或React/Vue組件狀態(tài)中尋找需要具體分析 // 例如 window._feInstance?.state?.videoInfo?.url return null; video_url driver.execute_script(script) if video_url: return video_url # 3. 如果上述方法失敗一個“笨”但有效的方法是分析頁面HTML中可能隱藏的JSON數(shù)據(jù) # 抖音經(jīng)常將視頻信息放在script typeapplication/json或某個大JSON字符串中 page_source driver.page_source # 這里可以編寫正則表達(dá)式來搜索包含.mp4鏈接的JSON塊例如 import re # 這是一個非常簡化的示例實際模式復(fù)雜得多 pattern r\(https:[^\\s]*?\.mp4[^\\s]*?)\ found_urls re.findall(pattern, page_source) for url in found_urls: if douyin in url or tiktok in url: # 簡單過濾 return url return None核心技巧在實際操作中策略二中的“分析頁面JSON數(shù)據(jù)”是最穩(wěn)定、最高效的方法。你需要使用開發(fā)者工具在Network面板中搜索.mp4找到一個視頻請求后在Headers或Preview中查看其完整URL。然后在Elements面板中搜索這個URL的一部分或者搜索videoInfo、playApi等關(guān)鍵詞往往能找到一段包含所有視頻信息的巨大JSON對象。將這個JSON對象的路徑例如某個script idRENDER_DATA標(biāo)簽的內(nèi)容通過driver.execute_script(return document.getElementById(RENDER_DATA).textContent;)獲取下來再用json.loads可能需要urllib.parse.unquote解碼解析就能結(jié)構(gòu)化地提取出視頻標(biāo)題、描述、點(diǎn)贊數(shù)以及不同清晰度的視頻播放地址。這是專業(yè)爬蟲的常用手段。4.4 視頻下載與存儲管理獲取到MP4直鏈后下載就相對簡單了。但需要注意下載時需要攜帶合適的請求頭特別是Referer通常需要設(shè)置為抖音的域名否則服務(wù)器可能會拒絕服務(wù)。import requests import os from urllib.parse import urlparse def download_video(video_url, referer_url, save_dir./videos): if not video_url: return False os.makedirs(save_dir, exist_okTrue) # 從URL中提取一個合理的文件名如視頻ID parsed_url urlparse(video_url) # 假設(shè)URL格式為 .../video_id.mp4?... path parsed_url.path video_id os.path.splitext(os.path.basename(path))[0] or unknown_video filename f{video_id}.mp4 filepath os.path.join(save_dir, filename) 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: referer_url, # 關(guān)鍵告訴服務(wù)器請求來自抖音頁面 Accept: */*, Accept-Language: zh-CN,zh;q0.9, Connection: keep-alive, } try: print(f正在下載: {filename}) response requests.get(video_url, headersheaders, streamTrue, timeout30) response.raise_for_status() # 檢查HTTP錯誤 with open(filepath, wb) as f: for chunk in response.iter_content(chunk_size8192): f.write(chunk) print(f下載成功: {filepath}) return True except Exception as e: print(f下載失敗 {video_url}: {e}) return False # 在主循環(huán)中調(diào)用 for idx, detail_link in enumerate(video_detail_links): print(f\n處理第 {idx1}/{len(video_detail_links)} 個視頻: {detail_link}) mp4_url get_video_url_via_cdp(driver, detail_link) # 或者用其他方法 if mp4_url: success download_video(mp4_url, detail_link) if not success: # 可以加入重試邏輯 pass # 處理間隔隨機(jī)化 time.sleep(random.uniform(2, 5))5. 高級技巧與穩(wěn)定性優(yōu)化5.1 處理登錄狀態(tài)與Cookie要抓取個人主頁或“朋友”標(biāo)簽頁的內(nèi)容需要登錄狀態(tài)。Selenium可以模擬登錄但更推薦手動登錄后導(dǎo)出Cookie供腳本使用這樣更穩(wěn)定且能繞過復(fù)雜的登錄驗證如滑塊驗證碼。用配置好的Selenium驅(qū)動打開抖音并手動登錄。登錄成功后使用driver.get_cookies()獲取所有Cookie。將Cookie列表以JSON格式保存到本地文件。在后續(xù)的抓取腳本啟動后先訪問一次抖音首頁然后通過driver.add_cookie(cookie_dict)循環(huán)添加所有Cookie最后刷新頁面或訪問目標(biāo)頁即可保持登錄狀態(tài)。def load_cookies_from_file(driver, cookie_filecookies.json): import json driver.get(https://www.douyin.com) # 先導(dǎo)航到域名 time.sleep(2) with open(cookie_file, r, encodingutf-8) as f: cookies json.load(f) for cookie in cookies: # 確保domain字段有效抖音可能是 .douyin.com if domain in cookie and cookie[domain] not in [.douyin.com, www.douyin.com]: cookie[domain] .douyin.com try: driver.add_cookie(cookie) except Exception as e: print(f添加Cookie失敗: {cookie.get(name)}, 錯誤: {e}) driver.refresh() # 刷新使Cookie生效 time.sleep(3) # 檢查是否登錄成功例如查找用戶頭像元素 try: driver.find_element(By.CSS_SELECTOR, div[data-e2euser-avatar]) print(Cookie登錄成功) return True except: print(Cookie登錄失敗可能需要重新獲取。) return False5.2 應(yīng)對頁面結(jié)構(gòu)變化與元素定位抖音前端迭代很快選擇器很容易失效。提高腳本魯棒性的方法使用多種定位策略組合不要只依賴一個CSS選擇器。可以組合>def find_element_with_retry(driver, selectors, byBy.CSS_SELECTOR, timeout10): 嘗試多個選擇器直到找到一個元素。 selectors: 選擇器字符串列表 for selector in selectors: try: element WebDriverWait(driver, timeout).until( EC.presence_of_element_located((by, selector)) ) return element except: continue raise NoSuchElementException(f所有選擇器都未找到元素: {selectors}) # 使用示例 video_container find_element_with_retry(driver, [ div[data-e2euser-post-list], .Eie04v01, //div[contains(class, video-list)] # XPath示例 ])5.3 引入更逼真的人類行為模擬簡單的隨機(jī)等待還不夠。我們可以模擬更復(fù)雜的行為模式隨機(jī)滾動幅度不完全滾動到底部有時滾動一半有時小幅回滾。模擬鼠標(biāo)移動使用Selenium的ActionChains在頁面上隨機(jī)移動鼠標(biāo)。隨機(jī)點(diǎn)擊非功能區(qū)域偶爾在空白處點(diǎn)擊一下。變化操作間隔使用正態(tài)分布或隨機(jī)區(qū)間來生成等待時間使其更接近真人操作。from selenium.webdriver.common.action_chains import ActionChains import random import numpy as np def human_like_scroll(driver): current_height driver.execute_script(return document.documentElement.scrollHeight) viewport_height driver.execute_script(return window.innerHeight) # 隨機(jī)決定滾動目標(biāo)位置例如滾動到頁面高度的70%-100%之間 scroll_to_ratio random.uniform(0.7, 1.0) target_scroll int(current_height * scroll_to_ratio) # 分步滾動模擬人類的不連貫性 steps random.randint(3, 6) step_size target_scroll // steps for i in range(steps): driver.execute_script(fwindow.scrollBy(0, {step_size});) time.sleep(random.uniform(0.2, 0.8)) # 每步之間短暫停頓 # 偶爾小幅回滾 if random.random() 0.7: driver.execute_script(window.scrollBy(0, -100);) time.sleep(random.uniform(0.5, 1.2)) # 模擬鼠標(biāo)隨機(jī)移動 actions ActionChains(driver) x_offset random.randint(-50, 50) y_offset random.randint(-50, 50) try: # 移動到某個隨機(jī)元素上如果找不到則移動到文檔某處 some_elements driver.find_elements(By.TAG_NAME, div)[:10] if some_elements: actions.move_to_element(random.choice(some_elements)).move_by_offset(x_offset, y_offset).perform() except: pass time.sleep(random.uniform(1.5, 3.5)) # 滾動后“閱讀”時間6. 常見問題排查與解決方案實錄在實際操作中你幾乎一定會遇到下面這些問題。這里是我踩過坑后總結(jié)的排查清單。6.1 元素找不到NoSuchElementException這是最常見的問題??赡茉?頁面未加載完。解決方案始終使用WebDriverWait進(jìn)行顯式等待而不是time.sleep。確保等待條件正確如元素可見(visibility_of_element_located)或可點(diǎn)擊(element_to_be_clickable)??赡茉?選擇器已過時。解決方案打開瀏覽器開發(fā)者工具使用選擇器檢查工具重新定位元素。優(yōu)先使用>