返回

Puppeteer 获取 iframe 内容的替代方案 | 跨域安全解析

javascript

使用 Puppeteer 获取 iframe 内容的替代方案

使用 Puppeteer 处理包含 iframe 的页面时,跨域问题是经常遇到的一个挑战。当试图通过 JavaScript 从父页面访问不同源的 iframe 内容时,浏览器会出于安全考虑抛出 DOMException: Blocked a frame with origin [url] from accessing a cross-origin frame 异常。 一种常见的解决办法是使用 --disable-web-security 启动参数禁用浏览器的 Web 安全策略。 但这个方法有风险,因为它会暴露出本地文件系统,并不建议在生产环境中使用。 有其他方法可以在不牺牲安全性的前提下获取 iframe 内容吗?答案是肯定的。

方案一:使用 page.evaluate 在 iframe 上下文执行脚本

这个方案的核心思想是避免直接从主页面访问 iframe 的 DOM 元素,而是通过 page.evaluate 方法将 JavaScript 代码注入到 iframe 的执行上下文中。 在 iframe 内部获取数据后返回,Puppeteer 即可在主页面上下文中接收结果。这样就规避了跨域限制。

步骤:

  1. 使用 page.waitForSelectorframe 定位到目标 iframe。
  2. 使用 frame.evaluate 在 iframe 中执行脚本。 此脚本可以访问 iframe 的文档并提取内容。
  3. 返回提取的内容。

代码示例:

const puppeteer = require('puppeteer');

async function getIframeContent(url) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url);

  try {
        //等待iframe加载
    await page.waitForSelector('iframe');
    const frame = page.frames().find(f => f.name() === 'target-iframe');  //target-iframe 需要替换为实际iframe的名称或ID,
    if(frame){
        const iframeContent = await frame.evaluate(() => {
            return document.body.innerHTML;  //获取 iframe body 内容,可以修改获取方式
        });
       
       console.log('iframe 内容:', iframeContent);
    }else {
      console.log("iframe 不存在或无法访问");
    }


  } catch (e){
    console.log('页面加载错误或没有 iframe', e)
  } finally{
     await browser.close();
  }
}
const targetUrl = 'https://your-page-with-iframe.com';  // 替换为实际 URL
getIframeContent(targetUrl);

说明: page.frames() 方法返回一个数组,其中包含页面中的所有 frame 对象, 可以根据名称或 ID 找到目标 frame。 在frame.evaluate的回调函数内,可以直接访问 iframe 的 document 对象。注意 target-iframe 需要替换为 iframe 的实际 name 或者id

方案二:服务器端请求 iframe 资源

另一种安全的方法是在服务器端直接请求 iframe 的 URL。 这种方法避免了所有客户端跨域问题, 但你需要拥有服务器来发出 HTTP 请求。 如果 iframe 内容是动态的,则这种方案可能会比较复杂。

步骤:

  1. 使用 Puppeteer 获取 iframe 的 src 属性。
  2. 在服务器端向该 src 地址发出请求。
  3. 服务器将 iframe 的内容返回给 Puppeteer。
  4. Puppeteer 使用返回的数据。

命令行指令/服务器代码 (Node.js):

使用 node-fetch 获取 iframe 内容

const fetch = require('node-fetch');
async function fetchIframeContent(iframeSrc) {
 try{
   const response = await fetch(iframeSrc);
    if(response.ok) {
      const iframeContent = await response.text();
      return iframeContent
    } else {
      console.log('请求iframe内容出错:', response.statusText);
        return null;
    }

 }catch(e){
   console.log("Fetch错误:", e);
     return null;
 }

}

async function main () {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto("https://your-page-with-iframe.com");  // 替换为包含iframe的实际页面 URL
     try{
    await page.waitForSelector("iframe");

       const frameElement = await page.$("iframe")
       const src = await page.evaluate(el => el.src, frameElement)


     if(src){
         const content =  await fetchIframeContent(src);
          console.log("iframe Content :", content);
        } else {
             console.log('iframe 没有 src');
        }
    } catch (e){
     console.log("解析错误或者iframe没有发现:", e)
   } finally {
      await browser.close();
    }
}

main();

说明: 首先通过 Puppeteer 定位 iframe 并提取 src 属性。 fetchIframeContent 函数使用 node-fetch 请求 iframe 地址并获取内容。 实际情况中需要根据具体情况处理响应, 例如处理 HTTP 错误或者进行内容解析。 使用服务器请求内容能避免客户端的跨域限制,同时保持本地安全。

方案三: 使用 Chrome 扩展程序

也可以通过开发 Chrome 扩展程序来实现内容读取。 扩展程序运行在浏览器上下文中,可以通过 manifest 配置读取 iframe 内容,并将数据传输到页面上的 JavaScript。此方案实现稍微复杂一些。

安全建议

在任何场景下,请注意处理 iframe 内容中可能存在的恶意脚本。
确保验证来自未知来源的 iframe 数据,避免执行未授权代码。如果使用第三方内容,请进行代码审查,必要时可以使用沙箱隔离。 如果能提前了解 iframe 的结构和内容类型, 可以进行专门的处理, 可以极大提高数据解析的效率和安全性。

选用方案时, 应综合考虑环境需求和安全需求, 在不同的业务场景下,上述不同解决方案各有利弊,没有最好的,只有最合适的。