Selenium: как внедрить/выполнить Javascript на страницу перед загрузкой/выполнением любых других скриптов на странице?

Я использую веб-драйвер selenium python для просмотра некоторых страниц. Я хочу ввести код javascript на страницы до того, как любые другие коды Javascript будут загружены и выполнены. С другой стороны, мне нужно, чтобы мой JS-код выполнялся как первый JS-код этой страницы. Есть ли способ сделать это с помощью Selenium?

Я гуглил пару часов, но не смог найти нормального ответа!


person Alex    schedule 11.07.2015    source источник
comment
Но мой вопрос в том, как я могу внедрить JS-код с помощью Selenium Webdriver перед загрузкой страницы. У меня нет доступа к содержимому этих страниц, поэтому я не могу внедрить в них код JS, если не использую прокси-сервер для перезаписи содержимого страницы.   -  person Alex    schedule 11.07.2015
comment
Думаю, я нашел ответ. Согласно grokbase.com/t/gg/selenium-users/12a99543jq/, мы не можем этого сделать, если не используем прокси-сервер для внедрения скрипта в начале страницы.   -  person Alex    schedule 11.07.2015
comment
Сможете ли вы установить приложение, такое как GreaseMonkey или Tampermonkey, для внедрения ваших скриптов? addons.mozilla.org/en-us/firefox/addon/greasemonkey   -  person Brian Cain    schedule 08.11.2015
comment
Да, вы можете сделать это с помощью собственного расширения или GreaseMonkey.   -  person Alex    schedule 09.11.2015
comment
Если вы не используете физический дисплей и используете что-то вроде PhantomJS, вы можете получить DOM целевой страницы. Затем вы можете просмотреть DOM, внедрить свой скрипт и добавить триггер onLoad для выполнения скрипта при загрузке страницы. Как я вижу, это один из самых простых способов.   -  person Abhinav    schedule 17.11.2015


Ответы (5)


Если вы не можете изменить содержимое страницы, вы можете использовать прокси или скрипт контента в расширении, установленном в вашем браузере. Выполняя это в селене, вы должны написать некоторый код, который внедряет скрипт как один из дочерних элементов существующего элемента, но вы не сможете запустить его до загрузки страницы (когда возвращается вызов 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>
    ...
person init_js    schedule 23.11.2015

Начиная с версии 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

Еще один способ удаленного управления браузером и возможность внедрить скрипт до загрузки содержимого страниц — это использовать библиотеку, полностью основанную на отдельном протоколе, например: протокол DevTools. Реализация Python доступна здесь: https://github.com/pyppeteer/pyppeteer2 (Отказ от ответственности: я я один из главных авторов)

person Mattwmaster58    schedule 25.08.2019
comment
Отличный совет! Что делает эта строка: injected.append((req, req_body, res, res_body, parsed_html))? Я не нашел, к чему относится injected - person Jean Monet; 29.03.2020
comment
Это просто запись вложенных ресурсов. Я удалил его, чтобы избежать путаницы. - person Mattwmaster58; 29.03.2020
comment
Спасибо! Знаете ли вы, позволяет ли функция инъекции custom_response_handler изменять заголовки ответа? Я вижу, что мы можем вернуть ответ body, но в моем примере я также хотел бы добавить или изменить заголовок в ответе. - person Jean Monet; 30.03.2020
comment
Я не уверен, вы могли бы попробовать (пере)записать некоторые ключи в res.headers. - person Mattwmaster58; 30.03.2020
comment
Похоже, эта функция устарела в январе 2021 года: pypi.org/project/selenium-wire с V3 - вы знаете альтернативу? - person n.r.; 14.07.2021

Если вы хотите вставить что-то в HTML-код страницы до того, как он будет проанализирован и выполнен браузером, я бы посоветовал вам использовать прокси-сервер, такой как Митмпрокси.

person Jonathan    schedule 22.11.2015
comment
Можно ли это сделать, если сайт использует https? - person blablaalb; 24.08.2020

поэтому я знаю, что прошло несколько лет, но я нашел способ сделать это без изменения содержимого веб-страницы и без использования прокси! Я использую версию 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'
\`)

Этот «жадный» вариант работает для меня. Возможно, вам придется использовать опцию «нет». Документация: https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/capabilities_exports_PageLoadStrategy.html

РЕДАКТИРОВАТЬ: обратите внимание, что опция «нетерпеливый» еще не реализована в Chrome...

person Jacob    schedule 27.06.2019
comment
Спасибо! Искал, как выполнить скрипт до отображения страницы, и это работает. Я также заставил его работать в Chrome, если кто-то еще столкнется с этим. Пример Python - person 010011100101; 25.02.2020
comment
Не работает для меня. Это не гарантирует, что сценарий будет запущен до загрузки страницы, он позволяет запустить сценарий, как только страница станет интерактивной. - person villasv; 06.03.2021
comment
@ 010011100101 не могли бы вы опубликовать код здесь как решение? Спасибо - person n.r.; 14.07.2021

Selenium теперь поддерживает API Chrome Devtools Protocol (CDP), поэтому очень легко выполнять скрипт при каждой загрузке страницы. Вот пример кода для этого:

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

И он будет выполнять этот скрипт для КАЖДОЙ загрузки страницы. Более подробную информацию об этом можно найти по адресу:

person Khanh Luong    schedule 26.07.2021