戰(zhàn):繞過(guò)反爬的瀏覽器指紋隱身技術(shù))
1. 項(xiàng)目概述當(dāng)Selenium遇上“反爬”高墻如果你做過(guò)Web自動(dòng)化測(cè)試或者數(shù)據(jù)采集大概率對(duì)Selenium又愛(ài)又恨。愛(ài)的是它功能強(qiáng)大能模擬真人操作瀏覽器幾乎無(wú)所不能恨的是現(xiàn)在稍微有點(diǎn)規(guī)模的網(wǎng)站都能輕易識(shí)別出你是“機(jī)器人”。你精心編寫的腳本可能剛跑起來(lái)頁(yè)面就彈出一個(gè)驗(yàn)證碼或者直接返回一個(gè)空白頁(yè)告訴你“訪問(wèn)過(guò)于頻繁”。這感覺(jué)就像你穿著夜行衣去執(zhí)行任務(wù)結(jié)果一進(jìn)門所有的警報(bào)器都響了保安拿著手電筒直接照在你臉上。這就是我們今天要聊的核心問(wèn)題如何讓Selenium驅(qū)動(dòng)的瀏覽器在目標(biāo)網(wǎng)站眼里變得“隱形”。我最近在一個(gè)需要模擬真實(shí)用戶行為進(jìn)行大規(guī)模數(shù)據(jù)驗(yàn)證的項(xiàng)目里就深陷這個(gè)泥潭。常規(guī)的Selenium腳本被目標(biāo)站的風(fēng)控系統(tǒng)識(shí)別率高達(dá)90%以上測(cè)試流程寸步難行。直到我系統(tǒng)地研究和應(yīng)用了Selenium Stealth這套理念和技術(shù)組合才真正解決了問(wèn)題。這不是某一個(gè)具體的、叫做“Stealth”的庫(kù)雖然有一個(gè)同名的Python包而是一整套讓自動(dòng)化瀏覽器指紋“隱身”的實(shí)戰(zhàn)策略。簡(jiǎn)單來(lái)說(shuō)Selenium Stealth的目標(biāo)就是通過(guò)一系列技術(shù)手段修改或掩蓋那些能被網(wǎng)站檢測(cè)到的、屬于自動(dòng)化程序的“非人類”特征使其指紋與普通用戶通過(guò)Chrome、Firefox等瀏覽器手動(dòng)訪問(wèn)時(shí)幾乎一致。這不僅僅是加個(gè)user-agent那么簡(jiǎn)單它涉及到WebDriver屬性、JavaScript變量、瀏覽器特性、行為模式等數(shù)十個(gè)維度的對(duì)抗。接下來(lái)我將結(jié)合我的實(shí)戰(zhàn)經(jīng)驗(yàn)把這套“隱形術(shù)”從原理到操作掰開(kāi)揉碎了講清楚。2. 核心需求解析為什么你的Selenium腳本會(huì)被“發(fā)現(xiàn)”在深入技術(shù)細(xì)節(jié)前我們必須先弄明白對(duì)手是如何工作的。網(wǎng)站檢測(cè)自動(dòng)化腳本主要依賴于“瀏覽器指紋”技術(shù)。指紋就像你的網(wǎng)絡(luò)身份證由瀏覽器暴露給網(wǎng)站的眾多屬性信息組合而成。自動(dòng)化工具會(huì)無(wú)意中留下一些獨(dú)特的“指紋”。2.1 關(guān)鍵檢測(cè)點(diǎn)剖析網(wǎng)站通常從以下幾個(gè)層面進(jìn)行檢測(cè)WebDriver對(duì)象屬性這是最直接的證據(jù)。通過(guò)navigator.webdriver這個(gè)屬性普通瀏覽器返回undefined或false而由Selenium控制的瀏覽器會(huì)返回true。這是許多風(fēng)控系統(tǒng)的首要檢查項(xiàng)。插件與語(yǔ)言列表navigator.plugins插件列表和navigator.languages語(yǔ)言列表在自動(dòng)化環(huán)境中可能為空、格式異常或與聲稱的瀏覽器版本不匹配。屏幕分辨率與色彩深度自動(dòng)化腳本有時(shí)會(huì)使用非標(biāo)準(zhǔn)的窗口大小或者色彩深度screen.colorDepth,screen.pixelDepth與常見(jiàn)設(shè)備不符。HTTP請(qǐng)求頭特征雖然Selenium可以設(shè)置User-Agent但其他頭部信息如Accept-Language,Sec-CH-UA用戶代理客戶端提示等可能缺失或格式不正確無(wú)法完美模擬特定瀏覽器版本。JavaScript執(zhí)行環(huán)境差異一些原生JavaScript對(duì)象的屬性或方法如window.chrome,document.hidden在自動(dòng)化環(huán)境下的行為可能有細(xì)微差別。行為模式異常這是更高級(jí)的檢測(cè)。人類的操作有隨機(jī)延遲、不精確的鼠標(biāo)移動(dòng)軌跡、不規(guī)律的滾動(dòng)速度。而自動(dòng)化腳本的點(diǎn)擊、輸入、滾動(dòng)往往過(guò)于精準(zhǔn)和迅速像是機(jī)器在“瞬移”。2.2 不同場(chǎng)景下的需求差異你的“隱形”需求強(qiáng)度取決于目標(biāo)網(wǎng)站的風(fēng)控等級(jí)基礎(chǔ)內(nèi)容抓取與測(cè)試對(duì)于個(gè)人博客、技術(shù)文檔等無(wú)風(fēng)控或弱風(fēng)控站點(diǎn)可能只需要處理webdriver屬性即可。電商價(jià)格監(jiān)控、社交媒體數(shù)據(jù)收集這類網(wǎng)站通常有反爬機(jī)制需要處理核心的JavaScript環(huán)境變量和請(qǐng)求頭。金融數(shù)據(jù)、高級(jí)別平臺(tái)自動(dòng)化這是“地獄難度”。需要綜合運(yùn)用所有技術(shù)并模擬人類行為模式任何一環(huán)出錯(cuò)都可能導(dǎo)致失敗。我的項(xiàng)目屬于第二種目標(biāo)是幾個(gè)主流電商和內(nèi)容平臺(tái)因此需要一套中等偏上強(qiáng)度的隱身方案。3. 技術(shù)方案選型從“補(bǔ)丁”到“系統(tǒng)工程”面對(duì)這些檢測(cè)點(diǎn)我們有不同層次的技術(shù)方案可以選擇。我將其分為三個(gè)層級(jí)3.1 層級(jí)一基礎(chǔ)屬性覆蓋治標(biāo)不治本這是最初級(jí)的做法也是很多人唯一知道的做法修改User-Agent使用options.add_argument(user-agent...)。移除“自動(dòng)化控制”提示options.add_experimental_option(excludeSwitches, [enable-automation])和options.add_experimental_option(useAutomationExtension, False)。執(zhí)行CDP命令通過(guò)Chrome DevTools Protocol (CDP) 執(zhí)行Page.addScriptToEvaluateOnNewDocument來(lái)覆蓋navigator.webdriver等屬性。為什么這不夠因?yàn)檫@些修改是“打補(bǔ)丁”只覆蓋了最明顯的幾個(gè)點(diǎn)。現(xiàn)代風(fēng)控系統(tǒng)會(huì)進(jìn)行多維度、交叉驗(yàn)證。例如你聲稱自己是Chrome 120但你的navigator.plugins列表是空的這立刻就會(huì)暴露。3.2 層級(jí)二專用隱身庫(kù)快速上手但有局限這就是標(biāo)題中提到的selenium-stealth(Python包)。它是一個(gè)對(duì)層級(jí)一技術(shù)的封裝提供了一鍵式解決方案。from selenium_stealth import stealth # ... 初始化driver后 stealth(driver, languages[en-US, en], vendorGoogle Inc., platformWin32, webgl_vendorIntel Inc., rendererIntel Iris OpenGL Engine, fix_hairlineTrue, )它的優(yōu)點(diǎn)方便開(kāi)箱即用覆蓋了十多個(gè)常見(jiàn)檢測(cè)點(diǎn)。它的缺點(diǎn)更新可能滯后瀏覽器和檢測(cè)技術(shù)都在更新庫(kù)的更新可能跟不上。靈活性不足參數(shù)是預(yù)設(shè)的如果你想模擬一臺(tái)MacBook上的Safari可能需要自己修改源碼。可能被特征識(shí)別大量用戶使用相同的selenium-stealth配置其修改指紋的模式本身可能成為新的檢測(cè)特征。3.3 層級(jí)三自定義CDP與行為模擬終極方案這是最徹底、最靈活的方法。核心是直接使用CDP命令在頁(yè)面加載前注入JavaScript精確地、按需地修改瀏覽器環(huán)境。同時(shí)結(jié)合ActionChains等工具模擬人類操作。為什么我最終選擇了層級(jí)三在項(xiàng)目中期使用selenium-stealth后識(shí)別率從90%降到了50%但仍然不夠。分析日志發(fā)現(xiàn)目標(biāo)網(wǎng)站增加了一些新的、冷門的檢測(cè)點(diǎn)。selenium-stealth沒(méi)有覆蓋到。我需要能夠動(dòng)態(tài)調(diào)整策略針對(duì)特定網(wǎng)站進(jìn)行“外科手術(shù)式”的指紋修改。層級(jí)三方案給了我完全的掌控權(quán)。4. 實(shí)戰(zhàn)構(gòu)建你自己的“隱形”Selenium驅(qū)動(dòng)下面我將以Chrome瀏覽器為例詳細(xì)拆解如何從零開(kāi)始構(gòu)建一個(gè)高度隱身的WebDriver。這套方案是我在項(xiàng)目中反復(fù)調(diào)試后總結(jié)的你可以直接復(fù)制使用并根據(jù)需要調(diào)整。4.1 環(huán)境準(zhǔn)備與基礎(chǔ)配置首先確保你安裝了最新版的Selenium和瀏覽器驅(qū)動(dòng)。pip install selenium webdriver-manager使用webdriver-manager可以自動(dòng)管理驅(qū)動(dòng)版本避免兼容性問(wèn)題。初始化Driver時(shí)我們就要開(kāi)始添加“隱身”配置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_stealth_driver(): chrome_options Options() # 1. 基礎(chǔ)隱身設(shè)置必須 chrome_options.add_argument(--disable-blink-featuresAutomationControlled) chrome_options.add_experimental_option(excludeSwitches, [enable-automation]) chrome_options.add_experimental_option(useAutomationExtension, False) # 2. 模擬真實(shí)用戶代理和語(yǔ)言示例為Windows上的Chrome 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) chrome_options.add_argument(accept-languageen-US,en;q0.9) # 3. 其他實(shí)用參數(shù)減少特征 chrome_options.add_argument(--disable-gpu) # 某些虛擬環(huán)境可能需要 chrome_options.add_argument(--no-sandbox) # Linux環(huán)境常用 chrome_options.add_argument(--disable-dev-shm-usage) # 解決共享內(nèi)存問(wèn)題 chrome_options.add_argument(--disable-blink-featuresAutomationControlled) # 以無(wú)頭模式運(yùn)行時(shí)這個(gè)參數(shù)很重要 # chrome_options.add_argument(--headlessnew) # 如果需要無(wú)頭模式使用new headless # 4. 禁用自動(dòng)化密碼保存提示等 prefs { credentials_enable_service: False, profile.password_manager_enabled: False } chrome_options.add_experimental_option(prefs, prefs) # 使用webdriver-manager自動(dòng)獲取驅(qū)動(dòng) service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionschrome_options) return driver注意--headlessnew是Chrome較新版本的無(wú)頭模式比舊的--headless更隱蔽但并非完全無(wú)法檢測(cè)。對(duì)于高風(fēng)控網(wǎng)站能不用無(wú)頭盡量不用。4.2 核心使用CDP進(jìn)行深度指紋修改這是隱身技術(shù)的核心。我們通過(guò)CDP在文檔創(chuàng)建前執(zhí)行JS覆蓋關(guān)鍵屬性。def apply_stealth_js(driver): # 這段JavaScript是隱身的關(guān)鍵它在頁(yè)面任何腳本執(zhí)行前就修改了環(huán)境。 stealth_js // 1. 覆蓋webdriver屬性 Object.defineProperty(navigator, webdriver, { get: () undefined }); // 2. 覆蓋plugins模擬常見(jiàn)的插件 Object.defineProperty(navigator, plugins, { get: () [1, 2, 3, 4, 5] }); // 3. 覆蓋languages Object.defineProperty(navigator, languages, { get: () [en-US, en] }); // 4. 覆蓋chrome對(duì)象僅針對(duì)Chrome // 注意這里我們模擬了chrome對(duì)象的存在但移除了其runtime屬性自動(dòng)化特征 window.chrome { runtime: {}, // 可以添加其他chrome屬性 }; // 5. 覆蓋permissions API const originalQuery window.navigator.permissions.query; window.navigator.permissions.query (parameters) ( parameters.name notifications ? Promise.resolve({ state: Notification.permission }) : originalQuery(parameters) ); // 6. 處理iframes中的檢測(cè) Object.defineProperty(HTMLIFrameElement.prototype, contentWindow, { get: function() { return window; } }); console.log(Stealth JS injected); # 使用CDP命令執(zhí)行 driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: stealth_js })如何使用在driver.get(url)之前調(diào)用apply_stealth_js(driver)。這樣在目標(biāo)頁(yè)面加載時(shí)我們的修改已經(jīng)生效。4.3 模擬人類行為模式即使指紋完美機(jī)械式的操作也會(huì)出賣你。我們需要讓腳本“像人一樣”。隨機(jī)延遲不要在操作間使用固定的time.sleep(2)。import random import time def human_delay(min_s1, max_s3): 生成一個(gè)隨機(jī)的延遲時(shí)間 time.sleep(random.uniform(min_s, max_s)) # 使用示例 driver.find_element(...).click() human_delay(1, 2) # 等待1到2秒之間的一個(gè)隨機(jī)時(shí)間 driver.find_element(...).send_keys(text)模擬鼠標(biāo)移動(dòng)使用ActionChains進(jìn)行非直線的移動(dòng)。from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By element driver.find_element(By.ID, some-button) actions ActionChains(driver) # 將鼠標(biāo)先移動(dòng)到頁(yè)面某個(gè)隨機(jī)位置再移動(dòng)到目標(biāo)元素 actions.move_by_offset(random.randint(-50, 50), random.randint(-50, 50)).pause(0.5) actions.move_to_element(element).pause(0.2) # 在元素上稍作停頓 actions.click() actions.perform()隨機(jī)滾動(dòng)不要一次性滾動(dòng)到底。def human_like_scroll(driver, scroll_to_heightNone): total_height scroll_to_height or driver.execute_script(return document.body.scrollHeight) current_position 0 scroll_step random.randint(200, 500) # 每次滾動(dòng)200-500像素 while current_position total_height: # 隨機(jī)決定這次滾動(dòng)多少 this_step min(scroll_step, total_height - current_position) this_step random.randint(int(this_step*0.8), this_step) # 加入隨機(jī)性 driver.execute_script(fwindow.scrollBy(0, {this_step});) current_position this_step human_delay(0.5, 1.5) # 滾動(dòng)后隨機(jī)停頓5. 進(jìn)階技巧與疑難排坑在實(shí)際項(xiàng)目中你會(huì)遇到各種各樣奇怪的問(wèn)題。下面是我踩過(guò)坑后總結(jié)的“排雷手冊(cè)”。5.1 如何檢測(cè)自己的隱身效果你不能盲目相信代碼必須驗(yàn)證。有以下幾種方法使用檢測(cè)網(wǎng)站訪問(wèn)像pixelscan.net、browserleaks.com這樣的瀏覽器指紋檢測(cè)站。用你的隱身Driver打開(kāi)它們查看檢測(cè)報(bào)告。重點(diǎn)關(guān)注“WebDriver”、“自動(dòng)化”等項(xiàng)目的檢測(cè)結(jié)果。本地JS檢測(cè)在頁(yè)面中執(zhí)行一些檢測(cè)腳本。driver.get(about:blank) test_js return { webdriver: navigator.webdriver, plugins: navigator.plugins.length, languages: navigator.languages, chrome: typeof window.chrome, } result driver.execute_script(test_js) print(檢測(cè)結(jié)果:, result) # 理想結(jié)果webdriver應(yīng)為undefined或falseplugins0chrome為object但可能沒(méi)有runtime5.2 常見(jiàn)問(wèn)題與解決方案問(wèn)題現(xiàn)象可能原因解決方案navigator.webdriver仍為trueCDP腳本注入失敗或順序不對(duì)確保在driver.get()前執(zhí)行execute_cdp_cmd。檢查Chrome版本與CDP兼容性。網(wǎng)站提示“請(qǐng)使用現(xiàn)代瀏覽器”User-Agent或HTTP頭信息不完整/錯(cuò)誤使用更真實(shí)的UA字符串并通過(guò)CDP或selenium-wire等庫(kù)設(shè)置完整的請(qǐng)求頭如Accept,Sec-CH-UA。僅在無(wú)頭模式下被檢測(cè)到無(wú)頭模式有額外特征嘗試使用--headlessnew。如果不行考慮使用“有頭但最小化”模式chrome_options.add_argument(--window-size1920,1080)并配合虛擬幀緩沖xvfb在服務(wù)器運(yùn)行。操作太快被封IP行為模式異常與指紋無(wú)關(guān)嚴(yán)格遵守“人類行為模擬”增加隨機(jī)延遲和錯(cuò)誤操作如偶爾點(diǎn)擊錯(cuò)位置再糾正。使用代理IP池輪換請(qǐng)求。部分iframe內(nèi)內(nèi)容仍檢測(cè)到自動(dòng)化iframe是獨(dú)立上下文我們的JS未注入在切換到iframe后重新在該iframe的上下文中執(zhí)行CDP命令注入JS。5.3 關(guān)于“免費(fèi)”與可持續(xù)性標(biāo)題中提到“親測(cè)免費(fèi)”這里的免費(fèi)指的是Selenium Stealth技術(shù)本身是開(kāi)源的無(wú)論是理念還是selenium-stealth庫(kù)都無(wú)需付費(fèi)。核心解決方案CDP是瀏覽器原生能力不需要購(gòu)買任何第三方服務(wù)。但是請(qǐng)注意隱身是一場(chǎng)持續(xù)的“軍備競(jìng)賽”。今天有效的方法明天可能就失效了。因此最寶貴的“免費(fèi)”資源是你的調(diào)試能力和知識(shí)儲(chǔ)備。你需要保持更新關(guān)注Chrome/WebDriver的版本更新它們可能會(huì)引入或移除某些特征。建立測(cè)試用例為你的目標(biāo)網(wǎng)站編寫簡(jiǎn)單的檢測(cè)腳本定期運(yùn)行確保隱身策略依然有效。理解原理而非死記代碼明白每個(gè)CDP命令和JS片段是在修改什么為什么這樣改。這樣當(dāng)出現(xiàn)新檢測(cè)點(diǎn)時(shí)你才能快速找到對(duì)策。6. 項(xiàng)目實(shí)戰(zhàn)一個(gè)完整的隱身數(shù)據(jù)采集流程示例假設(shè)我們需要從某個(gè)電商網(wǎng)站我們稱其為SiteX列表頁(yè)采集商品名稱和價(jià)格該網(wǎng)站有基礎(chǔ)的反爬。import random import time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.options import Options class StealthScraper: def __init__(self, use_headlessFalse): self.driver self._create_stealth_driver(use_headless) self._apply_stealth() def _create_stealth_driver(self, use_headless): options Options() if use_headless: options.add_argument(--headlessnew) # 使用新的無(wú)頭模式 # 基礎(chǔ)隱身與配置 options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) options.add_argument(user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...) service Service(ChromeDriverManager().install()) driver webdriver.Chrome(serviceservice, optionsoptions) driver.maximize_window() # 最大化窗口更符合用戶習(xí)慣 return driver def _apply_stealth(self): 應(yīng)用深度隱身JS js Object.defineProperty(navigator, webdriver, {get: () undefined}); // ... 此處包含上一節(jié)4.2中的所有JS代碼 ... self.driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, {source: js}) def _human_delay(self, min_t1, max_t3): time.sleep(random.uniform(min_t, max_t)) def scrape_sitex(self, url): print(f正在訪問(wèn): {url}) self.driver.get(url) self._human_delay(2, 4) # 初始頁(yè)面加載等待 data [] page_num 1 while True: print(f正在處理第 {page_num} 頁(yè)...) # 1. 模擬人類隨機(jī)滾動(dòng)閱讀 self._human_like_scroll() # 2. 等待商品元素加載 try: WebDriverWait(self.driver, 10).until( EC.presence_of_all_elements_located((By.CSS_SELECTOR, .product-item)) ) except: print(未找到商品元素可能頁(yè)面結(jié)構(gòu)已變或已被攔截。) break products self.driver.find_elements(By.CSS_SELECTOR, .product-item) for product in products: # 3. 模擬鼠標(biāo)懸停隨機(jī)選擇部分商品 if random.random() 0.7: # 30%的概率執(zhí)行懸停 actions ActionChains(self.driver) actions.move_to_element(product).pause(random.uniform(0.5, 1.5)).perform() # 4. 提取數(shù)據(jù) try: name_elem product.find_element(By.CSS_SELECTOR, .product-name) price_elem product.find_element(By.CSS_SELECTOR, .product-price) # 模擬人類閱讀速度逐個(gè)元素查看 self._human_delay(0.1, 0.3) name name_elem.text.strip() price price_elem.text.strip() if name and price: data.append({name: name, price: price}) except: continue # 某個(gè)元素提取失敗跳過(guò)該商品 # 5. 尋找下一頁(yè)按鈕并模擬人類點(diǎn)擊 try: next_btn self.driver.find_element(By.CSS_SELECTOR, .next-page) # 點(diǎn)擊前隨機(jī)延遲 self._human_delay(1, 2) # 滾動(dòng)到按鈕位置 self.driver.execute_script(arguments[0].scrollIntoView({behavior: smooth, block: center});, next_btn) self._human_delay(0.5, 1) next_btn.click() page_num 1 self._human_delay(2, 4) # 等待新頁(yè)面加載 except: print(已到達(dá)最后一頁(yè)或找不到下一頁(yè)按鈕。) break return data def _human_like_scroll(self): 模擬人類滾動(dòng) scroll_pause_time random.uniform(0.5, 1.5) scroll_step random.randint(300, 700) current_scroll 0 max_scroll self.driver.execute_script(return document.body.scrollHeight) - self.driver.execute_script(return window.innerHeight) while current_scroll max_scroll: # 每次滾動(dòng)距離隨機(jī) this_step min(scroll_step, max_scroll - current_scroll) this_step random.randint(int(this_step*0.7), this_step) self.driver.execute_script(fwindow.scrollBy(0, {this_step});) current_scroll this_step time.sleep(scroll_pause_time) # 隨機(jī)決定是否進(jìn)行一小段回滾模仿人類猶豫 if random.random() 0.9 and current_scroll 100: rollback random.randint(-50, -10) self.driver.execute_script(fwindow.scrollBy(0, {rollback});) current_scroll rollback time.sleep(random.uniform(0.3, 0.8)) def close(self): self.driver.quit() # 使用示例 if __name__ __main__: scraper StealthScraper(use_headlessFalse) # 高風(fēng)控站建議先用有頭模式調(diào)試 try: data scraper.scrape_sitex(https://www.example-sitex.com/products) print(f共采集到 {len(data)} 條商品數(shù)據(jù)。) for item in data[:5]: # 打印前5條看看 print(item) finally: scraper.close()這個(gè)示例融合了之前所有的知識(shí)點(diǎn)深度指紋修改、人類行為模擬、健壯的錯(cuò)誤處理。它不是一個(gè)萬(wàn)能腳本但為你提供了一個(gè)高度可定制和可調(diào)試的框架。7. 總結(jié)與個(gè)人心得讓Selenium“隱形”不是調(diào)用一個(gè)魔法函數(shù)就能完成的它是一個(gè)系統(tǒng)工程涉及瀏覽器指紋、網(wǎng)絡(luò)協(xié)議和行為模式三個(gè)層面的對(duì)抗。我的經(jīng)驗(yàn)是分步實(shí)施逐步加強(qiáng)不要一開(kāi)始就上最復(fù)雜的方案。先用基礎(chǔ)配置和selenium-stealth測(cè)試如果被識(shí)別再逐步添加CDP深度修改和行為模擬。用檢測(cè)網(wǎng)站作為你的“試金石”。理解高于復(fù)制網(wǎng)上有很多代碼片段但如果不明白Object.defineProperty在做什么不明白CDP命令的執(zhí)行時(shí)機(jī)當(dāng)腳本失效時(shí)你將束手無(wú)策。花時(shí)間閱讀MDN上關(guān)于Web API的文檔理解navigator、screen、window這些對(duì)象。環(huán)境隔離與代理管理對(duì)于大規(guī)模任務(wù)一定要使用干凈的瀏覽器環(huán)境可以考慮每次啟動(dòng)新用戶目錄和高質(zhì)量的代理IP。一個(gè)被標(biāo)記的IP地址會(huì)讓任何隱身技術(shù)失效。保持敬畏合法使用技術(shù)是把雙刃劍。這套方法旨在幫助測(cè)試工程師更好地完成自動(dòng)化測(cè)試工作或研究人員在遵守robots.txt和網(wǎng)站條款的前提下進(jìn)行合規(guī)的數(shù)據(jù)分析。請(qǐng)勿將其用于惡意爬取、攻擊或干擾網(wǎng)站正常運(yùn)行。最后再分享一個(gè)調(diào)試小技巧在開(kāi)發(fā)隱身腳本時(shí)我通常會(huì)打開(kāi)Chrome的開(kāi)發(fā)者工具options.add_argument(--auto-open-devtools-for-tabs)在Console里實(shí)時(shí)查看navigator.webdriver等屬性的值這比任何外部檢測(cè)都更直接。隱身是一場(chǎng)貓鼠游戲而你的耐心和對(duì)細(xì)節(jié)的把握是贏得這場(chǎng)游戲的關(guān)鍵。