firefox无头注入JavaScript来修改浏览器属性

firefox无头注入JavaScript来修改浏览器属性

本文介绍了Selenium Webdriver:firefox无头注入JavaScript来修改浏览器属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图弄清楚如何将硒webdriver与python或java一起使用以注入javascript以修改浏览器属性/属性.我的最终目标是获得类似于将此与硒和Firefox一起使用,因为它是一种更加开放和灵活的选择.

I'm trying to figure out how is possible to use selenium webdriver with python or java to inject javascript in order to modify browser property/attribute.My final object is to get something similar to this with selenium and firefox since it is a more open and flexible choice.

木偶和铬文件test.js:

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch({
    args: ["--no-sandbox"],
    headless: true,
  });
  const page = await browser.newPage();
  const fs = require("fs");

  // In your puppeteer script, assuming the javascriptChromium.js file is in same folder of our script
  const preloadFile = fs.readFileSync("./javascriptChromium.js", "utf8");
  await page.evaluateOnNewDocument(preloadFile);

  const testUrl="https://intoli.com/blog/not-possible-to-block-chrome-headless/chrome-headless-test.html";

  await page.goto(testUrl);

  // save screenshot
  await page.screenshot({path: "puppeteer-chromium-async-script-test.png"});
  await browser.close()
})();

JavaScript文件javascriptChromium.js

Javascript file javascriptChromium.js

// overwrite the `languages` property to use a custom getter
Object.defineProperty(navigator, "languages", {
  get: function() {
    return ["en-US", "en", "es"];
  }
});

// Overwrite the `plugins` property to use a custom getter.
Object.defineProperty(navigator, 'plugins', {
  get: () => [1, 2, 3, 4, 5],
});

// Pass the Webdriver test
Object.defineProperty(navigator, 'webdriver', {
  get: () => false,
});

此代码运行良好,我通过此测试网站.

This code works well and I checked that the property are changed via this test Web site.

现在,硒和Firefox:

Now, selenium and firefox:

import os
from selenium import webdriver

def readJSFile(scriptFile):
    with open(scriptFile, 'r') as fileHandle:
        script=fileHandle.read()
    return script
injectedJavascript=readJSFile("./javascriptFirefox.js")

options=webdriver.FirefoxOptions()
options.set_headless(True)
driver=webdriver.Firefox(options=options)
driver.set_script_timeout(3)

# inject JavaScript
try:
    driver.execute_async_script(injectedJavascript)
except:
    print("Timeout")

# solution found here https://stackoverflow.com/questions/17385779/how-do-i-load-a-javascript-file-into-the-dom-using-selenium
driver.execute_script("var s=window.document.createElement('script'); s.src='javascriptFirefox.js';window.document.head.appendChild(s);")
testUrl="https://intoli.com/blog/not-possible-to-block-chrome-headless/chrome-headless-test.html";
driver.get(testUrl)

# example sync script
time=driver.execute_script("return performance.timing.loadEventEnd - performance.timing.navigationStart;")
print(time)
# example async script
time=driver.execute_async_script("var callback = arguments[arguments.length-1]; const time = () => { total=performance.timing.loadEventEnd - performance.timing.navigationStart; callback(total); }; time();")
print(time)

file="selenium-firefox-async-script-test.png"
driver.save_screenshot(file)

driver.quit()

JavaScript文件javascriptFirefox.js

Javascript file javascriptFirefox.js

// overwrite the `languages` property to use a custom getter
const setProperty = () => {
    Object.defineProperty(navigator, "languages", {
        get: function() {
            return ["en-US", "en", "es"];
        }
    });

    // Overwrite the `plugins` property to use a custom getter.
    Object.defineProperty(navigator, 'plugins', {
        get: () => [1, 2, 3, 4, 5],
    });

    // Pass the Webdriver test
    Object.defineProperty(navigator, 'webdriver', {
      get: () => false,
    });
    callback();
};
setProperty();

我是Java语言的新手,但是这两种方法(操纵p和硒)之间似乎有什么不同,是关于它们如何管理当前的标签页/页面.前者通过页面类和方法 page.evaluateOnNewDocument 对于后者,我没有找到等效的方法.我也尝试过使用油脂猴子或violentlmonkey注入javascript,但没有成功.

I'm new of javascript, but what seems different between the two approaches (puppeteer and selenium) is about how they manage the current tab/page. The former via page class and method page.evaluateOnNewDocument while for the latter I did not find and equivalent way.I tried also the use greasemonkey or violentlmonkey to inject javascript without success.

您有什么建议吗?

谢谢

推荐答案

我通过遵循以下帖子.简而言之,通过使用扩展,也可以使用firefox将javascript代码注入Web页面.为了避免浪费其他用户的时间,主要文件为:

I found the solution to the problem by following this post.In few words, by using an extensions it is possible to inject javascript code into the Web page also with firefox.In order to avoid a waste of time for the other users, the main files are:

Python文件:selenium + firefox

Python file: selenium+firefox

import json
import os
import sys

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver.firefox.firefox_profile import AddonFormatError

# Patch in support for WebExtensions in Firefox.
# See: https://intoli.com/blog/firefox-extensions-with-selenium/
class FirefoxProfileWithWebExtensionSupport(webdriver.FirefoxProfile):
    def _addon_details(self, addon_path):
        try:
            return super()._addon_details(addon_path)
        except AddonFormatError:
            try:
                with open(os.path.join(addon_path, "manifest.json"), "r") as f:
                    manifest = json.load(f)
                    return {
                        "id": manifest["applications"]["gecko"]["id"],
                        "version": manifest["version"],
                        "name": manifest["name"],
                        "unpack": False,
                    }
            except (IOError, KeyError) as e:
                raise AddonFormatError(str(e), sys.exc_info()[2])

profile_folder="profile_path"
profile=FirefoxProfileWithWebExtensionSupport(profile_folder)
extension_directory="extension"
profile.add_extension(extension_directory)
# firefox dev it is necessary for custom profile, not for standard one
firefox_binary="/usr/bin/firefox-dev"
options=webdriver.FirefoxOptions()
# firefox 56+ headless mode https://developer.mozilla.org/en-US/Firefox/Headless_mode
options.set_headless(True)
driver=webdriver.Firefox(options=options, firefox_profile=profile, firefox_binary=firefox_binary)

test_url="https://intoli.com/blog/not-possible-to-block-chrome-headless/chrome-headless-test.html";
driver.get(test_url)

file="selenium-firefox-extension-profile-script-second-test.png"
driver.save_screenshot(file)

test_url="https://intoli.com/blog/making-chrome-headless-undetectable/chrome-headless-test.html";
driver.get(test_url)

file="selenium-firefox-extension-profile-script-first-test.png"
driver.save_screenshot(file)

driver.quit()

扩展文件:manifest.js和content.js

Extensions files: manifest.js and content.js

{
  "manifest_version": 2,
  "name": "Smart Extension",
  "version": "1.0.0",
  "applications": {
    "gecko": {
      "id": "[email protected]"
    }
  },
  "content_scripts": [
    {
      "matches": ["*://*/*"],
      "js": ["content.js"],
      "run_at": "document_start"
    }
  ]
}

var script=document.createElement("script");
script.src=browser.extension.getURL("myscript.js");
script.async=false;
document.documentElement.appendChild(script);

JavaScript文件:myscript.js

Javascript file: myscript.js

// overwrite the `languages` property to use a custom getter
Object.defineProperty(navigator, "languages", {
  get: function() {
    return ["en", "es"];
  }
});

// Overwrite the `plugins` property to use a custom getter.
Object.defineProperty(navigator, "plugins", {
  get: () => new Array(Math.floor(Math.random() * 6) + 1),
});

// Pass the Webdriver test
Object.defineProperty(navigator, "webdriver", {
  get: () => false,
});

// hairline: store the existing descriptor
const elementDescriptor=Object.getOwnPropertyDescriptor(HTMLElement.prototype, "offsetHeight");

// redefine the property with a patched descriptor
Object.defineProperty(HTMLDivElement.prototype, "offsetHeight", {
    ...elementDescriptor,
  get: function() {
    if (this.id === "modernizr") {
      return 1;
    }
    return elementDescriptor.get.apply(this);
  },
});

["height", "width"].forEach(property => {
  // store the existing descriptor
  const imageDescriptor=Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, property);

  // redefine the property with a patched descriptor
  Object.defineProperty(HTMLImageElement.prototype, property, {
    ...imageDescriptor,
    get: function() {
      // return an arbitrary non-zero dimension if the image failed to load
      if (this.complete && this.naturalHeight == 0) {
        return 24;
      }
      // otherwise, return the actual dimension
      return imageDescriptor.get.apply(this);
    },
  });
});

const getParameter=WebGLRenderingContext.getParameter;
WebGLRenderingContext.prototype.getParameter=function(parameter) {
  // UNMASKED_VENDOR_WEBGL WebGLRenderingContext.prototype.VENDOR
  if (parameter === 37445) {
    return "Intel Open Source Technology Center";
  }
  // UNMASKED_RENDERER_WEBGL WebGLRenderingContext.prototype.RENDERER
  if (parameter === 37446) {
    return "Mesa DRI Intel(R) Ivybridge Mobile";
  }
  return getParameter(parameter);
};

这对于图形模式下的所有测试都适用,而在无头模式下,除WebGL测试外的所有测试似乎都对错误.

This works well for all tests in graphical mode while in headless mode all tests except WebGL test which seems affect to a bug.

这篇关于Selenium Webdriver:firefox无头注入JavaScript来修改浏览器属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-12 23:59