google translate检测处于 headless 环境


前言

在爬取 google translate 的过程中发现,构造 token 的脚本在正常浏览器上可以正常使用,但是在 headless 的浏览器中不行。表现为返回了低质量的翻译结果

一开始尝试用 jsdom 来模拟完整的 google translate 页面加载发现行不通,改用 puppeteer 也不行。

初步判断是 headless 浏览器的某些特征被识别到了,毕竟使用 puppeteer 写爬虫的人太多了。

本文将梳理常见的识别 headless 浏览器的方式,便于问题定位。

img

如何识别浏览器?很多人第一反应是通过 user agent,通过 navigator.userAgent 获得。

image-20221123174145171

然而这只是最简单的一个特征,也非常容易被修改。

事实上,不同的浏览器之间因为实现不同,会存在非常多彼此不一样的接口实现,如:

  • only IE has ActiveX support, objects Debug, document.security, navigator.cpuClassand dozens of others,
  • only FF and Safari have MathML support,
  • only chrome has shape detection api ,
  • only in Chrome and FF there is support for the webM codec in the tag video,

同理,headless 浏览器和 chrome 也会存在不同的接口实现,体现在 window 或 navigator 的差异上。

还有一种方式是通过 reCAPTCHA v3 score,这个接口会针对当前环境打分.

分值越接近 1 代表越接近真实用户

image-20221123174156521

//window.chrome

//https://github.com/berstend/puppeteer-extra/blob/master/packages/puppeteer-extra-plugin-stealth/evasions/chrome.app/index.test.js

(()=>{ const catchErr = (fn, ...args) => {
   try {
    return fn.apply(this, args)
   } catch ({ name, message, stack }) {
    return { name, message, stack }
   }
  }

  return {
   app: {
    exists: window.chrome && 'app' in window.chrome,
    toString: chrome.app.toString(),
    deepToString: chrome.app.runningState.toString()
   },
   data: {
    getIsInstalled: chrome.app.getIsInstalled(),
    runningState: chrome.app.runningState(),
    getDetails: chrome.app.getDetails(),
    InstallState: chrome.app.InstallState,
    RunningState: chrome.app.RunningState
   },
   errors: {
    getIsInstalled: catchErr(chrome.app.getDetails, 'foo').message,
    stackOK: !catchErr(chrome.app.getDetails, 'foo').stack.includes(
     'at getDetails'
    )
   }
  }})()



 {
  app: {
   exists: true,
   toString: '[object Object]',
   deepToString: 'function getDetails() { [native code] }'
  },
  data: {
   InstallState: {
    DISABLED: 'disabled',
    INSTALLED: 'installed',
    NOT_INSTALLED: 'not_installed'
   },
   RunningState: {
    CANNOT_RUN: 'cannot_run',
    READY_TO_RUN: 'ready_to_run',
    RUNNING: 'running'
   },
   getDetails: null,
   getIsInstalled: false,
   runningState: 'cannot_run'
  },
  errors: {
   getIsInstalled: 'Error in invocation of app.getDetails()',
   stackOK: true
  }
 }

Author: Lic
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source Lic !
  TOC