Scraping Browser指南优化成本

Scraping browser 代理流量优化方案

介绍

在使用 Puppeteer 进行数据抓取时,流量消耗是一个重要的考虑因素。尤其是在使用代理服务时,流量费用可能会显著增加。为了优化流量使用,我们可以采取以下几种策略:

  1. 资源拦截:通过拦截不必要的资源请求来减少流量消耗。
  2. 请求 URL 拦截:根据 URL 特征拦截特定请求,进一步减少流量。
  3. 模拟移动设备:使用移动设备配置来获取更轻量级的页面版本。
  4. 综合优化:结合以上方法,达到最佳效果。

优化方案1:资源拦截

资源拦截简介

在 Puppeteer 中,通过 page.setRequestInterception(true) 可以捕获浏览器发起的每一次网络请求,并决定继续( request.continue())、终止(request.abort())或自定义响应(request.respond())。

这种方法可以显著减少带宽消耗,特别适用于爬虫截图性能优化场景。

可拦截资源类型及建议

资源类型说明示例拦截后影响推荐
image图片资源JPG/PNG/GIF/WebP 等图片页面不显示图片内容⭐ 安全
font字体文件TTF/WOFF/WOFF2 等字体使用系统默认字体替代⭐ 安全
media媒体文件视频/音频文件媒体内容无法播放⭐ 安全
manifestWeb 应用清单PWA 配置文件PWA 功能可能受影响⭐ 安全
prefetch预取资源<link rel="prefetch">对页面影响较小⭐ 安全
stylesheetCSS 样式表外部 CSS 文件页面样式丢失,可能影响布局⚠️ 谨慎
websocketWebSocket实时通信连接实时功能失效⚠️ 谨慎
eventsource服务器发送事件服务器推送数据推送功能失效⚠️ 谨慎
preflightCORS 预检请求OPTIONS 请求跨域请求失败⚠️ 谨慎
scriptJavaScript 脚本外部 JS 文件动态功能失效,SPA 可能无法渲染❌ 避免
xhrXHR 请求AJAX 数据请求无法获取动态数据❌ 避免
fetchFetch 请求现代 AJAX 请求无法获取动态数据❌ 避免
document主文档HTML 页面本身页面无法加载❌ 避免

推荐拦截等级说明:

  • 安全:拦截后几乎不影响数据抓取或首屏渲染,建议默认阻断。
  • ⚠️ 谨慎:可能破坏样式、实时功能或跨域请求,需要按业务判断。
  • 避免:高概率导致 SPA/动态站点无法正常渲染或获取数据,除非你非常确定不需要这些资源。

资源拦截示例代码

import puppeteer from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function scrapeWithResourceBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // 启用请求拦截
    await page.setRequestInterception(true);
 
    // 定义要阻止的资源类型
    const BLOCKED_TYPES = new Set([
        'image',
        'font',
        'media',
        'stylesheet',
    ]);
 
    // 拦截请求
    page.on('request', (request) => {
        if (BLOCKED_TYPES.has(request.resourceType())) {
            request.abort();
            console.log(`已阻止: ${request.resourceType()} - ${request.url().substring(0, 50)}...`);
        } else {
            request.continue();
        }
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // 提取数据
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// 使用方法
scrapeWithResourceBlocking('https://www.scrapeless.com')
    .then(data => console.log('爬取结果:', data))
    .catch(error => console.error('爬取失败:', error));

优化方案2:请求URL拦截

除了按资源类型拦截外,还可以根据URL特征进行更精细的拦截控制,这对于阻止广告、分析脚本和其他不必要的第三方请求特别有效。

URL拦截策略

  1. 按域名拦截:阻止特定域名的所有请求
  2. 按路径拦截:阻止特定路径的请求
  3. 按文件类型拦截:阻止特定扩展名的文件
  4. 按关键词拦截:阻止URL中包含特定关键词的请求

常见可拦截URL模式

URL模式说明示例推荐
广告服务广告网络域名ad.doubleclick.net, googleadservices.com⭐ 安全
分析服务统计和分析脚本google-analytics.com, hotjar.com⭐ 安全
社交媒体插件社交分享按钮等platform.twitter.com, connect.facebook.net⭐ 安全
追踪像素跟踪用户行为的像素URL包含pixel, beacon, tracker⭐ 安全
大型媒体文件大型视频、音频文件扩展名为.mp4, .webm, .mp3⭐ 安全
字体服务在线字体服务fonts.googleapis.com, use.typekit.net⭐ 安全
CDN资源静态资源CDNcdn.jsdelivr.net, unpkg.com⚠️ 谨慎

URL拦截示例代码

import puppeteer from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function scrapeWithUrlBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // 启用请求拦截
    await page.setRequestInterception(true);
 
    // 定义要阻止的域名和URL模式
    const BLOCKED_DOMAINS = [
        'google-analytics.com',
        'googletagmanager.com',
        'doubleclick.net',
        'facebook.net',
        'twitter.com',
        'linkedin.com',
        'adservice.google.com',
    ];
 
    const BLOCKED_PATHS = [
        '/ads/',
        '/analytics/',
        '/pixel/',
        '/tracking/',
        '/stats/',
    ];
 
    // 拦截请求
    page.on('request', (request) => {
        const url = request.url();
 
        // 检查域名
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            request.abort();
            console.log(`已阻止域名: ${url.substring(0, 50)}...`);
            return;
        }
 
        // 检查路径
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            request.abort();
            console.log(`已阻止路径: ${url.substring(0, 50)}...`);
            return;
        }
 
        // 允许其他请求
        request.continue();
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // 提取数据
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// 使用方法
scrapeWithUrlBlocking('https://www.scrapeless.com')
    .then(data => console.log('爬取结果:', data))
    .catch(error => console.error('爬取失败:', error));

优化方案3:模拟移动设备

模拟移动设备是另一种有效的流量优化策略,因为移动版网站通常提供更轻量级的页面内容。

移动设备模拟的优势

  1. 更轻量级的页面版本:许多网站为移动设备提供更精简的内容
  2. 更小的图片资源:移动版通常加载更小尺寸的图片
  3. 简化的CSS和JavaScript:移动版通常使用更简化的样式和脚本
  4. 减少广告和非核心内容:移动版常常去除部分非核心功能
  5. 自适应响应:可以获取到为小屏幕优化的内容布局

移动设备模拟配置

以下是几种常用移动设备的配置参数:

const iPhoneX = {
    viewport: {
        width: 375,
        height: 812,
        deviceScaleFactor: 3,
        isMobile: true,
        hasTouch: true,
        isLandscape: false
    }
};

或直接使用 puppeteer 内置的方法来模拟移动设备

import { KnownDevices } from 'puppeteer-core';
const iPhone = KnownDevices['iPhone 15 Pro'];
 
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(iPhone);

移动设备模拟示例代码

import puppeteer, {KnownDevices} from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function scrapeWithMobileEmulation(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // 设置移动设备模拟
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
    // 提取数据
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// 使用方法
scrapeWithMobileEmulation('https://www.scrapeless.com')
    .then(data => console.log('爬取结果:', data))
    .catch(error => console.error('爬取失败:', error));

综合优化示例

以下是结合所有优化方案的综合示例:

import puppeteer, {KnownDevices} from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function optimizedScraping(url) {
    console.log(`开始优化爬取: ${url}`);
 
    // 记录流量使用情况
    let totalBytesUsed = 0;
 
    const browser = await puppeteer.connect({
        browserWSEndpoint: scrapelessUrl,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // 设置移动设备模拟
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    // 设置请求拦截
    await page.setRequestInterception(true);
 
    // 定义要阻止的资源类型
    const BLOCKED_TYPES = [
        'image',
        'media',
        'font'
    ];
 
    // 定义要阻止的域名
    const BLOCKED_DOMAINS = [
        'google-analytics.com',
        'googletagmanager.com',
        'facebook.net',
        'doubleclick.net',
        'adservice.google.com'
    ];
 
    // 定义要阻止的URL路径
    const BLOCKED_PATHS = [
        '/ads/',
        '/analytics/',
        '/tracking/'
    ];
 
    // 拦截请求
    page.on('request', (request) => {
        const url = request.url();
        const resourceType = request.resourceType();
 
        // 检查资源类型
        if (BLOCKED_TYPES.includes(resourceType)) {
            console.log(`已阻止资源类型: ${resourceType} - ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // 检查域名
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            console.log(`已阻止域名: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // 检查路径
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            console.log(`已阻止路径: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // 允许其他请求
        request.continue();
    });
 
    // 监控网络流量
    page.on('response', async (response) => {
        const headers = response.headers();
        const contentLength = headers['content-length'] ? parseInt(headers['content-length'], 10) : 0;
        totalBytesUsed += contentLength;
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // 模拟滚动以触发懒加载内容
    await page.evaluate(() => {
        window.scrollBy(0, window.innerHeight);
    });
 
    await new Promise(resolve => setTimeout(resolve, 1000))
 
    // 提取数据
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000),
            links: Array.from(document.querySelectorAll('a')).slice(0, 10).map(a => ({
                text: a.innerText,
                href: a.href
            }))
        };
    });
 
    // 输出流量使用统计
    console.log(`\n流量使用统计:`);
    console.log(`已使用: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
    await browser.close();
    return data;
}
 
// 使用方法
optimizedScraping('https://www.scrapeless.com')
    .then(data => console.log('爬取完成:', data))
    .catch(error => console.error('爬取失败:', error));

优化对比

我们尝试去掉综合示例的优化代码,来对比一下优化前与优化后的流量对比。以下是未优化的示例代码:

import puppeteer from 'puppeteer-core';
 
const scrapelessUrl = 'wss://browser.scrapeless.com/browser?token=your_api_key&session_ttl=180&proxy_country=ANY';
 
async function optimizedScraping(url) {
  console.log(`开始优化爬取: ${url}`);
 
  // 记录流量使用情况
  let totalBytesUsed = 0;
 
  const browser = await puppeteer.connect({
    browserWSEndpoint: scrapelessUrl,
    defaultViewport: null
  });
 
  const page = await browser.newPage();
 
  // 设置请求拦截
  await page.setRequestInterception(true);
 
  // 拦截请求
  page.on('request', (request) => {
    request.continue();
  });
 
  // 监控网络流量
  page.on('response', async (response) => {
    const headers = response.headers();
    const contentLength = headers['content-length'] ? parseInt(headers['content-length'], 10) : 0;
    totalBytesUsed += contentLength;
  });
 
  await page.goto(url, {waitUntil: 'domcontentloaded'});
 
  // 模拟滚动以触发懒加载内容
  await page.evaluate(() => {
    window.scrollBy(0, window.innerHeight);
  });
 
  await new Promise(resolve => setTimeout(resolve, 1000))
 
  // 提取数据
  const data = await page.evaluate(() => {
    return {
      title: document.title,
      content: document.body.innerText.substring(0, 1000),
      links: Array.from(document.querySelectorAll('a')).slice(0, 10).map(a => ({
        text: a.innerText,
        href: a.href
      }))
    };
  });
 
  // 输出流量使用统计
  console.log(`\n流量使用统计:`);
  console.log(`已使用: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
  await browser.close();
  return data;
}
 
// 使用方法
optimizedScraping('https://www.scrapeless.com')
  .then(data => console.log('爬取完成:', data))
  .catch(error => console.error('爬取失败:', error));

运行未优化代码后,我们可以根据打印信息很直观的看到流量的差距:

场景已使用流量 (MB)节省比例
未优化6.03
优化后0.81≈ 86.6 %

通过结合以上优化方案,可以显著减少代理流量消耗,提高爬取效率,同时确保获取到所需的核心内容。