如何使用 Puppeteer 阻止 window.location 并获取页面内容?

5
我想使用Puppeteer获取页面的完整内容,对于普通页面,这个方法很有效,但是如果有window.location重定向,我想要阻止它并仅获取原始内容:
例如,如果https://example.com/thisredirects返回:
<html>
<body>
<p>Page not found - Please wait while we redirect you home...</p>
<script type="text/javascript" language="javascript">
   window.location = "//example.com";
</script>
</body>
</html>

我想获取HTML并阻止位置重定向。如果我尝试使用setRequestInterception来阻止/中止位置更改,响应将返回null,并且实际上无法完全阻止重定向(对于重定向状态代码可行,但不适用于返回200然后使用window.location进行重定向的页面):

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({ headless: false });
  const pageUrl = "https://example.com/thisredirects";

  const page = await browser.newPage();
  await page.setCacheEnabled(false);
  await page.setRequestInterception(true);

  const requests = [];
  page.on('request', async request => {
    let isNavRequest = request.isNavigationRequest() && request.frame() === page.mainFrame();
    if (!isNavRequest) {
      request.continue();
      return;
    }
    requests.push(request);
    if (requests.length == 1) {
      console.log("Load initial page: " + request.url());
      request.continue();
      return;
    }
    console.log("Block redirect to: " + request.url());
    request.abort();
  });

  let response;
  try {
    console.log(`Request: ${pageUrl}`);
    response = await page.goto(pageUrl, { waitUntil: 'domcontentloaded' });
    const content = await response.text();
    console.log(content);
    await page.close();
    await browser.close();
  }
  catch (err) {
    console.log(err);
  }
})()

有没有一种方法可以阻止window.location,并在不完全禁用Javascript的情况下获取原始HTML(如上所示)?

即使我倾听所有响应:

  page.on('response', async response => {
    if (response.ok && response.url() === pageUrl) {
      console.log(await response.text());
    }
  });

它无法获取原始的HTML。它会抛出Could not load body for this request. This might happen if the request is a preflight request.

1
如果在导航到网页之前禁用JavaScript会怎样?page.setJavaScriptEnabled(false) - GrafiCode
谢谢你的回复@GrafiCode,不幸的是,即使对于页面上下文,禁用JavaScript也不是一个选项,因为在不重定向的页面上(我事先不知道哪些页面会/不会),我需要启用JavaScript。 - Zak123
2个回答

2

我之前不知道通过向request.abort发送不同的错误代码(aborted)可以让你访问先前的请求。有了这个,我能够访问原始响应中的文本:

const page = await browser.newPage();
await page.setCacheEnabled(false);
await page.setRequestInterception(true);

const requests = [];
let redirectBlocked = false;

page.on('request', async request => {
    let isNavRequest = request.isNavigationRequest() && request.frame() === page.mainFrame();
    if (!isNavRequest) {
        request.continue();
        return;
    }

    requests.push(request);
    if (requests.length == 1) {
        request.continue();
        return;
    }

    // *snip* more here to detect legitimate redirects...

    redirectBlocked = true;
    request.abort('aborted');

    let originalResponse = await requests[0].response();
    console.log(await originalResponse.text());
});

const response = await page.goto(pageUrl, { waitUntil: 'domcontentloaded' });

if (!redirectBlocked) console.log(await response.text());

1

@GrafiCode提供的关于page.setJavascriptEnabled(false)的提示是很好的:稍后,您可以通过将其值设置为true来重新启用它。

要解决问题,您可以按照以下步骤进行操作:

  1. 禁用JavaScript以防止window.location被立即重新分配
  2. 导航到(不正常的)页面
  3. 删除试图操纵位置的<script>标记(page.$$evalpage.evaluate puppeteer方法可用于执行Element.remove()
  4. 在清理后保存HTML - 无重定向的标记(page.content)。
  5. 启用JavaScript
  6. 在页面上设置保存的HTML(page.setContent
  7. 您将无法像上面尝试的那样访问response.text()(因为setContent与goto返回方式不同),但您可以在<body>innerText上使用page.$eval
const page = await browser.newPage()
await page.setJavaScriptEnabled(false)
await page.goto(pageUrl)

await page.$$eval('script', scripts =>
  scripts.forEach(src => {
    if (src.innerHTML.includes('window.location')) src.remove()
  })
)

const html = await page.content()
await page.setJavaScriptEnabled(true)
await page.setContent(html)

const text = await page.$eval('body', el => el.innerText)
console.log(text)

输出(<p>标签的内容):
Page not found - Please wait while we redirect you home...

我最终找到了一种方法,可以在完全不禁用JavaScript的情况下实现此操作(没有意识到您发送的request.abort错误代码会使响应链表现出不同的行为)。在我想要记录被阻止的重定向的情况下,这对我来说更好。感谢您的解决方案,已点赞。 - Zak123

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接