【问题标题】:Selenium webdriver: Modifying navigator.webdriver flag to prevent selenium detectionSelenium webdriver:修改 navigator.webdriver 标志以防止硒检测
【发布时间】:2019-04-02 00:48:38
【问题描述】:

我正在尝试使用 selenium 和 chrome 在网站中自动执行一项非常基本的任务,但网站以某种方式检测到 chrome 何时由 selenium 驱动并阻止每个请求。我怀疑该网站依赖于像 https://stackoverflow.com/a/41904453/648236 这样的暴露 DOM 变量来检测硒驱动的浏览器。

我的问题是,有没有办法可以使 navigator.webdriver 标志为假?我愿意在进行修改后尝试重新编译 selenium 源,但我似乎无法在存储库中的任何地方找到 NavigatorAutomationInformation 源https://github.com/SeleniumHQ/selenium

非常感谢任何帮助

P.S:我还尝试了https://w3c.github.io/webdriver/#interface的以下内容

Object.defineProperty(navigator, 'webdriver', {
    get: () => false,
  });

但它只在初始页面加载后更新属性。我认为网站会在我的脚本执行之前检测到变量。

【问题讨论】:

    标签: java selenium selenium-webdriver webdriver webdriver-w3c-spec


    【解决方案1】:

    首先更新1

    execute_cdp_cmd():现在有了execute_cdp_cmd(cmd, cmd_args) 命令,您可以使用Selenium 轻松执行 commands。使用此功能,您可以轻松修改 navigator.webdriver 以防止 Selenium 被检测到。


    防止检测2

    为了防止 Selenium 驱动的 WebDriver 被检测到,一种利基方法将包括以下任一/所有步骤:

    • 添加参数--disable-blink-features=AutomationControlled

      from selenium import webdriver
      
      options = webdriver.ChromeOptions() 
      options.add_argument('--disable-blink-features=AutomationControlled')
      driver = webdriver.Chrome(options=options, executable_path=r'C:\WebDrivers\chromedriver.exe')
      driver.get("https://www.website.com")
      

    您可以在Selenium can't open a second page找到相关的详细讨论

    • 转至execute_cdp_cmd() 命令如下:

      #Setting up Chrome/83.0.4103.53 as useragent
      driver.execute_cdp_cmd('Network.setUserAgentOverride', {"userAgent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.53 Safari/537.36'})
      
    • webdrivernavigatorproperty 值更改为 undefined

      driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
      
    • 排除enable-automation开关的集合

      options.add_experimental_option("excludeSwitches", ["enable-automation"])
      
    • 关闭useAutomationExtension

      options.add_experimental_option('useAutomationExtension', False)
      

    示例代码3

    合并上述所有步骤和有效的代码块将是:

    from selenium import webdriver
    
    options = webdriver.ChromeOptions() 
    options.add_argument("start-maximized")
    options.add_experimental_option("excludeSwitches", ["enable-automation"])
    options.add_experimental_option('useAutomationExtension', False)
    driver = webdriver.Chrome(options=options, executable_path=r'C:\WebDrivers\chromedriver.exe')
    driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
    driver.execute_cdp_cmd('Network.setUserAgentOverride', {"userAgent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.53 Safari/537.36'})
    print(driver.execute_script("return navigator.userAgent;"))
    driver.get('https://www.httpbin.org/headers')
    

    历史

    根据W3C Editor's Draft,当前的实现严格提到:

    user agent 下时,webdriver-active flag 设置为 true >遥控器,初始设置为false

    进一步,

    Navigator includes NavigatorAutomationInformation;
    

    需要注意的是:

    NavigatorAutomationInformation接口不应暴露在 WorkerNavigator 上。

    NavigatorAutomationInformation接口定义为:

    interface mixin NavigatorAutomationInformation {
        readonly attribute boolean webdriver;
    };
    

    如果设置了webdriver-active flag,则返回 true,否则返回 false。

    最后,navigator.webdriver 定义了一种标准方式,用于协作用户代理通知文档它由 WebDriver 控制,以便在自动化过程中触发替代代码路径。 p>

    警告:更改/调整上述参数可能会阻塞导航并检测到WebDriver实例。


    更新(2019 年 11 月 6 日)

    就目前的实现而言,在不被检测到的情况下访问网页的理想方法是使用 ChromeOptions() 类添加几个参数:

    • 排除enable-automation开关的集合
    • 关机useAutomationExtension

    通过ChromeOptions的实例如下:

    • Java 示例:

      System.setProperty("webdriver.chrome.driver", "C:\\Utility\\BrowserDrivers\\chromedriver.exe");
      ChromeOptions options = new ChromeOptions();
      options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
      options.setExperimentalOption("useAutomationExtension", false);
      WebDriver driver =  new ChromeDriver(options);
      driver.get("https://www.google.com/");
      
    • Python 示例

      from selenium import webdriver
      
      options = webdriver.ChromeOptions()
      options.add_experimental_option("excludeSwitches", ["enable-automation"])
      options.add_experimental_option('useAutomationExtension', False)
      driver = webdriver.Chrome(options=options, executable_path=r'C:\path\to\chromedriver.exe')
      driver.get("https://www.google.com/")
      
    • Ruby 示例

        options = Selenium::WebDriver::Chrome::Options.new
        options.add_argument("--disable-blink-features=AutomationControlled")
        driver = Selenium::WebDriver.for :chrome, options: options
      

    传说

    1:仅适用于 Selenium 的 Python 客户端。

    2:仅适用于 Selenium 的 Python 客户端。

    3:仅适用于 Selenium 的 Python 客户端。

    【讨论】:

    • 那么我们该怎么做呢?对不起,我没有明白这一点。我正在使用 python 客户端。
    • @DebanjanB 我还应该使用--disable-blink-features--disable-blink-features=AutomationControlled吗?
    • navigator.webdriver 在打开新页面时仍然是true
    • 这不再起作用了。他们修补了开关“enable-automation”和“useAutomationExtension”,现在它们的行为正确,将 navigator.wedriver 显示为 true。此外,我无法确定这部分从未起作用: driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
    • 1 月 21 日,这些技术都不再起作用了。我感觉在 chrome 更新后一切都停止了。
    【解决方案2】:

    ChromeDriver

    终于用一个简单的标志找到了简单的解决方案! :)

    --disable-blink-features=AutomationControlled
    

    navigator.webdriver=true 将不再显示该标志集。

    有关您可以禁用的功能列表,check them out here

    【讨论】:

    • 天啊!!它在 chromedriver 83 中对我有用。我尝试了很多解决方案,但这是唯一一个完美运行的解决方案。我使用 chromeOptions.add_argument 添加了 python。谢谢
    • 绝对精彩! ...对于任何使用 symfony/panther 和 chrome 的人,您可以通过设置 PANTHER_CHROME_ARGUMENTS="--disable-dev-shm-usage --disable-blink-features=AutomationControlled --window-size=1920x1080"
    • 非常感谢!! python中的代码:options.add_argument("--disable-blink-features=AutomationControlled")
    • 嗨,谁能给我发送确切的代码行以避免检测?谢谢
    • 效果很好。但现在无头不起作用。有什么帮助吗?
    【解决方案3】:

    不要使用 cdp 命令更改 webdriver 的值,因为这会导致不一致,以后可以用来检测 webdriver。使用下面的代码,这将删除 webdriver 的任何痕迹。

    options.add_argument("--disable-blink-features")
    options.add_argument("--disable-blink-features=AutomationControlled")
    

    【讨论】:

    • 我使用 Chrome 88.0.4324.150 ,工作正常。谢谢。
    • @Baki Billah 你能帮我在 webdriverio 上做同样的事情吗?我尝试添加 args: ['enable-automation', '--disable-blink-features=AutomationControlled', '--disable-blink-features'] 并没有帮助。
    【解决方案4】:

    之前(在浏览器控制台窗口中):

    > navigator.webdriver
    true
    

    变化(硒):

    // C#
    var options = new ChromeOptions();
    options.AddExcludedArguments(new List<string>() { "enable-automation" });
    
    // Python
    options.add_experimental_option("excludeSwitches", ['enable-automation'])
    

    之后(在浏览器控制台窗口中):

    > navigator.webdriver
    undefined
    

    这不适用于 ChromeDriver 79.0.3945.16 及更高版本。请参阅发行说明here

    【讨论】:

    • 如何在 Firefox 中做同样的事情? stackoverflow.com/questions/57122151/…
    • 我也可以确认这一点——在我看来——不再有效。未成功更改 navigator.webdriver。会喜欢替代品。
    • 为我工作,变量已被删除。
    • 也为我工作。 Chrome 78.0.3904.97,带有 chromedriver 78.0.3904.10500。
    • 这个 C# 版本对我有用。驱动程序 85.0.4183.87
    【解决方案5】:

    截至 2020 年 4 月,排除 2019 年 11 月 6 日更新中提到的启用自动化开关集合不再起作用。相反,我收到以下错误:

    ERROR:broker_win.cc(55)] Error reading broker pipe: The pipe has been ended. (0x6D)
    

    以下是截至 2020 年 4 月 6 日 Chrome 80 的运行情况。

    之前(在 Chrome 控制台窗口中):

    > navigator.webdriver
    true
    

    Python 示例:

    options = webdriver.ChromeOptions()
    options.add_argument("--disable-blink-features")
    options.add_argument("--disable-blink-features=AutomationControlled")
    

    之后(在 Chrome 控制台窗口中):

    > navigator.webdriver
    undefined
    

    【讨论】:

    • 尝试了很多东西,但这两个对我有用! Chromium 版本 85 非常感谢!!!
    【解决方案6】:

    现在您可以使用 cdp 命令完成此操作:

    driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
      "source": """
        Object.defineProperty(navigator, 'webdriver', {
          get: () => undefined
        })
      """
    })
    
    driver.get(some_url)
    

    顺便说一句,你想返回undefinedfalse 是一个死的赠品。

    【讨论】:

    • 现在(即 Chrome 97.0.4692.71)实际上是 false。无法评论 19 年 Chrome 是否为 false/undefined,但如果它自 (?) 以来发生变化会感到惊讶
    【解决方案7】:

    终于解决了 ChromeDriver 的问题,Chrome 大于 v79。

    ChromeOptions options = new ChromeOptions();
    options.addArguments("--disable-blink-features");
    options.addArguments("--disable-blink-features=AutomationControlled");
    ChromeDriver driver = new ChromeDriver(options);
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("source", "Object.defineProperty(navigator, 'webdriver', { get: () => undefined })");
    driver.executeCdpCommand("Page.addScriptToEvaluateOnNewDocument", params);
    

    【讨论】:

    • 你能在 python 中分享这个吗?
    【解决方案8】:

    python 的简单 hack:

    options = webdriver.ChromeOptions()    
    options.add_argument("--disable-blink-features=AutomationControlled")
    

    【讨论】:

      【解决方案9】:

      由于这个问题与 selenium 有关,因此覆盖 navigator.webdriver 的跨浏览器解决方案很有用。这可以通过在目标页面的任何 JS 运行之前修补浏览器环境来完成,但不幸的是,除了 chromium 之外,没有其他浏览器允许在文档加载后和任何其他 JS 运行之前评估任意 JavaScript 代码(firefox 与 Remote Protocol 接近)。

      在修补之前,我们需要检查默认浏览器环境的外观。在更改属性之前,我们可以看到它的默认定义为 Object.getOwnPropertyDescriptor()

      Object.getOwnPropertyDescriptor(navigator, 'webdriver');
      // undefined
      

      所以通过这个快速测试,我们可以看到webdriver 属性未在navigator 中定义。它实际上是在Navigator.prototype中定义的:

      Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver');
      // {set: undefined, enumerable: true, configurable: true, get: ƒ}
      

      更改拥有它的对象的属性非常重要,否则可能会发生以下情况:

      navigator.webdriver; // true if webdriver controlled, false otherwise
      // this lazy patch is commonly found on the internet, it does not even set the right value
      Object.defineProperty(navigator, 'webdriver', {
        get: () => undefined
      });
      navigator.webdriver; // undefined
      Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver').get.apply(navigator);
      // true
      

      一个不那么天真的补丁会首先针对正确的对象并使用正确的属性定义,但深入挖掘我们会发现更多的不一致:

      const defaultGetter = Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver').get;
      defaultGetter.toString();
      // "function get webdriver() { [native code] }"
      Object.defineProperty(Navigator.prototype, 'webdriver', {
        set: undefined,
        enumerable: true,
        configurable: true,
        get: () => false
      });
      const patchedGetter = Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver').get;
      patchedGetter.toString();
      // "() => false"
      

      一个完美的补丁不会留下任何痕迹,而不是替换getter函数,如果我们可以拦截对它的调用并更改返回值会很好。 JavaScript 通过Proxy apply handler 提供了原生支持:

      const defaultGetter = Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver').get;
      defaultGetter.apply(navigator); // true
      defaultGetter.toString();
      // "function get webdriver() { [native code] }"
      Object.defineProperty(Navigator.prototype, 'webdriver', {
        set: undefined,
        enumerable: true,
        configurable: true,
        get: new Proxy(defaultGetter, { apply: (target, thisArg, args) => {
          // emulate getter call validation
          Reflect.apply(target, thisArg, args);
          return false;
        }})
      });
      const patchedGetter = Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver').get;
      patchedGetter.apply(navigator); // false
      patchedGetter.toString();
      // "function () { [native code] }"
      

      现在唯一的不一致是函数名,不幸的是no way 覆盖了原生toString() 表示中显示的函数名。但即便如此,它也可以传递通用正则表达式,通过在其字符串表示的末尾查找{ [native code] } 来搜索欺骗的浏览器本机函数。要消除这种不一致,您可以修补 Function.prototype.toString 并使其为您修补的所有本机函数返回有效的本机字符串表示形式。

      总而言之,在 selenium 中它可以应用于:

      chrome.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {'source': """
          Object.defineProperty(Navigator.prototype, 'webdriver', {
              set: undefined,
              enumerable: true,
              configurable: true,
              get: new Proxy(
                  Object.getOwnPropertyDescriptor(Navigator.prototype, 'webdriver').get,
                  { apply: (target, thisArg, args) => {
                      // emulate getter call validation
                      Reflect.apply(target, thisArg, args);
                      return false;
                  }}
              )
          });
      """})
      

      playwright 项目维护了一个 Firefox 和 WebKit 的 fork 以添加浏览器自动化功能,其中一个等效于Page.addScriptToEvaluateOnNewDocument,但没有针对 Python 的通信协议实现,但可以从头开始实现.

      【讨论】:

      • 你知道我在哪里可以获得 Python、ChromeRemotedriver、Selenium4 的这些信息吗?
      • @TheDan 上面的代码是针对 python 的,在 selenium 4 中你需要获取 CDP 会话并调用execute_cdp_cmd。你应该为此提出一个新问题。
      【解决方案10】:

      正如上面评论中提到的 - https://stackoverflow.com/a/60403652/2923098 以下选项对我来说完全工作在 Java 中)-

      ChromeOptions options = new ChromeOptions();
      options.addArguments("--incognito", "--disable-blink-features=AutomationControlled");
      

      【讨论】:

        【解决方案11】:

        对于那些尝试过这些技巧的人,请确保还检查您正在使用的用户代理是否与您的爬虫所要使用的平台(移动/台式机/平板电脑)对应的用户代理仿真。我花了一段时间才意识到那是我的致命弱点;)

        【讨论】:

        • 这应该是一条评论。
        【解决方案12】:

        我想为 pguardiario

        提到的 cdp 命令方法添加一个 Java 替代方法
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("source", "Object.defineProperty(navigator, 'webdriver', { get: () => undefined })");
        driver.executeCdpCommand("Page.addScriptToEvaluateOnNewDocument", params);
        

        为了使其工作,您需要使用 org.openqa.selenium.chromium.ChromiumDriver 包中的 ChromiumDriver。据我所知,Selenium 3.141.59 中不包含该软件包,因此我使用了 Selenium 4 alpha。

        此外,excludeSwitches/useAutomationExtension 实验选项似乎不再适用于 ChromeDriver 79 和 Chrome 79。

        【讨论】:

          【解决方案13】:

          如果您使用 Remote Webdriver ,下面的代码会将 navigator.webdriver 设置为 undefined

          适用于 ChromeDriver 81.0.4044.122

          Python 示例:

              options = webdriver.ChromeOptions()
              # options.add_argument("--headless")
              options.add_argument('--disable-gpu')
              options.add_argument('--no-sandbox')
              driver = webdriver.Remote(
                 'localhost:9515', desired_capabilities=options.to_capabilities())
              script = '''
              Object.defineProperty(navigator, 'webdriver', {
                  get: () => undefined
              })
              '''
              driver.execute_script(script)
          

          【讨论】:

            【解决方案14】:

            使用--disable-blink-features=AutomationControlled 禁用navigator.webdriver

            【讨论】:

              【解决方案15】:

              Python

              我尝试了这篇文章中提到的大部分内容,但我仍然面临问题。 现在救我的是https://pypi.org/project/undetected-chromedriver

              pip install undetected-chromedriver
              
              
              import undetected_chromedriver.v2 as uc
              from time import sleep
              from random import randint
              
              
              driver = uc.Chrome()
              driver.get('www.your_url.here')
              driver.maximize_window() 
              
              sleep(randint(3,9))
              

              有点慢,但我会在不工作时慢慢来。

              我想如果每个感兴趣的人都可以查看源代码,看看是什么提供了胜利。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-07-23
                • 2015-01-17
                • 2021-11-09
                • 1970-01-01
                相关资源
                最近更新 更多