【问题标题】:Injecting HTML before script evaluation with puppeteer在使用 puppeteer 进行脚本评估之前注入 HTML
【发布时间】:2021-05-29 13:27:10
【问题描述】:

我想使用 puppeteer 将一些 HTML 注入到页面上的特定元素中。

必须在执行任何 JavaScript 之前注入 HTML。

我认为有两种方法可以做到这一点:

  1. 使用page.evaluateOnNewDocument 注入 HTML

这个函数是"is invoked after the document was created",但我不能从中访问 DOM 元素。例如:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  page.on('console', consoleObj => console.log(consoleObj.text()));

  await page.evaluateOnNewDocument(
    () => {
      const content = document.querySelector('html');
      console.log(content);
    }
  );

  await page.goto(process.argv[2]);

  await browser.close();
})();

当我访问一个页面时,这个脚本只会输出换行符。

  1. 在我注入 HTML 之前,使用page.setJavaScriptEnabled 防止 javascript 执行。就像per the docs 一样,在我重新打开它后它不会开始执行javascript。例如:

我的脚本看起来像这样:

const fs = require('fs');
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  const html = fs.readFileSync('./example.html', 'utf8');

  await page.setJavaScriptEnabled(false)
  await page.goto(process.argv[2]);
  await page.evaluate(
    content => {
      const pageEl = document.querySelector('div.page');
      let node = document.createElement('div');
      node.innerHTML = content;
      pageEl.appendChild(node);
    }, html
  );
  await page.setJavaScriptEnabled(true)

  await browser.close();
})();

或者,也可以执行this 之类的操作,尽管对于相当简单的请求而言,这似乎过于复杂。

有没有我忽略的更简单的方法?

干杯

【问题讨论】:

  • 至于方式1:脚本执行的时候好像没有DOM。至于2方式:似乎setJavaScriptEnabled()page.evaluate()没有影响。有点不清楚您有什么限制:您是否需要在创建 DOM 之后但在执行任何页面脚本之前插入一个元素?
  • 是的,HTML 必须注入到特定的元素中,所以在加载 DOM 之后,但在执行任何 JavaScript 之前。在页面再次导航之前,使用 setJavaScriptEnabled(true) 重新启用 JavaScript 不会产生影响
  • 也许您可以尝试在'domcontentloaded' 页面事件上调用page.evaluate(),但成功似乎无法预测。
  • 或者您可以设置MutationObserverevaluateOnNewDocument() 以捕捉添加所需节点的时刻。
  • 感谢您的建议。为了给您更多的上下文,我尝试在添加 jQuery 事件侦听器之前注入 HTML。我尝试使用domcontentloaded,但没有成功。 pastebin.com/zVNvDXGF 这个 sn-p 运行得不够早,无法拦截正在添加的 jQuery 事件侦听器。 (意味着元素不会被足够早地添加)

标签: javascript node.js puppeteer


【解决方案1】:

看来这实际上是一个非常受欢迎的请求,我也许应该在发布我的问题之前进行更彻底的搜索。

尽管如此,我还是选择了 aslushnikov here 提出的解决方案。

以下代码只是我为测试这个想法而编写的,我相信还有很大的改进空间。

我做了一个简单的函数来执行 XHR:

const requestPage = async (url) => {
  return new Promise(function (resolve, reject) {
    let xhr = new XMLHttpRequest();
    xhr.open('GET', url);
    xhr.setRequestHeader('Ignore-Intercept', 'Value');
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        const response = {};
        xhr.getAllResponseHeaders()
          .trim()
          .split(/[\r\n]+/)
          .map(value => value.split(/: /))
          .forEach(keyValue => {
              response[keyValue[0].trim()] = keyValue[1].trim();
          });
        resolve({ ...response, body: xhr.response });
      } else {
        reject({
            status: this.status,
            statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
          status: this.status,
          statusText: xhr.statusText
      });
    };
    xhr.send();
  });
};

然后我将这个函数暴露给页面。

然后我使用此函数执行 XHR,而不是允许请求继续,并将其结果用作对请求的响应。

await page.setRequestInterception(true);
page.on('request', async (request) => {
  if (
    request.url() === url
    && (
      typeof request.headers()['access-control-request-headers'] === 'undefined'
      || !request.headers()['access-control-request-headers'].match(/ignore-intercept/gi)
    ) && typeof request.headers()['ignore-intercept'] === 'undefined'
  ) {
    const response = await page.evaluate(`requestPage('${url}')`);
    response.body += "hello";
    request.respond(response);
  } else {
    request.continue();
  }
});

await page.goto(`data:text/html,<iframe style='width:100%; height:100%' src=${url}></iframe>`);

令人讨厌的是,除非所需页面位于 iframe 中,否则似乎无法使用 page.evaluate。 (因此await page.goto(`data:text/html....

【讨论】:

    【解决方案2】:

    通过以下 sn-p,我能够增强身体。 我将其用于模拟目的。

    const browser = await puppeteer.launch();
    browser.on('targetchanged', async target => {
      const targetPage = await target.page();
      const client = await targetPage.target().createCDPSession();
      await client.send('Runtime.evaluate', {
        expression: `
          window.document.addEventListener("DOMContentLoaded", function () {
            const container = window.document.createElement('span');
            container.innerText = "Hello World!";
            window.document.body.appendChild(container);
          });
        `,
      });
    });
    

    我不完全确定targetchanged 是什么。我摆弄它的假设是,当浏览器转到特定页面“目标”时,我可能是错的。

    其他资源

    【讨论】:

      【解决方案3】:

      您可以使用Page.evaluateOnNewDocument 运行可以操作DOM 的JS。

      https://pptr.dev/#?product=Puppeteer&version=v5.2.1&show=api-pageevaluateonnewdocumentpagefunction-args

      【讨论】:

        猜你喜欢
        • 2018-06-20
        • 1970-01-01
        • 2020-01-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-15
        • 2020-08-12
        • 1970-01-01
        相关资源
        最近更新 更多