Selenium webdriver:修改 navigator.webdriver 标志以防止硒检测

Posted

技术标签:

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

我正在尝试使用 selenium 和 chrome 在网站中自动执行一项非常基本的任务,但网站以某种方式检测到 chrome 何时由 selenium 驱动并阻止每个请求。我怀疑该网站依赖于像 https://***.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,
  );

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

【问题讨论】:

【参考方案1】:

首先更新1

execute_cdp_cmd():现在有了execute_cdp_cmd(cmd, cmd_args) 命令,您可以使用Selenium 轻松执行google-chrome-devtools 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找到相关的详细讨论

将user-agent 转至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 中做同样的事情? ***.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://***.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))

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

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

【讨论】:

以上是关于Selenium webdriver:修改 navigator.webdriver 标志以防止硒检测的主要内容,如果未能解决你的问题,请参考以下文章

Selenium webdriver:修改 navigator.webdriver 标志以防止硒检测

修改 selenium webdriver 的参数

用 python selenium 爬简书,Python自动化领域之 Selenium WebDriver 学习第2篇

selenium,unittest——下拉菜单操作,百度账号设置修改

如何修改webdriver使其跳过浏览器检测

聊聊Selenium不同webdriver的构造