構(gòu)解析到實(shí)戰(zhàn) UA 池與輪換技巧)
1. 從一個(gè)不起眼的字符串說(shuō)起為什么我們需要認(rèn)識(shí) User Agent如果你做過(guò)爬蟲(chóng)、寫過(guò)接口自動(dòng)化測(cè)試、或者只是單純?cè)跒g覽器里按過(guò) F12 看過(guò) Network 面板那你一定見(jiàn)過(guò)那一長(zhǎng)串看起來(lái)像亂碼的字符串——Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36。這就是User Agent簡(jiǎn)稱 UA。它本質(zhì)上就是客戶端在發(fā)起 HTTP 請(qǐng)求時(shí)主動(dòng)貼在腦門上的一張“自我介紹便簽”告訴服務(wù)端“我是誰(shuí)、我來(lái)自哪里、我大概能干什么”。很多人第一次接觸 UA 是因?yàn)橛龅搅恕巴粋€(gè)頁(yè)面瀏覽器打開(kāi)正常代碼請(qǐng)求卻返回一堆亂碼或者直接 403”。這背后的核心原因往往就是服務(wù)端根據(jù) UA 做了內(nèi)容協(xié)商或者訪問(wèn)控制。UA 能做的事情遠(yuǎn)比想象中多它決定了服務(wù)端返回桌面版還是移動(dòng)版頁(yè)面、決定是否觸發(fā)反爬策略、影響 CDN 緩存命中、甚至影響某些前端庫(kù)的兼容性分支??梢哉f(shuō)UA 是客戶端與服務(wù)端之間最廉價(jià)、最普遍、也最容易被忽視的“身份憑證”。這篇文章適合三類人看第一類是剛?cè)腴T爬蟲(chóng)和數(shù)據(jù)采集的朋友經(jīng)常被 UA 卡住卻不知道從哪下手第二類是做前端兼容和自動(dòng)化測(cè)試的工程師需要模擬不同設(shè)備環(huán)境第三類是對(duì) HTTP 協(xié)議細(xì)節(jié)感興趣、想把“知其所以然”補(bǔ)齊的開(kāi)發(fā)者。我會(huì)從 UA 的結(jié)構(gòu)講起把常見(jiàn) UA 拆開(kāi)揉碎再給出可直接復(fù)用的 UA 列表、構(gòu)造方法和實(shí)戰(zhàn)避坑經(jīng)驗(yàn)。讀完你至少能做到看到一串 UA 能立刻判斷出瀏覽器內(nèi)核、操作系統(tǒng)、設(shè)備類型并且知道在什么場(chǎng)景下該用哪一類 UA。2. User Agent 的結(jié)構(gòu)拆解一串字符里到底藏了多少信息2.1 UA 的歷史包袱與字段構(gòu)成UA 字符串之所以看起來(lái)又長(zhǎng)又亂是因?yàn)樗池?fù)了三十年的歷史包袱。最早的瀏覽器叫 Mosaic后來(lái) Netscape 的瀏覽器代號(hào)是 Mozilla為了兼容只認(rèn) Mozilla 的服務(wù)器后來(lái)的瀏覽器紛紛在 UA 開(kāi)頭加上Mozilla/5.0來(lái)“偽裝”自己。這個(gè)傳統(tǒng)一直延續(xù)到今天所以你會(huì)看到 Chrome、Edge、Safari 的 UA 全都以Mozilla/5.0開(kāi)頭這純粹是歷史遺留的兼容性妥協(xié)跟 Mozilla 本身已經(jīng)沒(méi)什么關(guān)系了。一個(gè)典型的現(xiàn)代 UA 大致由以下幾段組成用空格和括號(hào)分隔Mozilla/5.0歷史兼容前綴幾乎所有主流瀏覽器都保留。(操作系統(tǒng)標(biāo)識(shí); 加密等級(jí)標(biāo)識(shí); 瀏覽器語(yǔ)言)括號(hào)內(nèi)是平臺(tái)信息比如Windows NT 10.0; Win64; x64表示 64 位 Windows 10。AppleWebKit/537.36渲染引擎標(biāo)識(shí)WebKit 系內(nèi)核的通用標(biāo)記。(KHTML, like Gecko)又一處歷史兼容標(biāo)記表示“我像 Gecko 一樣渲染”。Chrome/120.0.0.0真正的瀏覽器名稱和版本號(hào)。Safari/537.36為了兼容 Safari 檢測(cè)而保留的尾巴。理解這個(gè)結(jié)構(gòu)之后你就能像讀身份證一樣讀 UA 了。比如看到iPhone; CPU iPhone OS 17_0就知道是 iOS 17 的 iPhone看到Android 13; Pixel 7就知道是安卓 13 的某款設(shè)備。這種“逐段解析”的能力是后面所有實(shí)戰(zhàn)操作的基礎(chǔ)。2.2 主流瀏覽器 UA 的差異對(duì)比不同瀏覽器廠商在 UA 里塞的東西并不一樣這直接影響了服務(wù)端的判斷邏輯。我整理了一張對(duì)比表把幾個(gè)主流桌面瀏覽器的 UA 關(guān)鍵字段列出來(lái)方便你快速對(duì)照瀏覽器內(nèi)核標(biāo)識(shí)典型版本字段特殊標(biāo)記ChromeAppleWebKit/537.36Chrome/120.0.0.0無(wú)EdgeAppleWebKit/537.36Edg/120.0.0.0注意是Edg不是EdgeFirefoxGecko/20100101Firefox/121.0不含 AppleWebKitSafariAppleWebKit/605.1.15Version/17.0含 Safari/605.1.15這里有個(gè)非常容易踩的坑Edge 的 UA 里寫的是Edg/而不是Edge/。早期 Edge 用的是Edge/后來(lái)為了和某些老檢測(cè)邏輯區(qū)分改成了Edg/。如果你寫正則去匹配 Edge用Edge會(huì)漏掉一大批新版本。同理Firefox 的 UA 里根本沒(méi)有AppleWebKit如果你用“是否包含 AppleWebKit”來(lái)判斷是不是現(xiàn)代瀏覽器Firefox 會(huì)被誤判。這些細(xì)節(jié)在寫兼容邏輯時(shí)都是實(shí)打?qū)嵉目印?.3 移動(dòng)端 UA 的識(shí)別要點(diǎn)移動(dòng)端 UA 比桌面端更復(fù)雜因?yàn)樵O(shè)備碎片化嚴(yán)重。但抓住幾個(gè)關(guān)鍵詞就能覆蓋絕大多數(shù)情況iPhone包含iPhone系統(tǒng)字段形如CPU iPhone OS 17_0 like Mac OS X。iPad包含iPad注意 iPadOS 13 之后默認(rèn)請(qǐng)求桌面版 UA需要額外處理。Android 手機(jī)包含Android和Mobile比如Android 13; Pixel 7 Build/...。Android 平板包含Android但通常不含Mobile這是區(qū)分手機(jī)和平板的關(guān)鍵。微信內(nèi)置瀏覽器包含MicroMessenger這個(gè)標(biāo)記在做微信生態(tài)適配時(shí)極其重要。提示判斷移動(dòng)端時(shí)不要只匹配Mobile一個(gè)詞。有些安卓平板 UA 里也會(huì)出現(xiàn)Mobile穩(wěn)妥做法是結(jié)合Android和屏幕尺寸信息綜合判斷。3. 常見(jiàn) User Agent 實(shí)戰(zhàn)清單可直接抄作業(yè)的 UA 庫(kù)3.1 桌面端高頻 UA 推薦在實(shí)際做數(shù)據(jù)采集時(shí)最省事的做法是維護(hù)一個(gè) UA 池每次請(qǐng)求隨機(jī)挑一個(gè)。下面這些是我實(shí)測(cè)下來(lái)比較穩(wěn)定、不容易被識(shí)別為異常的桌面 UA你可以直接拿去用DESKTOP_UA_POOL [ # Chrome on Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, # Chrome on macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, # Firefox on Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0, # Safari on macOS Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15, # Edge on Windows Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0, ]選這些 UA 的邏輯是它們都是當(dāng)前主流版本覆蓋面廣且版本號(hào)不會(huì)太舊。版本號(hào)太舊是常見(jiàn)失誤——你用一個(gè) Chrome 80 的 UA 去請(qǐng)求服務(wù)端一看就知道這是“老古董”很容易觸發(fā)風(fēng)控。所以 UA 池需要定期更新建議每季度檢查一次主流版本號(hào)。3.2 移動(dòng)端 UA 與設(shè)備模擬移動(dòng)端采集或者做響應(yīng)式測(cè)試時(shí)需要模擬具體設(shè)備。下面這組覆蓋了 iOS、Android、微信等高頻場(chǎng)景MOBILE_UA_POOL [ # iPhone Safari Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1, # Android Chrome Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36, # 微信內(nèi)置瀏覽器 Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.44, # Android 平板 Mozilla/5.0 (Linux; Android 13; SM-X700) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, ]這里要特別說(shuō)明微信 UA 的用法。微信內(nèi)置瀏覽器的 UA 里帶MicroMessenger很多做微信分享、H5 頁(yè)面適配的邏輯都依賴這個(gè)標(biāo)記。如果你要測(cè)試“在微信里打開(kāi)是什么效果”就必須把 UA 換成帶MicroMessenger的否則服務(wù)端會(huì)返回普通瀏覽器版本測(cè)出來(lái)的結(jié)果完全不對(duì)。3.3 特殊場(chǎng)景 UA搜索引擎與爬蟲(chóng)標(biāo)識(shí)除了瀏覽器還有一類 UA 是搜索引擎爬蟲(chóng)和自動(dòng)化工具的。這類 UA 的特點(diǎn)是“自報(bào)家門”比如GooglebotMozilla/5.0 (compatible; Googlebot/2.1; http://www.google.com/bot.html)BingbotMozilla/5.0 (compatible; bingbot/2.0; http://www.bing.com/bingbot.htm)通用爬蟲(chóng)python-requests/2.31.0、curl/8.4.0、Go-http-client/1.1這里有個(gè)關(guān)鍵認(rèn)知用python-requests這種默認(rèn) UA 去請(qǐng)求等于直接告訴服務(wù)端“我是腳本”。絕大多數(shù)有風(fēng)控的站點(diǎn)會(huì)第一時(shí)間攔截。所以做采集時(shí)第一件事就是把默認(rèn) UA 換掉。反過(guò)來(lái)如果你自己寫服務(wù)端想識(shí)別爬蟲(chóng)也可以從這些默認(rèn) UA 入手做基礎(chǔ)過(guò)濾。4. 動(dòng)手實(shí)操構(gòu)造、解析與輪換 UA 的完整流程4.1 用 Python 構(gòu)造和發(fā)送自定義 UA最基礎(chǔ)的用法是在請(qǐng)求頭里手動(dòng)指定 UA。以requests庫(kù)為例import requests 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, Accept-Language: zh-CN,zh;q0.9,en;q0.8, } resp requests.get(https://example.com, headersheaders, timeout10) print(resp.status_code)注意我同時(shí)加了Accept-Language。只改 UA 不改其他頭是很容易被識(shí)破的。因?yàn)橐粋€(gè)真實(shí)的 Chrome 請(qǐng)求除了 UA 還會(huì)帶Accept、Accept-Language、Accept-Encoding、Connection等一系列頭。如果你的請(qǐng)求只有 UA 像瀏覽器其他頭都是 requests 默認(rèn)值風(fēng)控系統(tǒng)一眼就能看出破綻。所以正確的做法是“成套偽裝”把常用請(qǐng)求頭一起補(bǔ)上。4.2 用正則解析 UA 提取關(guān)鍵信息有時(shí)候我們需要反過(guò)來(lái)從一串 UA 里提取瀏覽器、系統(tǒng)、設(shè)備信息。用正則是最直接的方式import re def parse_ua(ua: str) - dict: result {browser: Unknown, os: Unknown, device: Desktop} # 瀏覽器識(shí)別注意順序Edge 要在 Chrome 之前判斷 if Edg/ in ua: result[browser] Edge elif Firefox/ in ua: result[browser] Firefox elif Chrome/ in ua: result[browser] Chrome elif Safari/ in ua: result[browser] Safari # 系統(tǒng)識(shí)別 if Windows NT in ua: result[os] Windows elif Mac OS X in ua: result[os] macOS elif Android in ua: result[os] Android elif iPhone in ua or iPad in ua: result[os] iOS # 設(shè)備類型 if Mobile in ua or iPhone in ua: result[device] Mobile elif iPad in ua or (Android in ua and Mobile not in ua): result[device] Tablet return result ua Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0 print(parse_ua(ua)) # {browser: Edge, os: Windows, device: Desktop}這段代碼里最關(guān)鍵的是判斷順序。Edge 的 UA 里同時(shí)包含Chrome/和Edg/如果你先判斷 ChromeEdge 就會(huì)被誤判成 Chrome。同理很多國(guó)產(chǎn)瀏覽器 UA 里也帶Chrome/需要根據(jù)額外標(biāo)記區(qū)分。這個(gè)“先特殊后一般”的判斷順序是寫 UA 解析邏輯的鐵律。4.3 構(gòu)建可輪換的 UA 中間件在規(guī)模化采集時(shí)單個(gè) UA 反復(fù)請(qǐng)求同一個(gè)站點(diǎn)很容易被限流。這時(shí)候需要 UA 輪換。下面是一個(gè)簡(jiǎn)單的輪換中間件思路import random class UARotator: def __init__(self, ua_pool): self.ua_pool ua_pool self._last_ua None def get(self): # 避免連續(xù)兩次拿到同一個(gè) UA candidates [ua for ua in self.ua_pool if ua ! self._last_ua] ua random.choice(candidates) self._last_ua ua return ua rotator UARotator(DESKTOP_UA_POOL) for _ in range(3): print(rotator.get()[:60], ...)這個(gè)實(shí)現(xiàn)里我加了一個(gè)“避免連續(xù)重復(fù)”的小邏輯。原因是如果隨機(jī)池里只有 5 個(gè) UA純隨機(jī)很容易連續(xù)兩次抽到同一個(gè)而連續(xù)相同 UA 的請(qǐng)求模式反而更可疑。這個(gè)細(xì)節(jié)在常規(guī)教程里很少提但實(shí)測(cè)下來(lái)對(duì)降低觸發(fā)風(fēng)控的概率有幫助。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 UA 相關(guān)的典型報(bào)錯(cuò)與解決思路做采集和接口調(diào)試時(shí)跟 UA 相關(guān)的問(wèn)題主要集中在下面幾類。我整理成速查表遇到問(wèn)題可以直接對(duì)照現(xiàn)象可能原因排查與解決返回 403 ForbiddenUA 被識(shí)別為腳本換成主流瀏覽器 UA補(bǔ)齊其他請(qǐng)求頭返回內(nèi)容為移動(dòng)版UA 被判定為移動(dòng)設(shè)備檢查 UA 是否含 Mobile換成桌面 UA返回亂碼或空內(nèi)容服務(wù)端按 UA 返回了不同編碼檢查響應(yīng)頭 Content-Type確認(rèn) UA 與預(yù)期一致頁(yè)面元素定位失敗服務(wù)端返回了不同版本頁(yè)面固定 UA確保每次請(qǐng)求環(huán)境一致登錄態(tài)頻繁失效UA 變化導(dǎo)致會(huì)話校驗(yàn)失敗同一會(huì)話內(nèi)保持 UA 不變這里重點(diǎn)說(shuō)“登錄態(tài)失效”這一條。很多站點(diǎn)的會(huì)話校驗(yàn)會(huì)把 UA 作為指紋的一部分如果你在同一個(gè)會(huì)話里換了 UA服務(wù)端會(huì)認(rèn)為“換人了”直接踢掉登錄態(tài)。所以UA 輪換只適合無(wú)狀態(tài)請(qǐng)求涉及登錄的會(huì)話必須固定 UA。這個(gè)坑我在早期做自動(dòng)化時(shí)踩過(guò)排查了大半天才定位到是 UA 變了。5.2 那些文檔里不會(huì)寫的避坑經(jīng)驗(yàn)第一個(gè)經(jīng)驗(yàn)不要迷信“最新版本 UA 最好”。有些站點(diǎn)的風(fēng)控規(guī)則是“只允許最近 N 個(gè)版本”你用一個(gè)剛發(fā)布沒(méi)幾天的版本號(hào)反而可能因?yàn)橐?guī)則庫(kù)還沒(méi)更新而被攔。穩(wěn)妥做法是用“當(dāng)前主流版本往前推一到兩個(gè)大版本”比如現(xiàn)在主流是 120你用 118 或 119 通常更安全。第二個(gè)經(jīng)驗(yàn)UA 要和 IP、時(shí)區(qū)、語(yǔ)言保持一致。如果你用美國(guó) IP卻配一個(gè)zh-CN的語(yǔ)言頭和中文系統(tǒng) UA這種“地理矛盾”很容易被識(shí)別。做海外采集時(shí)UA 里的系統(tǒng)語(yǔ)言、請(qǐng)求頭的Accept-Language、出口 IP 的地理位置三者最好能對(duì)上。第三個(gè)經(jīng)驗(yàn)移動(dòng)端 UA 不要隨便混用。比如你用一個(gè) iPhone 的 UA但請(qǐng)求頭里Accept寫的是桌面瀏覽器的值或者屏幕分辨率參數(shù)對(duì)不上也會(huì)露餡。真要做移動(dòng)端模擬建議直接用成熟的設(shè)備模擬方案把 UA、視口、觸摸事件等一整套都配齊而不是只改一個(gè) UA 字符串。注意UA 只是客戶端指紋的一部分?,F(xiàn)代風(fēng)控還會(huì)看 TLS 指紋、Canvas 指紋、鼠標(biāo)軌跡等。UA 能解決大部分“低級(jí)攔截”但面對(duì)高級(jí)風(fēng)控單靠 UA 是不夠的。認(rèn)清這一點(diǎn)能幫你少走很多彎路。5.3 服務(wù)端視角如何正確使用 UA 做判斷如果你站在服務(wù)端想用 UA 做設(shè)備適配有幾點(diǎn)建議。首先優(yōu)先用響應(yīng)式設(shè)計(jì)和特性檢測(cè)而不是 UA 判斷。UA 判斷是出了名的脆弱瀏覽器版本一更新、UA 一改你的判斷邏輯就可能失效。其次如果非要用 UA一定要做“優(yōu)雅降級(jí)”——判斷不出來(lái)時(shí)返回通用版本而不是直接報(bào)錯(cuò)。最后不要把 UA 當(dāng)作安全邊界。UA 是客戶端可以隨意偽造的任何基于 UA 的“權(quán)限控制”都是紙糊的真正的安全校驗(yàn)必須放在服務(wù)端會(huì)話和令牌層面。6. 關(guān)于 UA 的一些個(gè)人體會(huì)我做了這么多年采集和自動(dòng)化UA 這個(gè)看似最簡(jiǎn)單的東西反而是最容易被低估的。新手往往覺(jué)得“隨便填一個(gè)就行”結(jié)果卡在 403 上半天找不到原因老手則容易過(guò)度依賴 UA 池忽略了請(qǐng)求頭一致性、會(huì)話固定這些更本質(zhì)的問(wèn)題。我的建議是把 UA 當(dāng)成整個(gè)請(qǐng)求指紋體系的一環(huán)來(lái)對(duì)待而不是孤立的一個(gè)字段。維護(hù)一個(gè)定期更新的 UA 池配合一致的請(qǐng)求頭、合理的請(qǐng)求頻率基本能覆蓋 80% 的常規(guī)場(chǎng)景。剩下那 20% 的硬骨頭靠的是對(duì)目標(biāo)站點(diǎn)行為的細(xì)致觀察而不是堆砌更多的 UA。這個(gè)內(nèi)容后續(xù)還可以往“客戶端指紋體系”方向擴(kuò)展把 TLS、Canvas、字體列表這些維度串起來(lái)看會(huì)更有意思。