资讯详情

User Agent 深度解析:从结构拆解到爬虫与适配实战

📅 2026/10/16 4:06:19 | 华诺云谱 👁 阅读
User Agent 深度解析:从结构拆解到爬虫与适配实战
1. 从一个被忽视的细节说起为什么User Agent值得单独聊做Web开发或者数据采集的朋友大概率都跟User Agent打过交道。这东西看起来不起眼就是HTTP请求头里的一行字符串但它在整个Web生态里扮演的角色远比大多数人想象的要复杂。我刚开始接触网络编程那会儿觉得UA就是个可有可无的标识随便填一个就行结果后来在真实项目里踩了一连串的坑才意识到这行字符串背后牵扯的东西有多深。User Agent直译过来叫“用户代理”本质上是一段身份声明。浏览器发请求的时候会带上它告诉服务器“我是谁、我大概长什么样、我跑在什么系统上”。服务器拿到这行信息之后可以做很多事情判断该返回桌面版页面还是移动版页面、决定要不要启用某些特性、做访问统计、甚至做风控拦截。听起来简单但真正把这行字符串拆开看里面的信息密度相当高。这篇文章想做的事情很明确把常见User Agent这件事讲透。不是那种复制粘贴一份UA列表就完事的敷衍内容而是从结构、分类、使用场景、实操技巧、常见坑几个维度把我知道的、踩过的、总结出来的东西都摊开讲。适合谁看做爬虫的、做前端适配的、做服务端日志分析的、做自动化测试的以及任何需要跟HTTP请求打交道的人。哪怕你只是好奇浏览器到底在偷偷告诉服务器什么信息这篇也能给你一个清晰的答案。我个人的习惯是遇到任何跟请求相关的问题第一反应就是去看UA。它就像快递单上的寄件人信息虽然不能完全代表包裹里的东西但能帮你快速判断很多事情的来龙去脉。下面我就按自己的理解把这套东西系统地梳理一遍。2. User Agent的结构拆解与核心逻辑2.1 一行字符串里到底藏了多少信息先拿一个最典型的UA来看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这是历史遗留产物。早年Netscape浏览器叫Mozilla后来所有浏览器为了兼容早期只认Mozilla的服务器都强行把自己伪装成Mozilla。现在这玩意儿基本就是个固定前缀没有实际意义但你不能省省了有些老服务器会不认。(Windows NT 10.0; Win64; x64)括号里是平台信息。Windows NT 10.0表示Windows 10或11Win64; x64表示64位系统。如果是Mac这里会是Macintosh; Intel Mac OS X 10_15_7如果是手机会带Android或iPhone字样。AppleWebKit/537.36渲染引擎标识。Chrome、Safari、Edge、Opera现在都基于WebKit或它的分支Blink所以这行几乎成了标配。(KHTML, like Gecko)又是历史兼容产物告诉服务器“我像Gecko一样能渲染”实际跟Gecko没啥关系。Chrome/120.0.0.0真正的浏览器标识和版本号。这是最有价值的部分。Safari/537.36Chrome为了兼容某些只认Safari的网站也会带上这个标记。所以你会看到一个Chrome的UA里同时出现Chrome和Safari这不矛盾。理解这个结构之后你就能明白为什么UA解析不能简单地用字符串匹配。因为不同浏览器、不同版本、不同平台的组合方式千变万化而且历史包袱极重。我见过有人写正则去匹配UA结果被各种奇葩组合搞得焦头烂额最后不得不引入专门的解析库。2.2 为什么UA会变得这么“混乱”这里得说一段历史。早期浏览器大战的时候各家为了抢占市场故意在UA里做手脚。有的浏览器把自己伪装成竞争对手有的服务器只给特定UA返回完整功能。结果就是大家互相模仿UA越来越长、越来越假。到了移动互联网时代又加上了设备型号、系统版本、甚至运营商信息UA直接膨胀成一大坨。这种混乱带来一个直接后果你不能完全相信UA。任何客户端都可以随意伪造UA服务器如果只靠UA做判断很容易被绕过。但这不代表UA没用它在统计、适配、调试这些场景下依然是第一手信息。关键是要理解它的局限性别把它当成安全边界。我个人的经验是UA适合做“软判断”比如决定返回哪套模板、记录访问来源分布不适合做“硬控制”比如权限校验、风控拦截。这个边界一定要划清楚否则迟早出事。2.3 常见UA的分类维度要把常见UA整理清楚得先确定分类维度。我一般按三个维度来分按设备类型分桌面端、移动端、平板、爬虫/机器人、其他智能电视、车机等。这个维度最实用因为大部分适配逻辑都是围绕设备类型展开的。按浏览器内核分WebKit/Blink系Chrome、Edge、Opera、国产浏览器大多也是、Gecko系Firefox、Trident系老IE现在基本绝迹。内核决定了CSS和JS的兼容性表现。按用途分真实用户浏览器、搜索引擎爬虫、数据采集工具、监控探针、自动化测试框架。这个维度在做日志分析和反爬策略时特别重要。下面这张表是我自己整理的一个速查对照把常见UA的核心特征列出来方便快速识别设备/用途典型标识片段关键判断点Windows桌面Windows NT 10.0; Win64; x64带Windows NT且无MobilemacOS桌面Macintosh; Intel Mac OS X带MacintoshAndroid手机Android 13; SM-G9910带Android和MobileiPhoneiPhone; CPU iPhone OS 17_0带iPhoneiPadiPad; CPU OS 17_0带iPad注意新版iPad可能伪装成MacChromeChrome/120.0.0.0 Safari/537.36同时有Chrome和SafariFirefoxFirefox/121.0带Firefox通常无ChromeSafariVersion/17.0 Safari/605.1.15有Version和Safari无Chrome搜索引擎爬虫Googlebot/2.1、Bingbot/2.0带bot字样通用爬虫库python-requests/2.31.0带库名和版本这张表不是让你死记硬背而是建立一个判断框架。实际遇到UA的时候先看有没有Mobile、有没有bot、有没有Chrome基本就能定位个八九不离十。3. 主流浏览器UA的详细解析与实操识别3.1 桌面端浏览器UA的演变与现状桌面端UA这几年最大的变化是“版本号冻结”和“信息精简”。Chrome从某个版本开始把Windows版本号固定在Windows NT 10.0不再随实际系统更新而变化macOS版本号也冻结在10_15_7。这么做是为了减少UA带来的指纹追踪风险但也给开发者判断真实系统版本增加了难度。具体来看几个主流桌面浏览器的UAChrome on WindowsMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Chrome on macOSMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Firefox on WindowsMozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0注意Firefox的格式跟Chrome差别很大。它用rv:121.0表示Gecko引擎版本用Gecko/20100101作为固定标记最后才是Firefox/121.0。而且Firefox的UA里没有Safari和Chrome这是区分它的关键。Safari on macOSMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15Safari的特点是带Version/17.0而且WebKit版本号跟Safari版本号一致。它没有Chrome标记这是跟Chrome on Mac最明显的区别。Edge on WindowsMozilla/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.0Edge现在基于Chromium所以UA跟Chrome几乎一样只在末尾多了Edg/120.0.0.0。这个Edg标记是识别Edge的唯一可靠依据。早期Edge用的是Edge/后来改成Edg/这个细节很多人会搞混。3.2 移动端UA的特殊性与识别难点移动端UA比桌面端复杂得多因为要携带设备型号、系统版本、甚至屏幕密度等信息。而且不同厂商的定制系统会往UA里塞私货导致格式五花八门。Android ChromeMozilla/5.0 (Linux; Android 13; SM-G9910) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36关键点Linux; Android 13; SM-G9910这一段SM-G9910是设备型号。Mobile这个词是判断手机的重要标志。但注意Android平板通常不带Mobile而是带Tablet或者什么都不带。iPhone SafariMozilla/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.1iPhone的UA里有个很有意思的地方like Mac OS X。这是因为早期iPhone Safari基于Mac Safari改的保留了这個标记。Mobile/15E148是移动版本标识这个字符串会随系统更新变化。iPad SafariMozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1iPad的UA跟iPhone几乎一样只是把iPhone换成iPad。但这里有个大坑新版iPadOS默认请求桌面版网站UA会伪装成MacMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15这时候你光看UA根本分不清是Mac还是iPad。解决办法是结合触摸事件检测或者屏幕尺寸判断不能只依赖UA。国产浏览器 很多国产浏览器基于Chromium但会在UA里加自己的标识。比如某浏览器可能长这样Mozilla/5.0 (Linux; Android 13; SM-G9910) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36 MyBrowser/1.0末尾的MyBrowser/1.0就是自定义标记。这类UA的识别策略是先按标准Chromium解析再看有没有额外标记。3.3 用代码解析UA的实操方案光靠眼睛看UA效率太低实际项目里肯定要用代码解析。我试过几种方案各有优劣。方案一正则匹配。适合只需要判断几个关键特征的场景。比如判断是不是移动端import re def is_mobile(ua): pattern rMobile|Android|iPhone|iPad|iPod|Windows Phone return bool(re.search(pattern, ua, re.IGNORECASE))这个方案简单直接但维护成本高。浏览器版本一更新正则可能就失效了。方案二用现成的解析库。Python里比较常用的是ua-parserfrom ua_parser import parse ua Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 result parse(ua) print(result.user_agent.family) # Chrome print(result.user_agent.major) # 120 print(result.os.family) # Windows print(result.device.family) # Other这个库的好处是规则库持续更新覆盖率高。缺点是依赖第三方数据遇到特别新的UA可能识别不准。方案三自己维护一个特征表。适合对准确性要求极高、且UA来源相对固定的场景。比如只处理自家App的WebView请求那UA格式基本可控自己写一套匹配规则反而更稳。我个人的建议是通用场景用ua-parser这类库打底关键判断再加一层自定义规则兜底。比如移动端适配先用库判断再用Mobile关键字二次确认双保险。注意不管用哪种方案都要做好UA缺失或格式异常的处理。有些客户端会发空UA有些会发超长UA代码里必须加长度限制和异常捕获否则容易出问题。4. 爬虫与自动化场景下的UA策略4.1 为什么爬虫必须重视UA做数据采集的人都知道很多网站会检查UA。如果UA是空的或者明显是爬虫库的默认UA请求大概率被拒。比如python-requests/2.31.0这种UA稍微有点防护的网站直接返回403。但反过来如果你把UA伪装成正常浏览器就能绕过大部分基础检测。这就是为什么爬虫项目里UA池是个标配。不过这里面的门道比想象中多不是随便找几个UA轮换就完事。4.2 UA池的构建与轮换策略我见过很多新手直接在网上复制一份UA列表然后随机取一个用。这种做法在早期可能有效现在越来越不好使了。原因有几个第一UA和请求的其他特征要匹配。比如你用了Chrome on Windows的UA但请求头里的Accept-Language、Sec-CH-UA这些字段对不上服务器一样能识别出异常。现代浏览器会发送一系列客户端提示头Client Hints这些跟UA是配套的。只改UA不改其他头等于穿了西装却踩着拖鞋。第二UA要跟行为匹配。如果一个UA声称是iPhone但请求频率、访问路径完全不像真人操作照样会被风控。第三UA池需要更新。浏览器版本一直在变如果你的UA池里全是几年前的版本号反而显得可疑。我的做法是维护一个精简但真实的UA池每个UA都配一套完整的请求头模板。轮换的时候整组替换而不是只换UA。下面是一个请求头模板的示例headers_template { 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: text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Sec-CH-UA: Not_A Brand;v8, Chromium;v120, Google Chrome;v120, Sec-CH-UA-Mobile: ?0, Sec-CH-UA-Platform: Windows, Sec-Fetch-Dest: document, Sec-Fetch-Mode: navigate, Sec-Fetch-Site: none, Sec-Fetch-User: ?1, Upgrade-Insecure-Requests: 1 }注意Sec-CH-UA系列头是Chromium特有的客户端提示跟UA里的Chrome版本必须一致。Sec-CH-UA-Mobile为?0表示桌面端?1表示移动端。这些细节对不上很容易被识别。4.3 常见爬虫UA的识别与应对从服务端角度看识别爬虫UA有一套常见规则。我整理了一个对照表UA特征可能来源服务端常见处理空UA脚本或配置错误直接拒绝或标记异常python-requestsPython爬虫库限流或拒绝curl/7.x命令行工具视情况放行或拒绝Java/1.8.0Java程序标记为可疑Go-http-clientGo程序标记为可疑Googlebot搜索引擎放行但验证IPHeadlessChrome无头浏览器重点检测版本号过旧未更新的爬虫可能放行但降级这里要特别说HeadlessChrome。很多人用无头浏览器做采集以为把UA改成普通Chrome就行了但无头模式有很多其他特征会暴露比如navigator.webdriver为true、缺少某些插件、屏幕尺寸异常等。光改UA不够还得做一系列反检测处理。提示如果你的采集目标有较强防护建议不要只依赖UA伪装。UA只是第一层后面还有TLS指纹、行为特征、IP信誉等多层检测。把精力花在理解目标网站的整体防护逻辑上比单纯堆UA池更有效。4.4 自动化测试中的UA设置做自动化测试的时候UA设置是另一个场景。比如你要测试网站在不同设备上的表现就需要模拟不同UA。用Selenium或者Playwright的时候可以这样设置from selenium import webdriver from selenium.webdriver.chrome.options import Options options Options() options.add_argument(--user-agentMozilla/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) driver webdriver.Chrome(optionsoptions) driver.get(https://example.com)Playwright的设置方式更简洁from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch() context browser.new_context( user_agentMozilla/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, viewport{width: 390, height: 844} ) page context.new_page() page.goto(https://example.com)注意这里同时设置了UA和视口尺寸。因为很多响应式网站是根据视口宽度决定布局的光改UA不改视口页面可能还是桌面版。这两个要配套改才能真实模拟移动设备。5. 服务端UA解析与日志分析实战5.1 从访问日志里提取有价值的信息服务端每天产生大量访问日志UA是其中信息密度最高的字段之一。但原始日志里的UA是一整行字符串直接看很费劲。我一般会做几步处理第一步解析UA提取设备类型、浏览器、操作系统三个核心字段。第二步按维度聚合统计比如移动端占比、Chrome版本分布、爬虫流量比例。第三步针对异常UA做告警比如突然出现大量空UA或者未知UA。用Python处理日志的示例from ua_parser import parse from collections import Counter def analyze_logs(log_lines): device_counter Counter() browser_counter Counter() os_counter Counter() for line in log_lines: ua extract_ua(line) # 假设这个函数能从日志行里提取UA if not ua: device_counter[unknown] 1 continue result parse(ua) device_counter[result.device.family] 1 browser_counter[result.user_agent.family] 1 os_counter[result.os.family] 1 return device_counter, browser_counter, os_counter这个分析能帮你快速了解用户构成。比如发现移动端占比超过70%那移动端适配的优先级就得提高发现某个旧版本浏览器占比异常高可能是有特殊用户群体需要兼容。5.2 UA解析中的常见陷阱在实际解析UA的过程中有几个坑我反复踩过这里列出来供参考陷阱一iPadOS伪装成Mac。前面提过新版iPad默认请求桌面版UA跟Mac一模一样。如果你的统计里Mac占比突然升高而你又没有对应的Mac用户增长那很可能是iPad被算进去了。陷阱二Chrome和Safari同时出现。新手容易看到Safari就以为是Safari浏览器实际上Chrome的UA里也有Safari标记。判断顺序应该是先看有没有Chrome或Edg再看Safari。陷阱三版本号格式不统一。有的用Chrome/120.0.0.0有的用Chrome/120还有的用Chrome/120.0。解析的时候要做归一化处理否则统计结果会很乱。陷阱四国产浏览器的自定义标记。很多国产浏览器在标准UA后面加自己的标识但格式不统一。有的用空格分隔有的用分号有的直接拼在后面。解析的时候要留出扩展空间不能写死规则。陷阱五爬虫伪装成浏览器。有些爬虫会把UA改得跟浏览器一模一样但其他特征对不上。这时候光靠UA解析分不出来需要结合请求频率、访问路径、IP分布等做综合判断。5.3 基于UA的灰度发布与适配策略UA在服务端还有一个重要用途灰度发布和差异化适配。比如新功能只对特定浏览器开放或者旧版本浏览器自动降级到兼容模式。我做过一个项目需要根据浏览器是否支持某个新API来决定返回哪套前端代码。做法是在服务端解析UA判断浏览器版本然后注入不同的配置def get_browser_config(ua): result parse(ua) browser result.user_agent.family major int(result.user_agent.major or 0) if browser Chrome and major 100: return {feature_x: True, polyfill: False} elif browser Firefox and major 100: return {feature_x: True, polyfill: False} elif browser Safari and major 16: return {feature_x: True, polyfill: False} else: return {feature_x: False, polyfill: True}这种策略比纯前端检测更可靠因为服务端可以在页面渲染之前就决定好返回内容避免闪烁和额外请求。但要注意UA判断只是辅助最终还是要靠特性检测兜底。因为UA可以伪造而且新浏览器版本可能随时改变行为。注意做灰度发布的时候UA判断规则要集中管理不要散落在各个业务代码里。否则浏览器版本一更新你得满项目找地方改。我一般会单独抽一个模块专门负责UA解析和策略输出业务代码只调用接口。6. 常见问题排查与避坑经验实录6.1 UA相关问题的排查思路遇到跟UA有关的问题我一般按这个顺序排查第一确认UA是否真的被发送了。用抓包工具看原始请求有时候代码里设置了UA但被中间件覆盖了或者被代理改写了。第二确认UA格式是否正确。有些库对UA格式有要求格式不对会直接报错。比如某些HTTP客户端不允许UA里有换行符。第三确认服务端解析逻辑是否匹配。服务端用的解析库版本可能比较旧识别不了新UA。这时候要么更新库要么加自定义规则。第四确认是否有缓存干扰。有时候UA改了但响应没变可能是CDN或者浏览器缓存了旧内容。清缓存或者加随机参数再试。6.2 常见问题速查表问题现象可能原因排查方法解决方案请求返回403UA被识别为爬虫查看请求头UA字段更换为真实浏览器UA页面显示桌面版UA被判断为桌面端检查UA是否含Mobile添加Mobile标识并调整视口UA解析结果为空UA格式异常或库不识别打印原始UA字符串更新解析库或加自定义规则统计中Mac占比异常iPad伪装成Mac结合触摸事件判断用特性检测区分iPad和Mac浏览器版本识别错误UA版本号格式不统一对比原始UA和解析结果做版本号归一化处理爬虫被限流UA轮换策略太简单检查请求头是否配套整组替换请求头模板移动端适配失效只改UA没改视口检查viewport设置UA和视口配套修改UA被截断请求头长度超限查看服务端日志精简UA或调整服务端配置6.3 几个我踩过的坑坑一以为UA是可靠的设备标识。早期做统计的时候我直接用UA里的设备型号做唯一标识结果发现同一型号设备大量重复根本没法区分。UA只能标识设备类型不能标识具体设备。要做设备识别得用其他方案。坑二忽略了UA的长度限制。有些服务器对请求头总长度有限制UA太长可能导致请求被拒。特别是移动端UA加上各种自定义标记后可能超过1KB。我遇到过因为UA过长导致请求失败的情况排查了半天才发现是长度问题。坑三在UA里放敏感信息。有些开发者喜欢在UA里加自定义标记比如用户ID、会话标识。这是非常危险的做法因为UA会出现在各种日志和第三方统计里等于把敏感信息到处传播。UA里只放必要的技术标识不要放业务数据。坑四过度依赖UA做安全判断。前面强调过UA可以随意伪造。我见过有系统用UA来判断是不是管理员结果被轻易绕过。安全相关的判断必须用服务端可控的凭证UA只能作为辅助信号。坑五忘记更新UA解析规则。浏览器版本更新很快解析库如果长期不更新新版本浏览器可能被识别成未知。我一般会定期检查解析库的更新日志同时监控未知UA的比例超过阈值就手动补充规则。6.4 关于UA的未来趋势UA这套机制虽然历史悠久但一直在演进。Chromium推动的客户端提示Client Hints就是试图用更结构化的方式替代UA的部分功能。未来UA可能会进一步精简更多信息通过独立的请求头传递。但短期内UA不会消失因为存量系统和习惯太难改变。对开发者来说务实的做法是继续用好UA但不要把架构绑死在UA上。解析逻辑要可替换判断规则要可配置这样无论UA怎么变你的系统都能快速适应。7. 我个人的一些实操体会做Web开发这些年UA这东西给我的感觉就是“小字段大麻烦”。它简单到一行字符串复杂到能牵扯出兼容性、安全、统计、适配一大堆问题。我的核心体会是把UA当成参考信息而不是决策依据。用它来做软判断、做统计、做调试但别用它来做硬控制、做安全边界。另外维护一份自己的UA速查表和解析规则库比每次遇到问题现查要高效得多。我现在的做法是遇到新的UA就随手记下来标注特征和判断要点日积月累就形成了一套自己的知识库。这东西看着不起眼但关键时刻能省很多时间。最后分享一个小技巧如果你不确定某个UA该怎么解析可以把它丢到浏览器的navigator.userAgent里对比一下或者用在线UA解析工具交叉验证。多验证几次你对UA的敏感度自然就上来了。这东西没有捷径就是多看、多记、多总结。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑