技能實(shí)戰(zhàn):從反爬識(shí)別到合規(guī)數(shù)據(jù)采集)
做數(shù)據(jù)采集的人幾乎都逃不過(guò)“反爬”這兩個(gè)字。就在上個(gè)月我給自己那個(gè)只會(huì)聊天的AI助手——代號(hào)“小龍蝦”——加了一套爬蟲(chóng)技能讓它能自己看網(wǎng)頁(yè)、提取數(shù)據(jù)、清洗入庫(kù)。整個(gè)過(guò)程踩了不少坑但也整理出了一套相對(duì)完整的思路。這篇內(nèi)容我會(huì)把這套方案的架構(gòu)、工具選擇和實(shí)戰(zhàn)代碼都拆開(kāi)講清楚尤其會(huì)聊一聊反爬機(jī)制到底在防什么以及哪些“突破”手段是合規(guī)的、哪些絕對(duì)不能碰。如果你也在做數(shù)據(jù)采集或者想給自己的AI應(yīng)用加上聯(lián)網(wǎng)抓取能力這篇文章應(yīng)該能幫你避開(kāi)我踩過(guò)的那些坑。1. 項(xiàng)目全景為什么給AI裝爬蟲(chóng)技能1.1 “小龍蝦”的初始能力邊界我一開(kāi)始做“小龍蝦”這個(gè)項(xiàng)目其實(shí)只是想讓它能回答一些需要實(shí)時(shí)數(shù)據(jù)的問(wèn)題。比如用戶(hù)問(wèn)“某款產(chǎn)品最近的價(jià)格趨勢(shì)”“某個(gè)論壇的熱帖標(biāo)題”它不能憑空編需要有真實(shí)數(shù)據(jù)來(lái)源。原本的方案很簡(jiǎn)單人工寫(xiě)幾個(gè)腳本定時(shí)抓數(shù)據(jù)喂給它。但問(wèn)題很快暴露出來(lái)——腳本只針對(duì)固定的頁(yè)面結(jié)構(gòu)一旦頁(yè)面改版選擇器全部失效而且碰到有反爬策略的站點(diǎn)腳本跑不了幾次就會(huì)被限制訪(fǎng)問(wèn)。后來(lái)我換了個(gè)思路既然“小龍蝦”是AI為什么不讓它自己學(xué)會(huì)“看頁(yè)面”和“提取信息”于是我給這個(gè)AI助手設(shè)計(jì)了一套爬蟲(chóng)技能層。它不再是單純的聊天模型而是可以調(diào)用工具、分析頁(yè)面結(jié)構(gòu)、動(dòng)態(tài)調(diào)整抓取策略的“數(shù)據(jù)小助手”。這個(gè)改造聽(tīng)起來(lái)簡(jiǎn)單真正做起來(lái)卻涉及三個(gè)層面的問(wèn)題反爬機(jī)制識(shí)別、頁(yè)面解析策略、數(shù)據(jù)清洗流程。每一個(gè)層面都有不少隱藏的坑。1.2 反爬機(jī)制真正想攔住的是誰(shuí)先說(shuō)反爬。很多人一聽(tīng)到“反爬”就覺(jué)得是貓鼠游戲非要繞過(guò)對(duì)方的防護(hù)才算贏。但實(shí)際動(dòng)手后你會(huì)發(fā)現(xiàn)大部分反爬機(jī)制的目標(biāo)不是阻止所有爬蟲(chóng)而是阻止“不守規(guī)矩”的爬蟲(chóng)。常見(jiàn)的幾類(lèi)反爬手段本質(zhì)都在做同一件事判斷訪(fǎng)問(wèn)者是不是真人以及訪(fǎng)問(wèn)行為是否合理。User-Agent校驗(yàn)服務(wù)器檢查HTTP請(qǐng)求頭里的UA字段非瀏覽器UA直接拒絕。訪(fǎng)問(wèn)頻率限制同一IP在短時(shí)間內(nèi)請(qǐng)求次數(shù)過(guò)多觸發(fā)限流甚至封禁。動(dòng)態(tài)渲染數(shù)據(jù)由JavaScript在瀏覽器里異步生成直接請(qǐng)求HTML拿不到內(nèi)容。行為校驗(yàn)檢測(cè)鼠標(biāo)軌跡、點(diǎn)擊間隔、頁(yè)面停留時(shí)間等判斷是否像真人。驗(yàn)證碼當(dāng)風(fēng)控系統(tǒng)判斷可疑時(shí)彈出驗(yàn)證碼要求人機(jī)驗(yàn)證。我做的第一版爬蟲(chóng)一上來(lái)就模擬請(qǐng)求去抓一個(gè)排行榜頁(yè)面結(jié)果連返回的響應(yīng)都變成了“403 Forbidden”。后來(lái)我仔細(xì)看了對(duì)方返回的HTML發(fā)現(xiàn)里面有一行提示大概意思是“請(qǐng)求頻率超限請(qǐng)稍后再試”。這才意識(shí)到反爬不是要把所有爬蟲(chóng)趕盡殺絕而是在逼你用更尊重站點(diǎn)資源的方式去抓數(shù)據(jù)。合規(guī)的應(yīng)對(duì)策略其實(shí)也簡(jiǎn)單模擬真實(shí)瀏覽器的基本請(qǐng)求頭、控制抓取頻率、盡量用瀏覽器自動(dòng)化處理動(dòng)態(tài)渲染頁(yè)面遇到驗(yàn)證碼就停下來(lái)檢查自己的節(jié)奏。這套思路放進(jìn)去之后“小龍蝦”的抓取成功率立刻高了很多。1.3 方案選型為什么用“瀏覽器自動(dòng)化AI解析”而不是純請(qǐng)求最開(kāi)始我傾向于用純請(qǐng)求庫(kù)加正則表達(dá)式因?yàn)樾阅芎?、資源占用低。但實(shí)際遇到動(dòng)態(tài)渲染頁(yè)面后這個(gè)方案直接失效。我試著用接口逆向的方式去找頁(yè)面背后的數(shù)據(jù)接口確實(shí)能找到一部分但有些站點(diǎn)會(huì)對(duì)接口參數(shù)做簽名校驗(yàn)?zāi)嫦虺杀痉浅8叨乙坏?duì)方更新簽名算法又要重新分析維護(hù)成本完全不可控。后來(lái)我轉(zhuǎn)向?yàn)g覽器自動(dòng)化方案。讓“小龍蝦”像人一樣打開(kāi)一個(gè)無(wú)頭瀏覽器等頁(yè)面加載完再讀取渲染后的DOM。這個(gè)方案的優(yōu)點(diǎn)是不太依賴(lài)頁(yè)面的具體實(shí)現(xiàn)方式不管是Vue還是React最后渲染出來(lái)的都是標(biāo)準(zhǔn)HTMLAI可以直接從中提取信息。缺點(diǎn)是速度和資源消耗都遠(yuǎn)大于純請(qǐng)求所以它更適合中小規(guī)模的定向采集不適合大規(guī)模爬取。配合AI解析之后方案變得更靈活了。我不需要預(yù)先寫(xiě)死提取規(guī)則而是把渲染后的HTML片段丟給大語(yǔ)言模型讓它分析出目標(biāo)數(shù)據(jù)的CSS選擇器或者XPath。這樣即使頁(yè)面改版只要把新的HTML片段再給模型看一遍規(guī)則就會(huì)自動(dòng)更新不需要人肉維護(hù)一堆正則表達(dá)式。2. 技能架構(gòu)與工具選型2.1 整體架構(gòu)請(qǐng)求層、解析層、AI決策層整個(gè)“小龍蝦”的爬蟲(chóng)技能我拆成了三層。第一層是請(qǐng)求層負(fù)責(zé)獲取頁(yè)面內(nèi)容。這個(gè)層級(jí)要考慮請(qǐng)求頭、超時(shí)時(shí)間、重試機(jī)制、頻率控制。對(duì)于靜態(tài)頁(yè)面用HTTP請(qǐng)求庫(kù)就夠了對(duì)于動(dòng)態(tài)頁(yè)面則要啟動(dòng)瀏覽器自動(dòng)化框架。第二層是解析層負(fù)責(zé)從HTML里提取結(jié)構(gòu)化數(shù)據(jù)。以前我習(xí)慣直接用XPath或CSS選擇器但選擇器寫(xiě)起來(lái)麻煩頁(yè)面一改就崩?,F(xiàn)在我會(huì)把頁(yè)面文本和結(jié)構(gòu)交給AI讓它生成提取規(guī)則然后我用一個(gè)通用的提取器去執(zhí)行這些規(guī)則。第三層是AI決策層也是“小龍蝦”的靈魂。這一層接收用戶(hù)的自然語(yǔ)言指令比如“抓取這個(gè)頁(yè)面里的商品名稱(chēng)和價(jià)格”然后自動(dòng)判斷該用什么請(qǐng)求方式、是否需要渲染JS、提取完數(shù)據(jù)后怎么清洗、要不要保存到數(shù)據(jù)庫(kù)。AI決策層的輸出是一份可執(zhí)行的任務(wù)計(jì)劃而不是一段寫(xiě)死的代碼。這三層各司其職好處是任何一層出問(wèn)題都可以單獨(dú)替換。比如請(qǐng)求層發(fā)現(xiàn)某個(gè)站點(diǎn)需要登錄才能看數(shù)據(jù)那就不用硬闖直接在AI決策層判斷“這個(gè)數(shù)據(jù)源不適合自動(dòng)化抓取”轉(zhuǎn)而搜索公開(kāi)API。2.2 工具選型每個(gè)環(huán)節(jié)的合適選擇工具選型我做了不少對(duì)比重點(diǎn)考慮三個(gè)維度社區(qū)活躍度、文檔質(zhì)量、和AI模型的配合難度。請(qǐng)求層我用了兩個(gè)方案一個(gè)是老牌的requests庫(kù)適合簡(jiǎn)單頁(yè)面另一個(gè)是httpx支持異步并發(fā)性能更好。requests的優(yōu)點(diǎn)是生態(tài)成熟、代碼簡(jiǎn)單但遇到連接復(fù)用和異步請(qǐng)求時(shí)有點(diǎn)力不從心。httpx的API和requests很像但原生支持異步我用它來(lái)處理需要同時(shí)抓取多個(gè)頁(yè)面的場(chǎng)景。解析層我選過(guò)BeautifulSoup、lxml和parsel。BeautifulSoup最直觀(guān)適合新手但性能一般。lxml速度最快XPath支持完善可語(yǔ)法太硬。parsel是Scrapy提煉出來(lái)的解析庫(kù)封裝得干凈既有CSS選擇器也支持XPath內(nèi)存占用也可控。我最后留了parsel因?yàn)樗虯I生成的選擇器兼容性最好。瀏覽器自動(dòng)化工具我在Selenium和Playwright之間猶豫了很久。Selenium名氣大、資料多但安裝驅(qū)動(dòng)麻煩運(yùn)行也不夠穩(wěn)定。Playwright的API設(shè)計(jì)更現(xiàn)代自帶瀏覽器下載和管理還可以通過(guò)CDP協(xié)議和AI模型交互所以最終選了Playwright。對(duì)于“小龍蝦”來(lái)說(shuō)它只需要加載頁(yè)面、等待關(guān)鍵元素出現(xiàn)、導(dǎo)出HTMLPlaywright用起來(lái)最省心。2.3 給“小龍蝦”的AI能力接口AI決策層我設(shè)計(jì)了一套簡(jiǎn)易的函數(shù)調(diào)用機(jī)制?!靶↓埼r”本身是一個(gè)大語(yǔ)言模型接口但我給它注冊(cè)了幾個(gè)工具函數(shù)包括“fetch_page”“render_page”“extract_data”“save_dataset”。模型根據(jù)用戶(hù)的問(wèn)題生成JSON格式的工具調(diào)用指令系統(tǒng)去執(zhí)行并把結(jié)果返回給模型模型再組織成回答。這套機(jī)制的核心是讓AI學(xué)會(huì)“看情況辦事”。比如用戶(hù)說(shuō)“幫我看看這個(gè)頁(yè)面里有哪些新聞標(biāo)題”模型會(huì)先選擇“render_page”而不是“fetch_page”因?yàn)樗佬侣務(wù)军c(diǎn)的內(nèi)容很可能由JS渲染。如果用戶(hù)要求“每5分鐘抓一次”模型會(huì)自動(dòng)在請(qǐng)求層插入隨機(jī)延時(shí)降低被封風(fēng)險(xiǎn)。工具接口設(shè)計(jì)有個(gè)很關(guān)鍵的細(xì)節(jié)要給模型提供足夠清晰的參數(shù)說(shuō)明。比如“fetch_page”需要傳入url、headers、timeout“extract_data”需要傳入html_snippet和schema。模型看到這些說(shuō)明后才知道怎么調(diào)用。沒(méi)有詳細(xì)的工具說(shuō)明AI再?gòu)?qiáng)也會(huì)瞎猜。3. 實(shí)戰(zhàn)三類(lèi)典型反爬場(chǎng)景的合規(guī)應(yīng)對(duì)3.1 場(chǎng)景一UA校驗(yàn)與基礎(chǔ)請(qǐng)求頭先看最簡(jiǎn)單的場(chǎng)景。很多網(wǎng)站老舊的防護(hù)機(jī)制就是檢查User-Agent如果是Python默認(rèn)的UA直接拒絕。我給“小龍蝦”設(shè)計(jì)請(qǐng)求層時(shí)把常用瀏覽器的UA維護(hù)成一份配置發(fā)起請(qǐng)求前隨機(jī)取一個(gè)并補(bǔ)上常見(jiàn)的Accept、Accept-Language、Connection頭。下面是一個(gè)簡(jiǎn)化版的請(qǐng)求函數(shù)用requests實(shí)現(xiàn)import requests import random BROWSER_UAS [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15, ] def fetch_page(url): headers { User-Agent: random.choice(BROWSER_UAS), Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Connection: keep-alive, } resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() return resp.text這樣改完后原本被UA校驗(yàn)擋掉的頁(yè)面大多能正常返回。但我提醒一句這只是最基本的一步現(xiàn)在很多站點(diǎn)的反爬是組合拳UA校驗(yàn)往往和頻率限制一起出現(xiàn)。實(shí)操心得不要偽造一個(gè)根本不存在的UA。有的開(kāi)發(fā)者隨手寫(xiě)了“Googlebot”的UA去抓頁(yè)面這樣做既不符合規(guī)范也容易觸發(fā)更嚴(yán)格的反爬。更穩(wěn)妥的做法是模擬普通用戶(hù)的真實(shí)環(huán)境比如Windows Chrome或者macOS Safari的UA。3.2 場(chǎng)景二動(dòng)態(tài)渲染頁(yè)面拿不到數(shù)據(jù)第二個(gè)場(chǎng)景非常常見(jiàn)。用requests請(qǐng)求某論壇的帖子列表返回的HTML里只有空殼數(shù)據(jù)全是通過(guò)JavaScript異步加載的。這時(shí)候就需要用Playwright渲染完整頁(yè)面再提取。我給“小龍蝦”封裝了一個(gè)“render_page”工具代碼如下from playwright.sync_api import sync_playwright def render_page(url, wait_fornetworkidle, timeout30000): with sync_playwright() as p: browser p.chromium.launch(headlessTrue) context browser.new_context( 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 ), viewport{width: 1280, height: 800}, localezh-CN, ) page context.new_page() page.goto(url, wait_untilwait_for, timeouttimeout) page.wait_for_timeout(2000) html page.content() browser.close() return html我知道很多人在這一步喜歡加“隨機(jī)休眠”讓頁(yè)面更像真人操作。但更重要的其實(shí)是等待策略。Wait_until設(shè)置為“networkidle”可以等網(wǎng)絡(luò)空閑后再返回如果數(shù)據(jù)加載特別慢還可以顯式等待某個(gè)選擇器出現(xiàn)比如“page.wait_for_selector(.list-item)”。這個(gè)場(chǎng)景的難點(diǎn)不是代碼而是判斷一個(gè)頁(yè)面到底需不需要渲染。我的經(jīng)驗(yàn)是先用普通請(qǐng)求抓一遍如果HTML長(zhǎng)度正常但目標(biāo)字段是空的或者頁(yè)面結(jié)構(gòu)里有明顯的script標(biāo)簽包含大量數(shù)據(jù)再切換成渲染模式?!靶↓埼r”的AI決策層會(huì)先做一次預(yù)判減少不必要的瀏覽器啟動(dòng)。3.3 場(chǎng)景三訪(fǎng)問(wèn)頻率限制與風(fēng)控提示第三個(gè)場(chǎng)景是高頻訪(fǎng)問(wèn)觸發(fā)限流。我最早測(cè)試某個(gè)商品比價(jià)站時(shí)用單線(xiàn)程連續(xù)抓了100個(gè)頁(yè)面結(jié)果第80個(gè)請(qǐng)求開(kāi)始頁(yè)面返回里出現(xiàn)了一段“請(qǐng)完成安全驗(yàn)證”的提示。這說(shuō)明觸發(fā)風(fēng)控了。合規(guī)的應(yīng)對(duì)措施不是去破解驗(yàn)證碼而是讓AI自動(dòng)降速并暫停。我在請(qǐng)求層加了一個(gè)“抓取狀態(tài)檢查器”專(zhuān)門(mén)分析返回內(nèi)容里是否有驗(yàn)證碼、滑塊、異常跳轉(zhuǎn)等關(guān)鍵詞。一旦發(fā)現(xiàn)異常立刻停止當(dāng)前任務(wù)并進(jìn)入指數(shù)退避模式。import time import random def smart_request(url, session, max_retries3): for attempt in range(max_retries): resp session.get(url, timeout10) content resp.text if 安全驗(yàn)證 in content or 滑塊 in content: wait_time (2 ** attempt) random.uniform(0, 1) time.sleep(wait_time) continue if resp.status_code 200: return content raise RuntimeError(請(qǐng)求被風(fēng)控?cái)r截已自動(dòng)停止)指數(shù)退避的意思是每次重試的等待時(shí)間翻倍第一次等2秒第二次等4秒第三次等8秒。這樣既不會(huì)立刻激怒風(fēng)控系統(tǒng)也給對(duì)方留出恢復(fù)時(shí)間。如果重試三次仍然被攔就直接放棄這個(gè)數(shù)據(jù)源不硬剛。實(shí)操心得在“小龍蝦”里我還會(huì)讓AI記錄每個(gè)站點(diǎn)的“健康分”。抓取成功一次加一分觸發(fā)風(fēng)控一次扣十分。分?jǐn)?shù)低于閾值后AI會(huì)主動(dòng)向用戶(hù)報(bào)告“該站點(diǎn)暫時(shí)不適合抓取建議改用官方API”。這個(gè)機(jī)制讓我避免了很多無(wú)謂的封禁。4. 反爬對(duì)抗的邊界技術(shù)可行不等于可以亂來(lái)4.1 Robots協(xié)議與法律底線(xiàn)標(biāo)題里寫(xiě)了“突破反爬”但這個(gè)“突破”必須是合規(guī)前提下的技術(shù)突破而不是鉆法律空子。每個(gè)正規(guī)站點(diǎn)都會(huì)在robots.txt里聲明哪些路徑允許抓取、哪些不允許。開(kāi)發(fā)者在寫(xiě)爬蟲(chóng)之前第一件事應(yīng)該是查看目標(biāo)站點(diǎn)的robots.txt。我遇到過(guò)有開(kāi)發(fā)者直接抓取某平臺(tái)需要登錄才能看到的數(shù)據(jù)還沾沾自喜地覺(jué)得“繞過(guò)了權(quán)限校驗(yàn)”。這種做法非常危險(xiǎn)已經(jīng)不只是反爬問(wèn)題而是越權(quán)訪(fǎng)問(wèn)。即使技術(shù)上能做到一旦被追責(zé)個(gè)人很難承擔(dān)后果。不要試圖繞過(guò)驗(yàn)證碼、不要破解登錄態(tài)、不要對(duì)非公開(kāi)數(shù)據(jù)強(qiáng)行抓取。這是我在團(tuán)隊(duì)里反復(fù)強(qiáng)調(diào)的三條紅線(xiàn)。“小龍蝦”的AI決策層里也內(nèi)置了這條規(guī)則當(dāng)頁(yè)面出現(xiàn)登錄墻或驗(yàn)證碼時(shí)默認(rèn)判定為“不可抓”并提示用戶(hù)尋找授權(quán)數(shù)據(jù)源。4.2 優(yōu)先用官方API和公開(kāi)數(shù)據(jù)集很多時(shí)候我們想抓的數(shù)據(jù)其實(shí)對(duì)方已經(jīng)提供了更穩(wěn)定、更合法的獲取途徑。比如很多社交平臺(tái)、天氣服務(wù)、地圖服務(wù)都有官方API雖然可能限頻或者收費(fèi)但數(shù)據(jù)質(zhì)量和穩(wěn)定性遠(yuǎn)高于爬蟲(chóng)。我在設(shè)計(jì)“小龍蝦”時(shí)把“尋找官方API”作為一個(gè)優(yōu)先動(dòng)作。AI會(huì)先根據(jù)用戶(hù)描述判斷數(shù)據(jù)所屬領(lǐng)域然后自動(dòng)搜索有沒(méi)有官方接口。只有確認(rèn)沒(méi)有API或者API權(quán)限不能滿(mǎn)足需求時(shí)才考慮頁(yè)面抓取。這樣做還有一個(gè)額外好處維護(hù)成本低。頁(yè)面結(jié)構(gòu)隨時(shí)會(huì)變但API一般相對(duì)穩(wěn)定。如果你做的是長(zhǎng)期數(shù)據(jù)采集項(xiàng)目用API幾乎是唯一的可持續(xù)方案。4.3 把“合規(guī)判斷”寫(xiě)進(jìn)AI腳本既然AI已經(jīng)參與到爬蟲(chóng)決策里那合規(guī)判斷也應(yīng)該交給它。我做了一個(gè)簡(jiǎn)單的規(guī)則引擎放在AI決策層旁邊。規(guī)則包括檢查robots.txt、檢查頁(yè)面是否需要登錄、檢查抓取頻率是否合理、檢查數(shù)據(jù)是否涉及個(gè)人隱私。這套規(guī)則引擎會(huì)在每次抓取任務(wù)開(kāi)始前執(zhí)行只要有一項(xiàng)不符合就中止任務(wù)并向用戶(hù)說(shuō)明原因。比如用戶(hù)要求抓取某論壇的用戶(hù)私人主頁(yè)AI會(huì)直接拒絕因?yàn)槟菍儆趥€(gè)人隱私數(shù)據(jù)。“小龍蝦”不再只是一個(gè)能抓數(shù)據(jù)的工具它更像一個(gè)有邊界意識(shí)的數(shù)據(jù)助手。偷懶的辦法是只把規(guī)則寫(xiě)進(jìn)系統(tǒng)提示詞里讓大模型“自覺(jué)”。但我個(gè)人建議做成獨(dú)立的規(guī)則模塊因?yàn)樘崾驹~可以被用戶(hù)改寫(xiě)規(guī)則卻始終獨(dú)立存在能夠更可靠地守住底線(xiàn)。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 AI生成的選擇器為什么老失效我在實(shí)際使用中遇到最頻繁的問(wèn)題就是AI生成的CSS選擇器明明在某一時(shí)刻有效過(guò)幾天再運(yùn)行就失效了。原因很簡(jiǎn)單頁(yè)面結(jié)構(gòu)變了。排查時(shí)我先登錄瀏覽器開(kāi)發(fā)者工具手動(dòng)查看目標(biāo)元素的當(dāng)前class和id。然后對(duì)比“小龍蝦”上次生成的選擇器看看差異在哪。通常有兩種情況一種是class名帶上了隨機(jī)后綴比如“l(fā)ist-item-2398”變成“l(fā)ist-item-7812”另一種是整塊DOM結(jié)構(gòu)移動(dòng)了層級(jí)。解決辦法是讓AI生成“語(yǔ)義更穩(wěn)”的選擇器。不要用純class名而是優(yōu)先用文本內(nèi)容或者結(jié)構(gòu)化屬性。比如// 不穩(wěn).list-item-2398 // 更穩(wěn)[data-typeproduct] .title另外我還會(huì)在提取規(guī)則里加一個(gè)“自檢步驟”。抓取結(jié)果為空時(shí)AI會(huì)自動(dòng)對(duì)比頁(yè)面里是否有相似結(jié)構(gòu)并嘗試重新生成選擇器。這樣即使頁(yè)面改版系統(tǒng)也能在無(wú)人干預(yù)的情況下自我修復(fù)。5.2 抓下來(lái)的數(shù)據(jù)亂碼或字段錯(cuò)位亂碼問(wèn)題多出在編碼識(shí)別上。很多網(wǎng)頁(yè)沒(méi)有聲明charset或者聲明與實(shí)際編碼不一致。我用requests時(shí)直接使用resp.text可能會(huì)得到亂碼因?yàn)閞equests會(huì)猜編碼。我的習(xí)慣是用resp.content拿到原始字節(jié)再通過(guò)代碼或頁(yè)面meta標(biāo)簽判斷編碼然后手動(dòng)解碼。import chardet def decode_response(resp): raw resp.content detected chardet.detect(raw[:5000]) encoding detected.get(encoding, utf-8) return raw.decode(encoding, errorsreplace)字段錯(cuò)位則通常是選擇器匹配到了錯(cuò)誤元素。比如想抓價(jià)格結(jié)果把“原價(jià)”和“折扣價(jià)”混在一起。這個(gè)時(shí)候不要只給AI一段HTML而是把目標(biāo)字段的上下文也喂給它讓它理解哪些是有效數(shù)據(jù)。5.3 請(qǐng)求被風(fēng)控后的恢復(fù)步驟一旦確認(rèn)被風(fēng)控很多人會(huì)焦慮地不停重試結(jié)果越試越黑。正確的恢復(fù)步驟是先完全停止任務(wù)讓IP“冷靜”一段時(shí)間然后檢查抓取日志確認(rèn)觸發(fā)風(fēng)控的原因。在我的項(xiàng)目里“小龍蝦”被風(fēng)控時(shí)會(huì)自動(dòng)切斷該站點(diǎn)任務(wù)轉(zhuǎn)去抓其他數(shù)據(jù)源然后等待至少30分鐘再試探性請(qǐng)求一次。如果試探請(qǐng)求返回正常才恢復(fù)抓取但會(huì)把原來(lái)的并發(fā)數(shù)減半抓取頻率也降到原來(lái)的一半。如果你同時(shí)抓取了多個(gè)站點(diǎn)建議給不同站點(diǎn)設(shè)置獨(dú)立的限速配置。有的站點(diǎn)比較寬容每秒10次都沒(méi)事有的站點(diǎn)比較敏感每秒2次都會(huì)被警告。把這些配置做成可調(diào)節(jié)參數(shù)比寫(xiě)死在代碼里要靈活得多。5.4 快速排查速查表下面這張表是我在實(shí)際排障過(guò)程中整理出來(lái)的按“現(xiàn)象-原因-解法”的順序羅列希望對(duì)你有幫助?,F(xiàn)象可能原因合規(guī)解法返回403UA被校驗(yàn)攔截設(shè)置真實(shí)瀏覽器UA補(bǔ)全請(qǐng)求頭頁(yè)面無(wú)數(shù)據(jù)動(dòng)態(tài)渲染未執(zhí)行換用瀏覽器自動(dòng)化工具渲染第N次請(qǐng)求被攔觸發(fā)頻率限制降低頻率啟用指數(shù)退避重試出現(xiàn)驗(yàn)證碼行為或頻率異常立即停止等待窗口期提取字段為空當(dāng)前選擇器失效讓AI重新分析HTML生成新規(guī)則中文亂碼編碼識(shí)別錯(cuò)誤使用chardet檢測(cè)編碼后手動(dòng)解碼登錄墻數(shù)據(jù)非公開(kāi)放棄抓取尋找API或授權(quán)最后再分享一個(gè)小技巧爬蟲(chóng)技能不是越大越好而是要能進(jìn)能退。你給AI加的能力越多它跑偏的概率也越大。我在“小龍蝦”上線(xiàn)第二周的時(shí)候發(fā)現(xiàn)它會(huì)自作主張去抓一些明顯不該碰的節(jié)點(diǎn)后來(lái)加上“合規(guī)規(guī)則引擎”才把問(wèn)題控制住。現(xiàn)在回頭看真正的“數(shù)據(jù)高手”不是能繞過(guò)多少限制而是知道該在哪兒停下。