前端埋点日志解决方案:完整 User-Agent 解析工具实现,爬虫识别、设备品牌、操作系统、浏览器版本全解析(附完整源码) 做Web开发、日志分析、风控拦截、用户运营的开发者大概率都踩过User-Agent解析的坑看似简单的一串UA字符串藏着无数难题杂乱无章的字符难以识别设备、主流爬虫伪装真实用户无法甄别、开源库识别率低、小众设备直接返回「未知」、系统版本匹配错乱……市面上多数通用UA解析库要么规则老旧长期不更新要么识别维度单一无法适配国内海量机型、小众系统以及各类爬虫伪装场景导致日志数据失真、风控误判、用户画像偏差、适配逻辑失效等一系列线上问题。今天给大家分享一套自研全能BrowserInfo解析工具基于Node.js开发无多余依赖、规则全面、识别精度拉满完美解决爬虫识别、设备品牌解析、操作系统匹配、浏览器版本拆解等核心痛点开箱即用适配绝大多数线上业务场景一、为什么不推荐通用开源UA解析库深耕Web日志解析与设备识别场景多年总结出通用解析工具的三大核心痛点也是绝大多数项目的共性难题1.爬虫识别缺失伪装流量无法甄别百度、字节、谷歌、必应、社交平台等各类爬虫会伪装成正常用户UA访问站点通用库无法精准识别导致统计流量虚高、真实用户数据被污染SEO分析、流量统计完全失真。2.国产设备/系统识别率极低通用库大多适配海外机型对华为、小米、OPPO、VIVO、荣耀、一加等国产主流手机统信UOS、鸿蒙生态等国产系统识别不全大量设备被统一归类为「Android」「未知」设备精细化分析无从谈起。3.解析维度单一数据杂乱不规范多数工具仅能简单识别浏览器和系统无法区分桌面端设备品牌、移动端具体机型系统版本映射混乱日志字段残缺需要二次加工大幅增加开发工作量。4.容错性差线上易报错针对空UA、残缺UA、非常规格式UA字符串通用库极易解析失败、抛出异常影响整体业务接口稳定性。二、自研解析工具核心能力全方位碾压通用方案这套工具针对线上真实场景打磨优化补齐了行业通用短板覆盖爬虫识别、设备品牌、设备名称、操作系统、浏览器信息五大核心解析能力适配PC、移动端、爬虫全场景精度、兼容性、稳定性全面升级。✅ 能力1全覆盖爬虫精准识别杜绝流量污染内置18主流爬虫专属匹配规则覆盖国内外主流搜索引擎、AI爬虫、社交平台爬虫百度、字节跳动、谷歌、必应、苹果、雅虎、搜狗、360、神马、Yandex、Claude、Facebook、Twitter等。不仅可以精准匹配爬虫名称还能智能提取爬虫版本号同时配置通用爬虫兜底规则拦截未知伪装爬虫彻底过滤虚假流量保障日志统计、流量分析数据真实有效。✅ 能力2全品牌设备识别告别「未知」设备深度适配国内外全品类设备品牌重点补齐国产设备识别短板▪ 移动端华为、荣耀、小米、红米、OPPO、一加、真我、VIVO、iQOO、三星、魅族、中兴、努比亚、锤子等全品牌适配▪ 桌面端精准识别联想、戴尔、惠普、华硕、宏碁、微软、华为、小米等PC品牌▪ 小众设备兼容TCL、酷派、乐视、金立等小众机型无遗漏、不归类同时支持桌面系统精准区分Windows、macOS、Chrome OS、统信UOS、Linux各发行版精准匹配彻底解决设备识别模糊问题。✅ 能力3精细化设备名称解析获取真实机型区别于通用工具的粗略归类本工具可从杂乱UA字符串中智能清洗、提取用户真实设备型号名称自动过滤wv后缀、Build冗余字段、多余空格格式化输出标准化设备名称。优先复用原始有效设备名无效则自动解析补齐兼顾准确性与兼容性为用户画像、设备适配、故障溯源提供精准数据支撑。✅ 能力4系统版本精准映射告别版本混乱精准解析iOS、Android、Windows、macOS、Chrome OS、统信UOS、Linux全系列操作系统内置专属版本映射规则▪ Windows系统智能版本转换将NT版本号自动映射为通俗易懂的Win7/8/10/11版本名称▪ iOS、macOS自动替换下划线为标准小数点版本格式▪ 精准识别统信UOS专业版、桌面版、服务器版等细分类型解析结果规范统一无需二次处理直接用于数据统计、业务适配、版本兼容判断。✅ 能力5全场景浏览器识别覆盖内外置应用打破通用工具仅支持主流浏览器的局限不仅适配Chrome、Safari、Edge、Firefox等常规浏览器还全覆盖国内主流内置浏览器微信、QQ、UC、百度、支付宝、钉钉、企业微信、小程序WebView。同时兼容海外社交平台客户端内核识别精准区分TikTok、YouTube、Facebook、Instagram等应用内置访问场景全方位适配移动端生态。✅ 能力6高容错高稳定线上零风险内置完整异常捕获机制针对空UA、无效UA、残缺UA、异常格式UA等极端场景不会抛出报错、不影响业务流程自动兜底返回默认值完美适配高并发线上生产环境。三、核心业务适用场景赋能各类开发需求这套解析工具实用性极强几乎适配所有需要解析客户端信息的Web业务场景1.用户行为日志分析精准统计不同设备、系统、浏览器的用户访问数据剔除爬虫流量数据更真实可信2.前端自适应适配根据设备、系统、浏览器类型动态适配页面样式、功能开关、兼容逻辑3.风控安全拦截精准识别爬虫、恶意伪装访问助力接口风控、防刷、防爬虫策略落地4.精细化用户运营基于设备品牌、机型、系统版本构建用户画像支撑精准运营、场景化推送5.线上问题溯源根据用户设备、浏览器信息快速复现、定位机型兼容、版本适配类线上BUG四、接入极简开箱即用零成本工具基于原生Node.js开发零第三方依赖、轻量无侵入支持按需引入所有方法接入流程极简无需复杂配置。核心导出方法全覆盖爬虫检测、设备品牌解析、设备名称解析、操作系统与版本解析、浏览器信息解析、一键批量解析日志信息。支持单条UA解析、批量日志解析兼容服务端所有Node.js项目接入后可直接替换老旧解析方案快速提升数据精度。五、总结告别解析凑活用专业工具解决专业问题多数项目的UA解析问题从来不是技术难度高而是通用工具规则老旧、场景适配不足、细节处理粗糙。这套自研BrowserInfo解析工具经过线上真实业务打磨补齐了国产设备、小众系统、各类爬虫、内置浏览器的识别短板以高精度、高兼容、高稳定、低侵入的优势彻底解决UA解析的各类玄学问题。如果你也被设备识别不准、爬虫流量干扰、日志数据混乱、适配兼容繁琐等问题困扰这套工具绝对是最优替代方案工具特性速览✅ 18主流爬虫精准识别拦截伪装虚假流量✅ 全品类国产海外设备品牌全覆盖✅ 精细化设备机型清洗解析标准化输出✅ 全系统版本智能映射格式统一规范✅ 主流浏览器国内内置浏览器全覆盖✅ 零依赖、高容错、线上稳定无报错✅ 开箱即用极简接入适配所有Node.js项目Webfunny全链路监控埋点平台 是一站式前端监控 用户行为埋点 大数据分析平台天然适配点位细查、用户行为回溯、批量导出等场景一体化架构监控 埋点同一套 SDK数据互通无壁垒私有化部署数据完全本地化满足企业合规要求高吞吐支撑基于 ClickHouse 构建亿级日志秒级查询全端覆盖H5 / 小程序 / APP / 鸿蒙全覆盖统一导出口径可定制强支持接口扩展、分布式锁、限流降级等企业级能力附源码/** * browserInfo 解析工具 * 用于从 User Agent 中解析设备品牌、设备名称、操作系统、浏览器名称等信息 */ /** * 爬虫匹配规则列表 */ const SPIDER_PATTERNS [ { pattern: /baiduspider(-render)?\/?([\d.])?/i, name: 百度爬虫 }, { pattern: /bytespider\/?([\d.])?/i, name: 字节跳动爬虫 }, { pattern: /googlebot\/?([\d.])?/i, name: 谷歌爬虫 }, { pattern: /bingbot\/?([\d.])?/i, name: 必应爬虫 }, { pattern: /applebot\/?([\d.])?/i, name: 苹果爬虫 }, { pattern: /slurp\/?([\d.])?/i, name: 雅虎爬虫 }, { pattern: /sosowebspider\/?([\d.])?/i, name: 搜搜爬虫 }, { pattern: /sogou\sweb\sspider\/?([\d.])?/i, name: 搜狗爬虫 }, { pattern: /360spider\/?([\d.])?/i, name: 360爬虫 }, { pattern: /yisouspider\/?([\d.])?/i, name: 神马搜索爬虫 }, { pattern: /yandexbot\/?([\d.])?/i, name: Yandex爬虫 }, { pattern: /ahrefsbot\/?([\d.])?/i, name: Ahrefs爬虫 }, { pattern: /bitsightbot\/?([\d.])?/i, name: BitSight爬虫 }, { pattern: /claudebot\/?([\d.])?/i, name: Claude爬虫 }, { pattern: /facebookexternalhit\/?([\d.])?/i, name: Facebook爬虫 }, { pattern: /twitterbot\/?([\d.])?/i, name: Twitter爬虫 }, { pattern: /linkedinbot\/?([\d.])?/i, name: LinkedIn爬虫 }, { pattern: /(spider|crawler|bot)\/?[\d.]*?/i, name: 爬虫 } ] /** * 检测是否为爬虫 * param {string} userAgent - User Agent 字符串 * returns {object|null} 匹配的爬虫信息包含 name 和 version */ function detectSpider(userAgent) { if (!userAgent) return null const agent userAgent.toLowerCase() for (let spiderPattern of SPIDER_PATTERNS) { const spiderMatch agent.match(spiderPattern.pattern) if (spiderMatch) { let version spiderMatch[2] || spiderMatch[1] || if (!version spiderMatch[0]) { const versionMatch spiderMatch[0].match(/([\d.])/) version versionMatch ? versionMatch[1] : } return { name: spiderPattern.name, version } } } return null } /** * 根据浏览器信息判断设备品牌 * param {string} browserInfo - 浏览器信息User Agent * returns {string} 设备品牌或系统名称 */ function getPhoneBrandFromBrowserInfo(browserInfo) { if (!browserInfo) { return 未知 } const userAgent browserInfo.toLowerCase() // 先判断是否是桌面端系统 const isDesktop /(windows nt|macintosh|mac os x|linux.*x86|linux.*x64)/i.test(userAgent) !/android|mobile/i.test(userAgent) if (isDesktop) { // 桌面端系统 - 尝试识别品牌或返回系统名称 // Apple Mac系列 if (/macintosh|mac os x/i.test(userAgent)) { return Apple } // Windows系统 - 尝试识别品牌通常UA中无法识别返回系统名称 if (/windows nt/i.test(userAgent)) { // 尝试识别一些可能的品牌标识 if (/lenovo/i.test(userAgent)) { return 联想 } if (/dell/i.test(userAgent)) { return 戴尔 } if (/hp|hewlett/i.test(userAgent)) { return 惠普 } if (/asus/i.test(userAgent)) { return 华硕 } if (/acer/i.test(userAgent)) { return 宏碁 } if (/surface/i.test(userAgent)) { return 微软 } if (/thinkpad/i.test(userAgent)) { return 联想 } if (/huawei|matebook/i.test(userAgent)) { return 华为 } if (/xiaomi/i.test(userAgent)) { return 小米 } if (/honor/i.test(userAgent)) { return 荣耀 } // 无法识别具体品牌返回系统名称 return Windows } // Chrome OS - 需要单独检测因为 Chrome OS 的 UA 不包含 linux if (/cros/i.test(userAgent)) { // Chrome OS 设备的 UA 中通常不包含品牌信息但尝试识别一些可能的品牌 // 注意大多数 Chromebook 的 UA 中不包含品牌所以通常返回 Chrome OS const uaUpper browserInfo.toUpperCase() if (/ACER|CHROMEBOOK.*ACER/i.test(uaUpper)) { return 宏碁 } if (/ASUS|CHROMEBOOK.*ASUS/i.test(uaUpper)) { return 华硕 } if (/DELL|CHROMEBOOK.*DELL/i.test(uaUpper)) { return 戴尔 } if (/HP|HEWLETT|CHROMEBOOK.*HP/i.test(uaUpper)) { return 惠普 } if (/LENOVO|CHROMEBOOK.*LENOVO/i.test(uaUpper)) { return 联想 } if (/SAMSUNG|CHROMEBOOK.*SAMSUNG/i.test(uaUpper)) { return 三星 } if (/TOSHIBA|CHROMEBOOK.*TOSHIBA/i.test(uaUpper)) { return 东芝 } // 大多数情况下Chrome OS 设备的 UA 中不包含品牌信息 // 返回 Chrome OS 作为统一的品牌标识 return Chrome OS } // Linux系统 if (/linux/i.test(userAgent) !/android/i.test(userAgent)) { // 统信 UOS国产操作系统 if (/uos/i.test(userAgent)) { return 统信 } // Ubuntu if (/ubuntu/i.test(userAgent)) { return Ubuntu } // Fedora if (/fedora/i.test(userAgent)) { return Fedora } // 通用Linux return Linux } return PC } // iPhone系列 if (/iphone|ipad|ipod/i.test(userAgent)) { return Apple } // 华为系列包括Honor荣耀 if (/huawei|honor|harmonyos|hms/i.test(userAgent)) { return 华为 } // OPPO系列包括内部型号 if (/oppo|oneplus|realme|pkg\d|pkd\d/i.test(userAgent)) { return OPPO } // 三星系列 if (/samsung|sm-|galaxy/i.test(userAgent)) { return 三星 } // 魅族系列 if (/meizu|flyme/i.test(userAgent)) { return 魅族 } // 一加系列 if (/oneplus|pj[a-z]\d{3}/i.test(userAgent)) { return 一加 } // 中兴系列 if (/zte|blade/i.test(userAgent)) { return 中兴 } // 联想系列 if (/lenovo|moto/i.test(userAgent)) { return 联想 } // TCL系列 if (/tcl/i.test(userAgent)) { return TCL } // 努比亚系列 if (/\bnx|nubia/i.test(userAgent)) { return 努比亚 } // 酷派系列 if (/coolpad/i.test(userAgent)) { return 酷派 } // 锤子科技坚果手机 if (/smartisan|nut/i.test(userAgent)) { return 锤子 } // 360手机 if (/360/i.test(userAgent) /mobile/i.test(userAgent)) { return 360手机 } // 乐视手机 if (/letv|leeco/i.test(userAgent)) { return 乐视 } // 金立手机 if (/gionee/i.test(userAgent)) { return 金立 } // VIVO系列 if (/vivo|iqoo|v\d{4}[a-z]*/i.test(userAgent)) { return VIVO } // 小米系列包括Redmi红米 if (/\bmi\b|redmi|miui|xiaomi|2\d{4}[a-z0-9]/i.test(userAgent)) { return 小米 } // 如果是Android但没有匹配到具体品牌 if (/android/i.test(userAgent)) { return Android } // 如果都没有匹配到 return 其他 } /** * 根据浏览器信息获取设备名称 * param {string} browserInfo - 浏览器信息User Agent * param {string} originDeviceName - 原始设备名称 * returns {string} 设备名称 */ function getDeviceNameFromBrowserInfo(browserInfo, originDeviceName) { if (!browserInfo) { return originDeviceName || 未知 } const ua browserInfo const agent ua.toLowerCase() // 爬虫检测 const spider detectSpider(agent) if (spider) { return spider.name } // 如果原始设备名称有效直接返回 if (originDeviceName originDeviceName ! undefined originDeviceName ! PC originDeviceName.trim() ! ) { return originDeviceName } const android ua.match(/(Android);?[\s\/]([\d.])?/) const ipad ua.match(/(iPad).*OS\s([\d_])/) const iphone !ipad ua.match(/(iPhone\sOS)\s([\d_])/) const mobileInfo ua.match(/Android\s[\S\s]Build\//) // iOS设备 if (iphone) { return iPhone } if (ipad) { return iPad } // Android设备 if (android) { let deviceName // 方法1处理现代格式 const modernAndroidMatch ua.match(/\(Linux;\s*Android\s[\d.];\s*([^)])\)/i) if (modernAndroidMatch modernAndroidMatch[1]) { deviceName modernAndroidMatch[1].trim() deviceName deviceName.replace(/;\s*wv$/, ).trim() } // 方法2尝试Build格式 if (!deviceName mobileInfo) { const info mobileInfo[0] const dNameArr info.split(;) for (let i 0; i dNameArr.length; i) { if (dNameArr[i].indexOf(Build) ! -1) { deviceName dNameArr[i].replace(/Build\//g, ) break } } if (deviceName dNameArr.length 1) { deviceName dNameArr[1] } } // 方法3其他Android格式 if (!deviceName) { const otherAndroidMatch ua.match(/Android[\s\/][\d.][;\s]([^;)])/i) if (otherAndroidMatch otherAndroidMatch[1]) { deviceName otherAndroidMatch[1].trim() } } // 清理设备名称 if (deviceName) { deviceName deviceName.replace(/(^\s*)|(\s*$)/g, ) deviceName deviceName.replace(/Build\//g, ) deviceName deviceName.replace(/;\s*wv$/, ) } return deviceName || Android } // 桌面端 if (/(windows nt|macintosh|mac os x|linux)/i.test(agent) !/android|mobile/i.test(agent)) { return PC } return originDeviceName || 未知 } /** * 根据浏览器信息获取操作系统和版本号 * param {string} browserInfo - 浏览器信息(User Agent) * param {string} originOs - 原始操作系统 * param {string} originOsVersion - 原始操作系统版本 * returns {object} { os: 操作系统名称, osVersion: 版本号 } */ function getOsFromBrowserInfo(browserInfo, originOs, originOsVersion) { if (!browserInfo) { return { os: originOs || 未知, osVersion: originOsVersion || } } const ua browserInfo const agent ua.toLowerCase() // 爬虫检测 const spider detectSpider(agent) if (spider) { return { os: spider.name, osVersion: spider.version } } // 如果原始os有效且不是web,直接返回 if (originOs originOs ! web originOs.trim() ! originOsVersion originOsVersion.trim() ! ) { return { os: originOs, osVersion: originOsVersion || } } // iOS const iosMatch ua.match(/(iPhone|iPad|iPod).*?(?:iPhone\s)?OS\s([\d_])/i) if (iosMatch) { const version iosMatch[2].replace(/_/g, .) return { os: iOS, osVersion: version } } // Android const androidMatch ua.match(/Android\s([\d.])/i) if (androidMatch) { return { os: Android, osVersion: androidMatch[1] } } // Windows const windowsMatch ua.match(/Windows NT ([\d.])/i) if (windowsMatch) { const versionMap { 10.0: 10/11, 6.3: 8.1, 6.2: 8, 6.1: 7, 6.0: Vista, 5.1: XP, 5.0: 2000 } const version versionMap[windowsMatch[1]] || windowsMatch[1] return { os: Windows, osVersion: version } } // macOS const macMatch ua.match(/Mac OS X ([\d_])/i) if (macMatch) { const version macMatch[1].replace(/_/g, .) return { os: macOS, osVersion: version } } // Chrome OS const chromeOSMatch ua.match(/CrOS\s(?:x86_64|armv7l|aarch64)\s([\d.])/i) if (chromeOSMatch) { return { os: Chrome OS, osVersion: chromeOSMatch[1] } } // 统信 UOS国产操作系统 const uosMatch ua.match(/UOS\s(?:Professional|Desktop|Server|Enterprise)?/i) if (uosMatch) { // 尝试提取版本信息UOS UA 中可能包含版本号 const uosVersionMatch ua.match(/UOS\s(?:Professional|Desktop|Server|Enterprise)\s*([\d.])?/i) const version uosVersionMatch uosVersionMatch[1] ? uosVersionMatch[1] : // 如果没有版本号尝试提取类型Professional/Desktop/Server/Enterprise if (!version uosMatch[0]) { const typeMatch uosMatch[0].match(/(Professional|Desktop|Server|Enterprise)/i) return { os: 统信 UOS, osVersion: typeMatch ? typeMatch[1] : } } return { os: 统信 UOS, osVersion: version } } // Linux if (/linux/i.test(agent) !/android/i.test(agent)) { return { os: Linux, osVersion: } } return { os: originOs || 未知, osVersion: originOsVersion || } } /** * 根据浏览器信息获取浏览器名称和版本 * param {string} browserInfo - 浏览器信息User Agent * param {string} originBrowserName - 原始浏览器名称 * param {string} originBrowserVersion - 原始浏览器版本 * returns {object} { browserName, browserVersion } */ function getBrowserInfoFromUA(browserInfo, originBrowserName, originBrowserVersion) { if (!browserInfo) { return { browserName: originBrowserName || 未知, browserVersion: originBrowserVersion || } } const agent browserInfo.toLowerCase() // 爬虫检测 - 优先检测爬虫 const spider detectSpider(agent) if (spider) { return { browserName: spider.name, browserVersion: spider.version } } // 如果原始浏览器名称有效直接返回 if (originBrowserName originBrowserName ! 其他 originBrowserName ! undefined originBrowserName.trim() ! ) { return { browserName: originBrowserName, browserVersion: originBrowserVersion || } } let browserName 未知 let browserVersion // 微信浏览器 if (agent.indexOf(micromessenger) 0) { const wechatMatch agent.match(/micromessenger\/([\d.])/i) browserName 微信 browserVersion wechatMatch wechatMatch[1] ? wechatMatch[1] : } // QQ浏览器 else if (agent.indexOf(mqqbrowser) 0) { const qqMatch agent.match(/mqqbrowser\/([\d.])/i) browserName QQ browserVersion qqMatch qqMatch[1] ? qqMatch[1] : } // UC浏览器 else if (agent.indexOf(ucbrowser) 0 || agent.indexOf(ubrowser) 0) { const ucMatch agent.match(/u?c?browser\/([\d.])/i) browserName UC browserVersion ucMatch ucMatch[1] ? ucMatch[1] : } // 百度浏览器 else if (agent.indexOf(baidubrowser) 0) { const baiduMatch agent.match(/baidubrowser\/([\d.])/i) browserName 百度 browserVersion baiduMatch baiduMatch[1] ? baiduMatch[1] : } // 支付宝内置浏览器 else if (agent.indexOf(alipayclient) 0 || agent.indexOf(alipay) 0) { const alipayMatch agent.match(/alipay(?:client)?\/([\d.])/i) browserName 支付宝 browserVersion alipayMatch alipayMatch[1] ? alipayMatch[1] : } // 钉钉内置浏览器 else if (agent.indexOf(dingtalk) 0) { const dingMatch agent.match(/dingtalk\/([\d.])/i) browserName 钉钉 browserVersion dingMatch dingMatch[1] ? dingMatch[1] : } // 企业微信 else if (agent.indexOf(wxwork) 0) { const wxworkMatch agent.match(/wxwork\/([\d.])/i) browserName 企业微信 browserVersion wxworkMatch wxworkMatch[1] ? wxworkMatch[1] : } // 小程序WebView else if (agent.indexOf(miniprogram) 0) { browserName 小程序 browserVersion } // iOS 第三方应用需要在 Safari 检测之前 else if (agent.indexOf(twitter) 0) { const twitterMatch agent.match(/twitter\sfor\s(?:iphone|ipad)\/([\d.])/i) browserName Twitter browserVersion twitterMatch twitterMatch[1] ? twitterMatch[1] : } else if (agent.indexOf(facebook) 0 agent.indexOf(fban) 0) { const fbMatch agent.match(/fban\/([\d.])/i) || agent.match(/fbios\/([\d.])/i) browserName Facebook browserVersion fbMatch fbMatch[1] ? fbMatch[1] : } else if (agent.indexOf(instagram) 0) { const instagramMatch agent.match(/instagram\s*([\d.])/i) browserName Instagram browserVersion instagramMatch instagramMatch[1] ? instagramMatch[1] : } else if (agent.indexOf(linkedinapp) 0) { const linkedinMatch agent.match(/linkedinapp\/([\d.])/i) browserName LinkedIn browserVersion linkedinMatch linkedinMatch[1] ? linkedinMatch[1] : } else if (agent.indexOf(line) 0 agent.indexOf(line/) 0) { const lineMatch agent.match(/line\/([\d.])/i) browserName Line browserVersion lineMatch lineMatch[1] ? lineMatch[1] : } else if (agent.indexOf(weibo) 0) { const weiboMatch agent.match(/weibo\s*([\d.])/i) || agent.match(/weibo__([\d.])/i) browserName 微博 browserVersion weiboMatch weiboMatch[1] ? weiboMatch[1] : } else if (agent.indexOf(youtube) 0) { const youtubeMatch agent.match(/youtube\/([\d.])/i) browserName YouTube browserVersion youtubeMatch youtubeMatch[1] ? youtubeMatch[1] : } else if (agent.indexOf(tiktok) 0) { const tiktokMatch agent.match(/tiktok\/([\d.])/i) browserName TikTok browserVersion tiktokMatch tiktokMatch[1] ? tiktokMatch[1] : } // IE else if (agent.indexOf(msie) 0) { const ieMatch agent.match(/msie ([\d.])/i) browserName ie browserVersion ieMatch ieMatch[1] ? ieMatch[1].replace(;, ) : } // Edge else if (agent.indexOf(edg) 0) { const edgeMatch agent.match(/edg\/([\d.])/i) browserName edge browserVersion edgeMatch edgeMatch[1] ? edgeMatch[1] : } // Firefox else if (agent.indexOf(firefox) 0) { const ffMatch agent.match(/firefox\/([\d.])/i) browserName firefox browserVersion ffMatch ffMatch[1] ? ffMatch[1] : } // Safari (不是Chrome) else if (agent.indexOf(safari) 0 agent.indexOf(chrome) 0) { const safariMatch agent.match(/version\/([\d.])/i) || agent.match(/safari\/([\d.])/i) browserName safari browserVersion safariMatch safariMatch[1] ? safariMatch[1] : } // Chrome else if (agent.indexOf(chrome) 0) { const chromeMatch agent.match(/chrome\/([\d.])/i) browserName chrome browserVersion chromeMatch chromeMatch[1] ? chromeMatch[1] : // 检测360浏览器 if (agent.indexOf(360se) 0) { const browser360Match agent.match(/360se\/([\d.])/i) browserName 360 browserVersion browser360Match browser360Match[1] ? browser360Match[1] : browserVersion } } return { browserName, browserVersion } } /** * 重新解析CUSTOMER_PV日志的browserInfo * param {object} logInfo - 日志对象 * param {string} browserInfo - 浏览器UA信息 * returns {object} 更新后的logInfo */ function parseBrowserInfo(logInfo, browserInfo) { if (!browserInfo) { return logInfo } try { // 解析手机品牌 weDeviceBrand logInfo.weDeviceBrand logInfo.weDeviceBrand ? logInfo.weDeviceBrand : getPhoneBrandFromBrowserInfo(browserInfo) // 解析设备名称 weDeviceName const originDeviceName logInfo.weDeviceName if (!originDeviceName || originDeviceName undefined || originDeviceName PC || originDeviceName.trim() ) { logInfo.weDeviceName getDeviceNameFromBrowserInfo(browserInfo, originDeviceName) } // 解析操作系统 os 和 osVersion const originOs logInfo.weOs const originOsVersion logInfo.weOsVersion if (!originOs || originOs web || originOs.trim() || !originOsVersion || originOsVersion.trim() ) { const { os, osVersion } getOsFromBrowserInfo(browserInfo, originOs, originOsVersion) logInfo.weOs os logInfo.weOsVersion osVersion } // 解析浏览器名称 browserName 和版本 browserVersion const originBrowserName logInfo.weBrowserName const originBrowserVersion logInfo.weBrowserVersion if (!originBrowserName || originBrowserName 其他 || originBrowserName undefined || originBrowserName.trim() ) { const { browserName, browserVersion } getBrowserInfoFromUA(browserInfo, originBrowserName, originBrowserVersion) logInfo.weBrowserName browserName logInfo.weBrowserVersion browserVersion } } catch (e) { console.log(parseBrowserInfo error:, e) } return logInfo } module.exports { detectSpider, getPhoneBrandFromBrowserInfo, getDeviceNameFromBrowserInfo, getOsFromBrowserInfo, getBrowserInfoFromUA, parseBrowserInfo }