除非手动打开带有 headless:false 的新选项卡,否则 Puppeteer 被阻止

时间:2021-07-13 00:35:25

标签: puppeteer

当我在无头模式下运行脚本时,它只是在 page.goto(url) 上超时。当我使用 headless:false 运行它并让它做它的事情时,您可以看到 URL 开始加载片刻,然后进入某种重定向和无限加载。

但是,如果在 headless:false 中,我打开一个新标签并手动导航到 URL,那么原始标签将正常加载。我已经采取了很多措施来避免在这里被发现;

const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth')
const userAgent = require('user-agents');

await puppeteer.use(StealthPlugin())
var browser = await puppeteer.launch({headless: false});

let page = await browser.newPage();

page.setViewport({
                width: 1200,
                height: 800,
                deviceScaleFactor: 1,
                hasTouch: false,
                isLandscape: true,
                isMobile: false,
            });

var agent = userAgent.random()
            await page.setUserAgent(agent.toString());         
            await page.setJavaScriptEnabled(true);
            // Pass the Webdriver Test.
            await page.evaluateOnNewDocument(() => {
                Object.defineProperty(navigator, 'webdriver', {
                get: () => false,
                });
            });

            // Pass the Chrome Test.
            await page.evaluateOnNewDocument(() => {
                // We can mock this in as much depth as we need for the test.
                window.navigator.chrome = {
                runtime: {},
                // etc.
                };
            });

            // Pass the Permissions Test.
            await page.evaluateOnNewDocument(() => {
                const originalQuery = window.navigator.permissions.query;
                return window.navigator.permissions.query = (parameters) => (
                parameters.name === 'notifications' ?
                    Promise.resolve({ state: Notification.permission }) :
                    originalQuery(parameters)
                );
            });

            // Pass the Plugins Length Test.
            await page.evaluateOnNewDocument(() => {
                // Overwrite the `plugins` property to use a custom getter.
                Object.defineProperty(navigator, 'plugins', {
                // This just needs to have `length > 0` for the current test,
                // but we could mock the plugins too if necessary.
                get: () => [1, 2, 3, 4, 5],
                });
            });

            // Pass the Languages Test.
            await page.evaluateOnNewDocument(() => {
                // Overwrite the `plugins` property to use a custom getter.
                Object.defineProperty(navigator, 'languages', {
                get: () => ['en-US', 'en'],
                });
            });
            const session = await page.target().createCDPSession();
            await session.send("Page.enable");
            await session.send("Page.setWebLifecycleState", { state: "active" });
            await page.bringToFront();
                    
            await page.goto(url, {waitUntil: "networkidle2"} );

除非我手动打开一个新选项卡并在地址栏中键入内容,否则我仍然告诉他们我正在运行 puppeteer 有什么想法吗?或者,有没有办法在浏览器中强制进行更人性化的交互,以打开新标签页,并允许我无头地执行此操作?

编辑:当我说“进入某种重定向和无限加载”时,要清楚的是,我看到页面渲染的短暂闪烁,然后它转到一个空白的白色页面。地址栏中没有发现任何变化,但加载图标指示器似乎显示了某种类型的重定向或刷新。无论我在 puppeteer 创建的选项卡之前、期间还是之后手动打开新选项卡,只要手动选项卡开始加载 URL,puppeteer 创建的选项卡就会突然开始工作。

1 个答案:

答案 0 :(得分:0)

问题出在 waitUntilnetworkidle2 上,这是因为有时 puppeteer 遵循的规则比我们认为的“完全加载的网页”要严格得多。即使你作为人类可以决定你想要的元素是否已经在 DOM 中(因为你看到了实际的元素)或者它不存在(因为你没有看到它),puppeteer 可能会等待别的东西。例如:即使背景图片仍在后台加载,您也会看到您的元素已经存在。

注意:未正确选择 waitUntil 选项可能导致空白页,我通过将 puppeteer 的 default timeout 修改为更短的时间来重现您的问题。

(1) 如果您的任务不需要每个网络连接,您可以通过将 waitUntil: 'networkidle2' 替换为 waitUntil: 'domcontentloaded' 来加速页面加载,因为此事件通常发生得更早。

await page.goto(url, { waitUntil: 'domcontentloaded' })

可能的 waitUntil 选项是:

  • load:触发 load 事件时。
  • domcontentloaded:触发 DOMContentLoaded 事件时。
  • networkidle0:当至少 500 毫秒内没有超过 0 个网络连接时。
  • networkidle2:当至少 500 毫秒的网络连接不超过 2 个时。

[source]

(2) 如果你正在处理一个单页应用并且你不能依赖 loaddomcontentloaded 事件,但是网络空闲的会超时,那么你可以尝试 {{3 }},一个键选择器,确保加载所需的 JavaScript 包并且页面正常运行:

page.goto(url) // not await-ed
await page.waitForSelector(keySelector) // ensures the page is functional

免责声明:即使问题不同但答案与waiting for a selector rather than an event

相似