ドキュメントブラウザーと CrawlAgent 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 App ManifestPWA設定ファイルPWA機能に影響が出る可能性がある⭐ 安全
prefetchプリフェッチリソース<link rel="prefetch">ページへの影響は最小限⭐ 安全
stylesheetCSS スタイルシート外部のCSSファイルページのスタイルが失われ、レイアウトに影響が出る可能性がある⚠️ 注意
websocketWebSocketリアルタイム通信接続リアルタイム機能が無効になる⚠️ 注意
eventsourceServer-Sent Eventsサーバープッシュデータプッシュ機能が無効になる⚠️ 注意
preflightCORSプリフライトリクエストOPTIONSリクエストクロスオリジンリクエストが失敗する⚠️ 注意
scriptJavaScriptスクリプト外部JSファイル動的機能が無効になり、SPAがレンダリングされない可能性がある❌ 避ける
xhrXHRリクエストAJAXデータリクエスト動的データを取得できなくなる❌ 避ける
fetchFetchリクエスト現代のAJAXリクエスト動的データを取得できなくなる❌ 避ける
documentメインドキュメントHTMLページ自体ページが読み込めない❌ 避ける

推奨レベルの説明:

  • ⭐ 安全:インターセプトはデータスクレイピングや初回画面のレンダリングにほとんど影響を与えません。デフォルトでブロックすることを推奨します。
  • ⚠️ 注意:スタイル、リアルタイム機能、またはクロスオリジンリクエストが壊れる可能性があります。ビジネス側での判断が必要です。
  • ❌ 避けるべき:SPAや動的サイトの正常なレンダリングやデータ取得ができなくなる可能性が高いため、これらのリソースが不要であると確信している場合を除き使用しないでください。

リソースインターセプションのコード例

import { Scrapeless } from '@scrapeless-ai/sdk';
import puppeteer from 'puppeteer-core';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function scrapeWithResourceBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // Enable request interception
    await page.setRequestInterception(true);
 
    // Define resource types to block
    const BLOCKED_TYPES = new Set([
        'image',
        'font',
        'media',
        'stylesheet',
    ]);
 
    // Intercept requests
    page.on('request', (request) => {
        if (BLOCKED_TYPES.has(request.resourceType())) {
            request.abort();
            console.log(`Blocked: ${request.resourceType()} - ${request.url().substring(0, 50)}...`);
        } else {
            request.continue();
        }
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // Extract data
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Usage
scrapeWithResourceBlocking('https://www.scrapeless.com')
    .then(data => console.log('Scraping result:', data))
    .catch(error => console.error('Scraping failed:', error));

最適化スキーム2:URLリクエストのインターセプション

リソースタイプによるインターセプションに加えて、URLの特性に基づいてより細かいレベルのインターセプション制御を行うことができます。これは、広告、アナリティクススクリプト、その他の不要なサードパーティリクエストをブロックする場合に特に有効です。

URLインターセプション戦略

  1. ドメインごとにインターセプト:特定のドメインからのすべてのリクエストをブロック
  2. パスごとにインターセプト:特定のパスからのリクエストをブロック
  3. ファイルタイプごとにインターセプト:特定の拡張子を持つファイルをブロック
  4. キーワードごとにインターセプト:URLsに特定のキーワードを含むリクエストをブロック

よくブロックされるURLのパターン

URLのパターン説明例推奨度
広告サービス広告ネットワークのドメインad.doubleclick.net, googleadservices.com⭐ 安全
アナリティクスサービス統計およびアナリティクススクリプトgoogle-analytics.com, hotjar.com⭐ 安全
ソーシャルメディアプラグインソーシャル共有ボタンなどplatform.twitter.com, connect.facebook.net⭐ 安全
トラッキングピクセルユーザーの行動を追跡するピクセルURLsに含まれる pixel、 beacon、 tracker⭐ 安全
大きなメディアファイル大きな動画、音声ファイル拡張子例: .mp4、 .webm、 .mp3⭐ 安全
フォントサービスオンラインフォントサービスfonts.googleapis.com, use.typekit.net⭐ 安全
CDNリソース静的リソースCDNcdn.jsdelivr.net, unpkg.com⚠️ 注意

URLインターセプションのコード例

import puppeteer from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
import puppeteer from 'puppeteer-core';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function scrapeWithUrlBlocking(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
    const page = await browser.newPage();
 
    // Enable request interception
    await page.setRequestInterception(true);
 
    // Define domains and URL patterns to block
    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/',
    ];
 
    // Intercept requests
    page.on('request', (request) => {
        const url = request.url();
 
        // Check domain
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            request.abort();
            console.log(`Blocked domain: ${url.substring(0, 50)}...`);
            return;
        }
 
        // Check path
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            request.abort();
            console.log(`Blocked path: ${url.substring(0, 50)}...`);
            return;
        }
 
        // Allow other requests
        request.continue();
    });
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
 
    // Extract data
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Usage
scrapeWithUrlBlocking('https://www.scrapeless.com')
    .then(data => console.log('Scraping result:', data))
    .catch(error => console.error('Scraping failed:', 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 from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function scrapeWithMobileEmulation(url) {
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // Set mobile device simulation
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    await page.goto(url, {waitUntil: 'domcontentloaded'});
    // Extract data
    const data = await page.evaluate(() => {
        return {
            title: document.title,
            content: document.body.innerText.substring(0, 1000)
        };
    });
 
    await browser.close();
    return data;
}
 
// Usage
scrapeWithMobileEmulation('https://www.scrapeless.com')
    .then(data => console.log('Scraping result:', data))
    .catch(error => console.error('Scraping failed:', error));

包括的な最適化の例

以下は、すべての最適化スキームを組み合わせた包括的な例です:

import puppeteer, {KnownDevices} from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function optimizedScraping(url) {
    console.log(`Starting optimized scraping: ${url}`);
 
    // Record traffic usage
    let totalBytesUsed = 0;
 
    const browser = await puppeteer.connect({
        browserWSEndpoint,
        defaultViewport: null
    });
 
    const page = await browser.newPage();
 
    // Set mobile device simulation
    const iPhone = KnownDevices['iPhone 15 Pro'];
    await page.emulate(iPhone);
 
    // Set request interception
    await page.setRequestInterception(true);
 
    // Define resource types to block
    const BLOCKED_TYPES = [
        'image',
        'media',
        'font'
    ];
 
    // Define domains to block
    const BLOCKED_DOMAINS = [
        'google-analytics.com',
        'googletagmanager.com',
        'facebook.net',
        'doubleclick.net',
        'adservice.google.com'
    ];
 
    // Define URL paths to block
    const BLOCKED_PATHS = [
        '/ads/',
        '/analytics/',
        '/tracking/'
    ];
 
    // Intercept requests
    page.on('request', (request) => {
        const url = request.url();
        const resourceType = request.resourceType();
 
        // Check resource type
        if (BLOCKED_TYPES.includes(resourceType)) {
            console.log(`Blocked resource type: ${resourceType} - ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Check domain
        if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
            console.log(`Blocked domain: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Check path
        if (BLOCKED_PATHS.some(path => url.includes(path))) {
            console.log(`Blocked path: ${url.substring(0, 50)}...`);
            request.abort();
            return;
        }
 
        // Allow other requests
        request.continue();
    });
 
    // Monitor network traffic
    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'});
 
    // Simulate scrolling to trigger lazy-loading content
    await page.evaluate(() => {
        window.scrollBy(0, window.innerHeight);
    });
 
    await new Promise(resolve => setTimeout(resolve, 1000))
 
    // Extract data
    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
            }))
        };
    });
 
    // Output traffic usage statistics
    console.log(`\nTraffic Usage Statistics:`);
    console.log(`Used: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
    await browser.close();
    return data;
}
 
// Usage
optimizedScraping('https://www.scrapeless.com')
    .then(data => console.log('Scraping complete:', data))
    .catch(error => console.error('Scraping failed:', error));

最適化の比較

包括的な例から最適化コードを削除して、最適化前後のトラフィックを比較してみましょう。以下は最適化されていないコード例です:

import puppeteer from 'puppeteer-core';
import { Scrapeless } from '@scrapeless-ai/sdk';
 
const client = new Scrapeless({ apiKey: 'API Key' });
 
const { browserWSEndpoint } = client.browser.create({
    sessionName: 'sdk_test',
    sessionTTL: 180,
    proxyCountry: 'ANY',
    sessionRecording: true,
    fingerprint,
});
 
async function unoptimizedScraping(url) {
  console.log(`Starting unoptimized scraping: ${url}`);
 
  // Record traffic usage
  let totalBytesUsed = 0;
 
  const browser = await puppeteer.connect({
    browserWSEndpoint,
    defaultViewport: null
  });
 
  const page = await browser.newPage();
 
  // Set request interception
  await page.setRequestInterception(true);
 
  // Intercept requests
  page.on('request', (request) => {
    request.continue();
  });
 
  // Monitor network traffic
  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'});
 
  // Simulate scrolling to trigger lazy-loading content
  await page.evaluate(() => {
    window.scrollBy(0, window.innerHeight);
  });
 
  await new Promise(resolve => setTimeout(resolve, 1000))
 
  // Extract data
  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
      }))
    };
  });
 
  // Output traffic usage statistics
  console.log(`\nTraffic Usage Statistics:`);
  console.log(`Used: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
 
  await browser.close();
  return data;
}
 
// Usage
unoptimizedScraping('https://www.scrapeless.com')
  .then(data => console.log('Scraping complete:', data))
  .catch(error => console.error('Scraping failed:', error));

最適化されていないコードを実行すると、出力された情報からトラフィックの差を非常に直感的に確認できます:

シナリオ使用したトラフィック (MB)節約率
非最適化6.03—
最適化0.81≈ 86.6 %

上記の最適化スキームを組み合わせることで、プロキシトラフィックの消費を大幅に削減し、スクレイピング効率を向上させ、必要なコアコンテンツを確実に取得できます。