如何在加载/执行页面的任何其他脚本之前将Javascript注入/执行到页面中

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

本文关键字:执行 Javascript 注入 任何 加载 其他 脚本      更新时间:2023-09-26

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

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

Selenium现在支持Chrome Devtools Protocol (CDP) API,因此,在每个页面加载上执行脚本非常容易。下面是一个示例代码:

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

并且它将为每个页面加载执行该脚本。更多相关信息请访问:

  • Selenium文档:https://www.selenium.dev/documentation/en/support_packages/chrome_devtools/
  • Chrome Devtools协议文档:https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-addScriptToEvaluateOnNewDocument

从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协议。我所知道的最有特色的是剧作家

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

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

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'
'`)

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

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