如何科學準確的識別用戶瀏覽器?

Hello 大家好,我是 Coco。今天給大家帶來一篇在瀏覽器識別方面做的非常全面細緻的一篇文章。由我在 ShowMeBug 的好友投稿授權分享~

Coco 注:ShowMeBug 是一款可記錄、可分析 、可覆盤的技術評估和在線面試神器。

正文從下面開始~

經常有用戶在 ShowMeBug 在線面試過程中,使用一些奇怪的瀏覽器來打開鏈接進行視頻面試,然後出現一些音視頻卡頓等問題。

多次排查後發現產品的音視頻功能一但使用了瀏覽器的 WebRTC 特性,就會對瀏覽器的要求特別高,如不在正常的瀏覽器支持範圍內,就容易出現問題。所以我們在檢測這個問題時,通常有兩種方法識別出瀏覽器的型號和版本。

第一種方法:使用 modernizr[1] 來進行 feature detection, 通過探測這些 API 是否存在,來判讀是否可以使用 WebRTC,畢竟 WebRTC 是有瀏覽器 API 組成。

不過,這種方法不夠嚴謹,WebRTC 是一項很新的技術,今年 1 月份才正式成爲官方標準,但目前只支持部份瀏覽器,穩定性也還需要進一步加強。

The World Wide Web Consortium (W3C) and the Internet Engineering Task Force (IETF) announced today that Web Real-Time Communications (WebRTC), which powers myriad services, is now an official standard, bringing audio and video communications anywhere on the Web.

今年 1 月,W3C 和 IETF 宣佈,爲無數服務提供支持的 WebRTC 現已成爲官方標準。

———— 2021.01.26

網絡實時通信 (WebRTC) 改變了通信格局;成爲萬維網聯盟 (W3C) 建議書和多個互聯網工程任務組 (IETF) 標準 [2]

顯然,feature detection 不符合我們的應用場景,目前不滿足我們的需求,所以此方法 pass。

第二種方法,也是我們目前採用的,業內比較通用的辦法就是檢測 User-Agent。

curl http://202.38.95.46:12001/ -H "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) HEICORE/49.1.2623.213 Safari/537.36"

User-Agent 是什麼

用戶不能直接去互聯網上獲取信息,需要一個軟件作爲載體去代表用戶的行爲,這個軟件就是 User-Agent (用戶代理),瀏覽器就是一種典型的 User-Agent 。

用戶使用不同的軟件去用統一的協議去做相同的事情。這也是定義在 http 請求裏的,每一條 http 請求一定會攜帶 User-Agent 頭 。

網站的服務者可以通過 User-Agent 頭來判斷用戶使用了什麼瀏覽器,當然也可以根據 User-Agent 的內容來提供差異化的服務。

標準語法和歷史

原本 User-Agent 瀏覽器的語法是很清晰的

User-Agent: <product> / <product-version> <comment>

User-Agent - HTTP | MDN[3]

因爲可以根據 User-Agent 的內容來提供差異化的服務,所以當年在瀏覽器大戰時期,瀏覽器的實現各不相同。當年 Mozilla (Firefox 的前身)瀏覽器最強的,很多網站都只對 Mozilla 提供高質量的服務,後來有人把自己僞裝成了 Mozilla (沒錯,就是 IE 先開始的)。從此 Mozilla/5.0 就變成了 User-Agent 的第一段。

History of the browser user-agent string[4]

後來的瀏覽就在這上面不斷擴充,就像今天這樣:

Linux / Firefox

Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0

Mac OS / Safari

Mozilla/5.0 (Macintosh; Intel Mac OS X 11_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1 Safari/605.1.15

Chromium OS  / Chrome

Mozilla/5.0 (X11; CrOS x86_64 13904.16.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.25 Safari/537.36

Windows / Edge

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

這就變成了去識別 User-Agent 變得很困難,目前的識別基本上都是使用正則表達式,配合自己的 User-Agent 庫來判斷。

這方面的庫有很多,對比很多後,這個庫是比較全的 ua-parser-js[5]

目前幾乎所有的網站識別瀏覽器都是 User-Agent 來判斷,有兩個接口:

前端有瀏覽器接口:

window.navigator.userAgent

後端可以通過瀏覽器的 http request header 來拿到 User-Agent

user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36

如何使用 User-Agent

在移動端因爲算力的問題,在有些老舊的處理器上也會出現卡頓的情況,不過,我們可以在瀏覽器裏跑一下 Benchmark 來判斷算力是否夠用,移動端的 User-Agent 會攜帶處理器信息(可以去查數據庫來判斷)

所以說,目前所進行的操作是:檢測用戶瀏覽器 ——> 給予提示,並在文檔頁顯示支持性列表

最後,要通過定義的規則生成這樣一個表格:

Screen_Shot_2021-08-13_at_17.09.59.png

爲了降低未來的維護成本要使用一套數據源,既可以檢測,又可以在文檔頁生成列表

ua-parser-js 的返回結果的數據結構比較科學,直接複用這一數據結構

interface Result {
  ua: string;
  browser: {
    name: string | undefined;
    version: string | undefined;
  };
  device: {
    model: string | undefined;
    type: string | undefined;
    vendor: string | undefined;
  };
  engine: {
    name: string | undefined;
    version: string | undefined;
  };
  os: {
    name: string | undefined;
    version: string | undefined;
  };
  cpu: {
    architecture: string | undefined;
  };
}

這樣我們可以定義三條規則:

// 存在優先級關係: daily_list > white_list > black_list
//
// https://www.npmjs.com/package/ua-parser-js
// @params: ua-parser-js.result
// @params: { <list_name>: <whiteList | blackList>}
// @return: string(list_name)
function browserDetect(ua, list) {
  const { daily_list, white_list, black_list } = list
  if (checkList(ua, daily_list)) return 'daily_list'
  if (checkList(ua, white_list)) return 'white_list'
  if (checkList(ua, black_list)) return 'black_list'
  return ''
}

基於這種數據結構,加入一種簡單的語法。支持版本號判斷,加入這個幾種符號的支持:>=<

由於沒有現成可以用的,所以要自己寫一段判斷

compare-versions[6]

這樣的話配置文件就可以這樣寫:config/browser.yml

# https://www.npmjs.com/package/ua-parser-js
#
# 本文件的語法是在這個庫之上做的修改

# 白名單,完全沒有問題的版本
white_list:
  - browser:
      name: "Chrome"
      version: ">= 85.0.0.0"
  - browser:
      name: "Firefox"
      version: ">= 85.0.0.0"
  - browser:
      name: "Edge"
      version: ">= 45.0.0.0"
    device:
      type: "mobile"
    os:
      name: "Android"
      version: ">= 10.0"

black_list:
  # 老版本的 Edge 不支持
  - browser:
      name: "Edge"
      version: "< 80.0.0.0"
    os:
      name: "Windows"

  # 手機微信內置瀏覽器
  - browser:
      name: "WeChat"
    device:
      type: "mobile"

使用我們產品的用戶,上至自己編譯瀏覽器自己用的極客,下至用微信內置瀏覽器的小白, 所以要在 beta 版、dev 版、canary 開發版、nightly 版等各種版本中給予提示,檢測 WebRTC 不穩定,就可能會有問題。

曾經有人用了開發版的瀏覽器,造成音視頻的不穩定,然後又跟新了最新版(還是開發版)。。。問題變得更嚴重了

由於開發版瀏覽器並不會在 ua 裏面攜帶 dev 的標識,只能通過版本號來判斷。可以使用 caniuse-lite 的數據庫,取出最新穩定版的的版本號,然後進行版本號比對。

但是 caniuse-lite 的數據庫有 1.3M 如果直接使用,會打包整個數據庫。這個體積的增加了太多,需要優化一下,所以採用把查詢結果打包成文件的辦法,實際上真正有用的數據非常的少。

創建一個生成器,可以動態創建這個文件 latest_browser_list_generator.js

#!/usr/bin/env node

const browserslist = require('browserslist')
const fs = require('fs')

const list = {
  'firefox': true,
  'chrome': true,
  'edge': true,
}

const latest = browserslist("last 1 version").filter(i => list[i.split(' ')[0]])
fs.writeFileSync('latest_browser_list.js'`export default ${JSON.stringify(latest)}`)

之後定期執行這兩個就可以了

當然,這個可以用 GitHub action 或 GitLab CI 來每週執行一次

除此之外,360 瀏覽器的檢測方法

360 瀏覽器隱藏了自己的 UA 。360 瀏覽器只有在訪問自己的網站(比如:360.cn)是纔會在 UA 裏攜帶 QIHU 360SE (360 安全瀏覽器)或 QIHU 360EE (360 極速瀏覽器)字段

360 安全瀏覽器和 360 極速瀏覽器的判斷 - V2EX[7]

對待國內瀏覽器(這個可以檢測到 360)

GitHub - mumuy/browser: Useragent analysis tool. 瀏覽器分析判斷工具 - 用戶代理、操作系統信息 [8]

不過這個庫的作者並沒有提供可以直接使用包,只能把核心代碼提取出來。

  // https://github.com/mumuy/browser/blob/4a50ee18cc76a5013dea3596bb33fbab9ed584c3/Browser.js#L111-L143
  if (_window.chrome) {
    let chrome_version = u.replace(/^.*Chrome\/([\d]+).*$/, '$1')
    if (_window.chrome.adblock2345 || _window.chrome.common2345) {
      match['2345Explorer'] = true
    } else if (
      _mime('type''application/360softmgrplugin') ||
      _mime('type''application/mozilla-npqihooquicklogin')
    ) {
      is360 = true
    } else if (chrome_version > 36 && _window.showModalDialog) {
      is360 = true
    } else if (chrome_version > 45) {
      is360 = _mime('type''application/vnd.chromium.remoting-viewer')
      if (!is360 && chrome_version >= 69) {
        is360 = _mime('type''application/hwepass2001.installepass2001') || _mime('type''application/asx')
      }
    }
  }

  // 修正
  if (match['Mobile']) {
    match['Mobile'] = !(u.indexOf('iPad') > -1)
  } else if (is360) {
    if (_mime('type''application/gameplugin')) {
      match['360SE'] = true
    } else if (
      _navigator &&
      typeof _navigator['connection'] !== 'undefined' &&
      typeof _navigator['connection']['saveData'] == 'undefined'
    ) {
      match['360SE'] = true
    } else {
      match['360EE'] = true
    }
  }

不過這裏要注意:navigator.mimeTypes 已經從 Web 標準中移除(也許未來的某天這個方法就沒法用了)

Navigator.mimeTypes - Web APIs | MDN[9]

判斷 360 的版本,是做了一個版本的對應關係

// https://github.com/mumuy/browser/blob/4a50ee18cc76a5013dea3596bb33fbab9ed584c3/Browser.js#L283-L292
function get360SEVersion(u) {
  let hash = { '86''13.0''78''12.0''69''11.0''63''10.0''55''9.1''45''8.1''42''8.0''31''7.0''21''6.3' }
  let chrome_version = u.replace(/^.*Chrome\/([\d]+).*$/, '$1')
  return hash[chrome_version] || ''
}
function get360EEVersion(u) {
  let hash = { '86''13.0''78''12.0''69''11.0''63''9.5''55''9.0''50''8.7''30''7.5' }
  let chrome_version = u.replace(/^.*Chrome\/([\d]+).*$/, '$1')
  return hash[chrome_version] || ''
}

最後

雖然我們分享瞭如何科學識別檢測用戶瀏覽器的方法,但是爲了網絡環境的健康,爲了不重蹈瀏覽器大戰時的覆轍,呼籲大家不要針對特定瀏覽器提供差異化內容。

參考資料

[1]

modernizr: https://modernizr.com/

[2]

網絡實時通信 (WebRTC) 改變了通信格局;成爲萬維網聯盟 (W3C) 建議書和多個互聯網工程任務組 (IETF) 標準: https://www.w3.org/2021/01/pressrelease-webrtc-rec.html

[3]

User-Agent - HTTP | MDN: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent

[4]

History of the browser user-agent string: https://webaim.org/blog/user-agent-string-history/

[5]

ua-parser-js: https://www.npmjs.com/package/ua-parser-js

[6]

compare-versions: https://www.npmjs.com/package/compare-versions

[7]

360 安全瀏覽器和 360 極速瀏覽器的判斷 - V2EX: https://www.v2ex.com/t/425627

[8]

GitHub - mumuy/browser: Useragent analysis tool. 瀏覽器分析判斷工具 - 用戶代理、操作系統信息: https://github.com/mumuy/browser

[9]

Navigator.mimeTypes - Web APIs | MDN: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/mimeTypes

iCSS,不止於 CSS,如果你也對各種新奇有趣的前端(CSS)知識感興趣,歡迎關注 。同時如果你有任何想法疑問,歡迎加我的微信 「****coco1s 」,一起探討!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/1cCU5lx5H4oQ5OeylRVaOg