在使用 puppeteer 进行脚本评估之前注入 HTML
Injecting HTML before script evaluation with puppeteer
我想使用 puppeteer 将一些 HTML 注入到页面上的特定元素中。
必须在执行任何 JavaScript 之前注入 HTML。
我认为有两种方法可以做到这一点:
- 使用
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();
})();
当我访问一个页面时,这个脚本只输出换行符。
- 使用
page.setJavaScriptEnabled
来阻止javascript 在我注入HTML 之前执行。但是,作为 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 的操作,尽管对于相当简单的请求来说这似乎过于复杂。
有没有我忽略的更简单的方法?
干杯
看来这实际上是一个非常受欢迎的请求,我或许应该在发布问题之前进行更彻底的搜索。
尽管如此,我还是选择了 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....
您可以使用Page.evaluateOnNewDocument
到运行 JS,您可以在其中操作DOM。
通过以下代码片段,我能够扩充 body。
我将其用于模拟目的。
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
是什么。我摆弄它的假设是,当浏览器转到特定页面“目标”时,但我可能是错的。
其他资源
我想使用 puppeteer 将一些 HTML 注入到页面上的特定元素中。
必须在执行任何 JavaScript 之前注入 HTML。
我认为有两种方法可以做到这一点:
- 使用
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();
})();
当我访问一个页面时,这个脚本只输出换行符。
- 使用
page.setJavaScriptEnabled
来阻止javascript 在我注入HTML 之前执行。但是,作为 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 的操作,尽管对于相当简单的请求来说这似乎过于复杂。
有没有我忽略的更简单的方法?
干杯
看来这实际上是一个非常受欢迎的请求,我或许应该在发布问题之前进行更彻底的搜索。
尽管如此,我还是选择了 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....
您可以使用Page.evaluateOnNewDocument
到运行 JS,您可以在其中操作DOM。
通过以下代码片段,我能够扩充 body。 我将其用于模拟目的。
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
是什么。我摆弄它的假设是,当浏览器转到特定页面“目标”时,但我可能是错的。
其他资源