User agent 那些事儿

Heero.Luo发表于1个月前,已被查看45次

User agent(下文简称 ua),也就是用户代理,指的是代表用户行为的程序(软件代理程序)。例如,网页浏览器就是一个“帮助用户获取、渲染网页内容并与之交互”的用户代理。

浏览器

当用户代理通过网络协议进行操作时,它通常会向网络上的协作端(比如服务器端)提交一个特定的字符串来标识自己。这段字符串就是用户代理字符串(下文简称 ua 串)。当我们用浏览器访问 Web 应用的时候,浏览器会把自己的 ua 串加到 HTTP 请求的 User-Agent 头字段进行传输。

UA 串的发展史

1990 年,Tim Berners-Lee 编写了第一个浏览器 WorldWideWeb,后来改名为 Nexus。但是,Nexus 只支持文字展示

1993 年,美国 NCSA 组织开发了 Mosaic 浏览器,能支持图片的展示使其成为第一款流行的浏览器。

这时候问题就来了,Nexus 不支持图片展示,而 Mosaic 是支持的。作为开发人员,该怎么编写页面代码呢?这时候 ua 串就可以发挥作用了,开发人员仅需针对 Mosaic ua 串的请求输出图片展示相关的代码即可。Mosaic 的 ua 串样本如下:

NCSA_Mosaic/1.0 (Windows 3.1)

其中 NCSA_Mosaic/1.0 为浏览器代号及其版本号,Windows 3.1 为操作系统及其版本号。

随后,1994 年,Mosaic 团队的领导者 Marc Andreessen 辞职并成立了自己的公司——Netscape,并开发了 Netscape Navigator 浏览器。它的内部代号为「Mozilla」,ua 串样本如下:

Mozilla/4.5 (compatible; HTTrack 3.0x; Windows 98)

Netscape Navigator 的市场占有率一度达到 90%,并且最先支持了框架(frame)特性。开发人员为了区别这两个浏览器,还是用的 ua 串识别这个方式。如果 ua 串中浏览器代号为 Mozilla,那就发送包含框架的页面,否则,就发送不含框架的页面。

再后来,1995 年,微软推出了 Internet Explorer(下文简称 IE)。IE 也支持框架,但因为过去的框架代码都是针对 Mozilla 这个代号发送的,这样一来,IE 即使支持框架,也收不到包含框架的代码。让所有的开发人员把自己的代码都改一遍明显不现实,最后 IE 只好“自称” Mozilla,同时在 ua 串的其他位置增加自己的代号 MSIE。它的 ua 串样本如下:

Mozilla/4.0 (compatible; MSIE 1.0; Windows 3.11)

IE 在浏览器市场中逐渐成为了霸主,败北的 Netscape Navigator 另起炉灶,开发了 Firefox。无论是 IE、Netscape Navigator 还是 Firefox,ua 串都以 Mozilla 代号开头。这也几乎成为了浏览器 ua 串事实上的格式标准:

Mozilla/[version] ([system and browser information]) [platform] ([platform details]) [extensions]

时至今日,ua 串承载的内容越来越多,开发人员识别起来的难度也越来越高了。

一个现代浏览器的 ua 串样本如下:

Mozilla/5.0 (Linux; U; Android 9; zh-CN; vivo Z1i Build/PKQ1.180819.001) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.8.5.1065 Mobile Safari/537.36

对其进行“人工”智能分析后可知:

  • 操作系统为 Android,版本号为 9;
  • 设备品牌为 vivo,型号为 Z1i;
  • 浏览器内核是 Chrome,版本号为 57.0.2987.108;
  • 客户端(浏览器)是 UC 浏览器,版本号是 12.8.5.1065。

那怎么通过程序把这些信息识别出来呢?

操作系统的识别

Windows

Windows 是最常见的桌面系统,它的 ua 串样本如下:

Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)

识别特征是「Windows NT <版本号>」,其中 Windows NT 是 Windows 的内核代号(从 Windows 2000 开始,用的都是这个内核)。内核版本号与 Windows 版本的对应关系如下:

内核版本号Windows 版本
Windows NT 5.0Windows 2000
Windows NT 5.1Windows XP
Windows NT 5.2Windows 2003
Windows NT 6.0Windows Vista
Windows NT 6.1Windows 7
Windows NT 6.2Windows 8
Windows NT 6.3Windows 8.1
Windows NT 10.0Windows 10

除了以上的 Windows 版本,还有像 Windows 3.1、Windows 95、Windows 98、Windows ME 这些历史悠久的版本,以及移动端的 Windows 系统,如非必要就不用特地去识别了。

macOS

另一个常见的桌面系统是 macOS,它是 Mac 电脑上的操作系统。UA 串特征是「Mac OS X <版本号>」。UA 串样本如下:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/601.7.8 (KHTML, like Gecko)
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/605.1.15 (KHTML, like Gecko)

要提一下的是,macOS 原名 OSX,2016年改名为 macOS。但是在 ua 串上只是版本号的区别。

iOS

iOS 是 iPod、iPhone 以及 iPad 的操作系统。它的 ua 串样本如下:

Mozilla/5.0 (iPhone; CPU iPhone OS 13_6_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.18(0x17001231) NetType/WIFI Language/zh_CN
Mozilla/5.0 (iPad; CPU OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.18(0x17001231) NetType/WIFI Language/zh_CN

UA 串特征是「OS <版本号> like Mac OS X」。可以发现,macOS 以及 iOS 的 ua 串特征都包含了「Mac OS X」,所以在识别的时候,要先判定是否 iOS 再判定是否 macOS。

随着苹果产品的更新迭代,出现了更大尺寸的 iPad,其屏幕尺寸更接近于桌面设备的屏幕尺寸。所以,从 iOS 13 起,除了 iPad Mini,其他 iPad 的系统设置中的「请求桌面网站」开关默认开启。

于是,iPad(除了 iPad Mini)的默认 ua 串变得跟 Mac 电脑的一模一样。幸好,目前还没有可触屏的 Mac 电脑,在前端检测最大触点数(navigator.maxTouchPoints > 0)还是可以区分出 iPad 与 Mac 电脑。

Android

另外一个市场占有率比较高的移动设备操作系统是 Android。它的 ua 串样本如下:

Mozilla/5.0 (Linux; U; Android 10; zh-CN; PCHM30 Build/QKQ1.200209.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.1.2.1092 Mobile Safari/537.36
UCWEB/2.0 (MIDP-2.0; U; Adr 4.1.2; id; GT-S6310) U2/1.0.0 UCBrowser/11.2.0.1125 U2/1.0.0 Mobile

UA 串特征主要是「Android <版本号>」,但也有一部分设备或浏览器用的是「Adr <版本号>」。

Linux

Linux 系统一般用于服务器,它的 ua 串样本如下:

Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:24.0) Gecko/20100101 Firefox/24.0
Mozilla/5.0 (X11; Linux i586; rv:31.0) Gecko/20100101 Firefox/31.0

主要的 ua 串特征是「Linux」。然而,Android 也是基于 Linux 的,也会包含 「Linux」关键词。所以,要确认是否服务器或桌面端的 Linux,还要结合「Linux」后面的「x86、i586、i686」去做判定。最终的 ua 串特征就是「Linux <x86|ix86>」。

PC 浏览器的识别

Firefox

Firefox 的 ua 串样本如下:

Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0

它的 ua 串特征是最简单的,就是「Firefox/<版本号>」。

Internet Explorer(IE)

在 IE 的历史中,有一条分界线,那就是 IE 10 这个版本。究其原因,是因为 IE 10 以及之后的版本,对 W3C 标准有比较好的支持,微软不希望开发者将其当成旧版的 IE 看待,所以在 ua 串上做了区分。

IE 10 以下的版本(不包括 10),ua 串样本如下:

Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0)

UA 串特征为「MSIE <版本号>」。

IE 10 及以上版本,ua 串样本如下:

Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko

UA 串特征为「Trident/<内核版本号>; rv:<浏览器版本号>」。

Safari

早期的 Safari 是有 Windows 版本的,后来变成了苹果设备的专属浏览器。它的 ua 串样本如下:

Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Safari/605.1.15
Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.2 Mobile/15E148 Safari/604.1

UA 串特征为「Version/<浏览器版本号>...Safari/<内核版本号>」。但是,Safari 浏览器的识别并没有这么简单。

Chrome

Chrome 的早期版本,使用的是苹果的 WebKit 渲染引擎,但是从 28 这个版本开始,改为用自家的 Blink 渲染引擎。但是它的 ua 串特征没有变化,只是版本号上的区别。Chrome 的 ua 串样本如下:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4315.4 Safari/537.36

UA 串特征均为「Chrome/<版本号>」。但是,大家可以发现,Chrome 的 ua 串包含了 Safari 的 ua 串特征!勿急,接着往下看。

Opera

Opera 原来使用的渲染引擎是 Presto,它的 ua 串样本如下:

Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14

UA 串特征是「Opera/<版本号>」。

从 15 这个版本开始,Opera 改为用 Blink 渲染引擎,ua 串样本如下:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 OPR/72.0.3815.400

UA 串特征是「OPR/<版本号>」。此时,大家又可以发现,Opera 的 ua 串包含 Chrome 以及 Safari 的 ua 串特征。

Edge

与 Opera 类似,Edge 也更换过渲染引擎。原来使用的是微软自家的 EdgeHTML,ua 串样本如下:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.18363

UA 串特征是「Edge/<版本号>」。

后来改为用 Blink 渲染引擎,ua 串样本如下:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.60

特征是「Edg/<版本号>」(注意,是 Edg,不是 Edge,少了个 e)。此时,大家可以再次发现,Edge 的用户代理字符串也包含了 Chrome 以及 Safari 的 ua 串特征。

Chromium 的壳

目前 Chrome、Edge、Opera,以及国内的好些 PC 浏览器,都是基于 Chromium 进行开发的。它们的 ua 串都会包含 Chrome 以及 Safari 的特征:

Mozilla/5.0 (Linux; U; Android 10; zh-CN; PCHM30 Build/QKQ1.200209.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.1.2.1092 Mobile Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/5.1.0.4000 Chrome/55.0.2883.75 Safari/537.36
Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36 TheWorld 7
Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36 QIHU 360SE/13.1.1618.0

但同时,这些浏览器也有自己的 ua 串特征:

浏览器ua 串特征
UC 浏览器UCBrowser/<版本号>
傲游浏览器Maxthon/<版本号>
世界之窗TheWorld <版本号>
360 浏览器QIHU 360SE/<版本号>

其中 360 浏览器的 ua 串比较有意思。360 跟腾讯发生过一次 3Q 大战。腾讯根据 360 浏览器的 ua 特征封锁了来自 360 浏览器的访问。后来,360 浏览器就调整为:只有访问自家产品以及合作网站的时候,才会在 ua 串中增加自己的特征串。

3Q大战

虽然 3Q 大战已经过去很多年,但 360 浏览器仍然没有针对所有网站开放自己的特征串。不过,在其极速模式下,会存在一个叫做 qihoo 的全局变量。可以通过判断 window.qihoo 是否存在,从而得知当前浏览器是否 360 浏览器。

综上所述:

  • 要知道一个浏览器是否为 Chrome,先要把其他基于 Chromium 的浏览器排除掉;
  • 要知道一个浏览器是否为 Safari,要把所有基于 Chromium 的浏览器排除掉。

所以识别流程应为:

  • 如果带有“壳”的 ua 串特征,则为对应“壳”的浏览器;
  • 如果没有“壳”的 ua 串特征,但是有 Chrome 的 ua 串特征,则为 Chrome;
  • 如果没有 Chrome 的 ua 串特征,但是有 Safari 的 ua 串特征,则为 Safari。

这里面的主要问题在于,“壳”的特征串没有固定的格式,在 ua 串中也没有固定的位置。所以“壳”的 ua 串特征库,是需要根据实际需求去持续更新的。

移动端浏览器

移动端有两种常见的网页浏览方式,一种是在浏览器中打开,另一种是在 app 中通过 WebView 打开。不过严格来说,浏览器也是一种 app,也是通过 WebView 去打开网页。

先看看 iOS 的情况。苹果不允许在移动设备上使用第三方浏览器内核,所以 iOS 上的第三方 app,其实都是调用系统提供的 WebView。这样做的好处在于,可以在所有 app 中保持网页渲染效果的一致性。

iOS 提供系统提供的 WebView 有两种:WKWebView 和 UIWebView。前者是 iOS 8 开始提供的性能比较高的 WebView,后者则是性能比较低、逐渐被淘汰的。时至今日,作为默认浏览器的 Safari 用的自然是 WKWebView,但第三方 app,可以在两种 WebView 之间自行选择。

Android 是一个比较开放的系统,它允许 app 调用系统 WebView,或者使用第三方的浏览器内核。几个常用的 app——UC 浏览器、QQ 浏览器、微信,它们的内核要么基于 WebKit 修改而来,要么基于 Blink 修改而来。所以,它们的 ua 串大多都带有 Safari 或者 Chrome 的 ua 串特征,并且,因为经过魔改,网页渲染效果或多或少都有一些差异。

PC 端的浏览器大部分都有对应的移动端 app,ua 串特征也是一致的,就不重复介绍了。接下来主要聊一下非浏览器 app。

微信

微信的 ua 串样本如下:

Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/7.0.4(0x17000428) NetType/WIFI Language/zh_CN
Mozilla/5.0 (Linux; Android 9; MHA-AL00 Build/HUAWEIMHA-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 MMWEBID/9772 MicroMessenger/7.0.6.1460(0x27000634) Process/tools NetType/WIFI Language/zh_CN

UA 串特征为「MicroMessenger/<版本号>」。

后来,微信团队又发布了企业微信。UA 串样本为:

Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_3 like Mac OS X) AppleWebKit/603.3.8 (KHTML, like Gecko) Mobile/14G60 wxwork/2.1.5 MicroMessenger/6.3.22
Mozilla/5.0 (Linux; Android 9.0; BKL-AL20 Build/HUAWEIBKL-AL20; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.132 MQQBrowser/6.2 TBS/044409 Mobile Safari/537.36 wxwork/2.7.2 MicroMessenger/6.3.22 NetType/WIFI Language/zh

企业微信的 ua 串里面包含微信的特征串,同时也有自己的特征串「wxwork/<版本号>」。所以,在代码流程上,要先判断是否企业微信,再判断是否微信。

QQ

手机 QQ 的 ua 串样本如下:

Mozilla/5.0 (Linux; Android 9; ONEPLUS A6000 Build/PKQ1.180716.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/044704 Mobile Safari/537.36 V1_AND_SQ_8.0.7_1204_YYB_D QQ/8.0.7.4085 NetType/4G WebP/0.3.0 Pixel/1080 StatusBarHeight/80
Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/18D70 QQ/8.7.8.635 V1_IPH_SQ_8.7.8_1_APP_A Pixel/1125 MiniAppEnable SimpleUISwitch/0 StudyMode/0 QQTheme/1000 Core/WKWebView Device/Apple(iPhone X) NetType/4G QBWebViewType/1 WKType/1

UA 特征为「QQ/<版本号>」。注意,QQ 的 ua 串也包含移动端 QQ 浏览器的 ua 串特征「MQQBrowser/<版本号>」。

设备品牌的识别

与很多桌面端设备不同,移动设备的浏览器 ua 串大多包含品牌信息。例如:

Mozilla/5.0 (Linux; U; Android 8.0.0; zh-CN; HUAWEI NXT-AL10 Build/HUAWEINXT-AL10) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/57.0.2987.108 UCBrowser/12.6.6.1046 Mobile Safari/537.36
Mozilla/5.0 (Linux; Android 8.1.0; OPPO R11t Build/OPM1.171019.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36 uni-app Html5Plus/1.0 (Immersed/18.0)

其中品牌信息分别为「HUAWEI」(华为)、「OPPO」(Oppo)这两个品牌词。这样一来,想从 ua 串中识别设备品牌,似乎只需要维护一个品牌词的特征库就可以了。

然而,有一部分设备或者浏览器,ua 串中仅有型号,没有品牌词。例如:

Mozilla/5.0 (Linux; U; Android 4.4.2; zh-CN; SM-N9006 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.0.7.1087 Mobile Safari/537.36
Mozilla/5.0 (Linux; U; Android 10; zh-CN; PCHM30 Build/QKQ1.200209.002) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/78.0.3904.108 UCBrowser/13.1.2.1092 Mobile Safari/537.36

其中型号分别为「SM-N9006」(三星)、「PCHM30」(Oppo)。这坑可就大了,常见品牌只是数十个的量级,但它们的型号总数起码是数万的量级,而且会不断增加。

所以设备品牌的识别,需要把两者结合起来:

  1. 先通过品牌词去识别,如果都匹配不上,则进入下一步。
  2. 通过“人工”智能,汇总剩余 ua 串的型号,通过搜索引擎查询到型号对应的品牌,并编写品牌的型号规则。

对于型号的识别,建议抓大放小,忽略量小的型号。并且,随着各种设备的推陈出新,其市场占有率的变化也是比较大的,需要及时更新识别规则。

广告时间

如果你有 ua 串识别的需求,又不想去处理以上的这些复杂情况,可以使用 @just4/ua-info 这个 JS 库。它可以从 ua 串中识别出常见的操作系统、浏览器内核、客户端、设备品牌等信息。

import { getCurrentUAInfo } from '@just4/ua-info';

const uaInfo = getCurrentUAInfo();
uaInfo.os.isWindows; // 是否 Windows 系统
uaInfo.os.isAndroid; // 是否 Android 系统
uaInfo.browser.isIE; // 是否 IE 浏览器核心
uaInfo.browser.isChrome; // 是否 Chrome 浏览器核心
uaInfo.client.isWx; // 是否微信
uaInfo.client.isUCBrowser; // 是否 UC 浏览器
uaInfo.brand.isApple; // 是否苹果设备
uaInfo.brand.isHuawei; // 是否华为设备

此外,还可以对操作系统、浏览器内核、客户端的版本号进行对比:

uaInfo.os.isWindows && uaInfo.os.version.gt('5.2');
uaInfo.browser.isIE && uaInfo.browser.version.gte('10');
uaInfo.client.isUCBrowser && uaInfo.client.version.lt('14');

最后

由上可见,ua 串的内容比较繁多,而且具有欺骗性。所以,在现代化的前端开发体系中,要检查浏览器是否支持某项功能,应以特性检测为先,ua 串识别是作为最后的手段

评论 (1条)

发表评论

(必填)

(选填,不公开)

(选填,不公开)

(必填)