Selenium:如何在 loading/executing 页面的任何其他脚本之前 Inject/execute Javascript 进入页面?

Selenium: How to Inject/execute a Javascript in to a Page before loading/executing any other scripts of the page?

我正在使用 selenium python webdriver 来浏览一些页面。我想在加载和执行任何其他 Javascript 代码之前将 javascript 代码注入页面。另一方面,我需要我的 JS 代码作为该页面的第一个 JS 代码执行。有没有办法通过 Selenium 做到这一点?

我在谷歌上搜索了几个小时,但找不到合适的答案!

如果您想在页面被浏览器解析和执行之前将某些内容注入页面的 html,我建议您使用代理,例如 Mitmproxy

如果您无法修改页面内容,您可以使用代理,或使用浏览器中安装的扩展程序中的内容脚本。在 selenium 中执行此操作时,您会编写一些代码将脚本作为现有元素的子元素之一注入,但是在页面加载之前您将无法拥有它 运行(当您的驱动程序 get() 调用 returns.)

String name = (String) ((JavascriptExecutor) driver).executeScript(
    "(function () { ... })();" ...

文档未指定代码开始执行的时刻。你会希望它在 DOM 开始加载之前,这样保证可能只能通过代理或扩展内容脚本路由来满足。

如果您可以使用最少的工具检测您的页面,您可能会检测到特殊 url 查询参数的存在并加载其他内容,但您需要使用内联脚本来执行此操作。伪代码:

 <html>
    <head>
       <script type="text/javascript">
       (function () {
       if (location && location.href && location.href.indexOf("SELENIUM_TEST") >= 0) {
          var injectScript = document.createElement("script");
          injectScript.setAttribute("type", "text/javascript");

          //another option is to perform a synchronous XHR and inject via innerText.
          injectScript.setAttribute("src", URL_OF_EXTRA_SCRIPT);
          document.documentElement.appendChild(injectScript);

          //optional. cleaner to remove. it has already been loaded at this point.
          document.documentElement.removeChild(injectScript);
       }
       })();
       </script>
    ...

所以我知道已经有几年了,但我找到了一种无需修改网页内容且无需使用代理即可完成此操作的方法!我使用的是 nodejs 版本,但大概 API 对于其他语言也是一致的。你要做的事情如下

const {Builder, By, Key, until, Capabilities} = require('selenium-webdriver');
const capabilities = new Capabilities();
capabilities.setPageLoadStrategy('eager'); // Options are 'eager', 'none', 'normal'
let driver = await new Builder().forBrowser('firefox').setFirefoxOptions(capabilities).build();
await driver.get('http://example.com');
driver.executeScript(\`
  console.log('hello'
\`)

那个 'eager' 选项对我有用。您可能需要使用 'none' 选项。 文档:https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/capabilities_exports_PageLoadStrategy.html

编辑:请注意 'eager' 选项尚未在 Chrome 中实现...

从 1.0.9 版本开始,selenium-wire 获得了修改对请求的响应的功能。下面是此功能的示例,可在页面到达网络浏览器之前将脚本注入页面。

import os
from seleniumwire import webdriver
from gzip import compress, decompress
from urllib.parse import urlparse

from lxml import html
from lxml.etree import ParserError
from lxml.html import builder

script_elem_to_inject = builder.SCRIPT('alert("injected")')

def inject(req, req_body, res, res_body):
    # various checks to make sure we're only injecting the script on appropriate responses
    # we check that the content type is HTML, that the status code is 200, and that the encoding is gzip
    if res.headers.get_content_subtype() != 'html' or res.status != 200 or res.getheader('Content-Encoding') != 'gzip':
        return None
    try:
        parsed_html = html.fromstring(decompress(res_body))
    except ParserError:
        return None
    try:
        parsed_html.head.insert(0, script_elem_to_inject)
    except IndexError: # no head element
        return None
    return compress(html.tostring(parsed_html))

drv = webdriver.Firefox(seleniumwire_options={'custom_response_handler': inject})
drv.header_overrides = {'Accept-Encoding': 'gzip'} # ensure we only get gzip encoded responses

另一种远程控制浏览器并能够在页面内容加载之前注入脚本的一般方法是使用完全基于单独协议的库,例如:Chrome DevTools 协议。我所知道的功能最全的是 playwright

Selenium 现已支持 Chrome Devtools 协议 (CDP) API,因此,在每次加载页面时执行脚本真的很容易。这是一个示例代码:

driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {'source': 'alert("Hooray! I did it!")'})

它会为每个页面加载执行该脚本。有关这方面的更多信息,请访问: