コストの最適化
はじめに
Puppeteer を使用してデータをスクレイピングする場合、トラフィックの消費量は重要な考慮事項です。特にプロキシサービスを使用する場合、トラフィックコストが大幅に増加する可能性があります。トラフィック使用量を最適化するためには、以下の戦略を採用できます。
- リソースのインターセプト:不要なリソースリクエストをブロックすることで、トラフィック消費を削減できます。
- リクエストURLのインターセプト:特定のリクエストをURLの特性に基づいてブロックすることで、さらにトラフィックを削減します。
- モバイルデバイスのシミュレーション:モバイルデバイスの設定を使用して、軽量なページバージョンを取得します。
- 包括的な最適化:上記の手法を組み合わせることで、最適な結果を実現します。
最適化スキーム1:リソースのインターセプト
リソースのインターセプトの紹介
Puppeteerでは、 page.setRequestInterception(true) がブラウザによって開始されたすべてのネットワークリクエストをキャプチャし、 続行 (request.continue())、 中止 (request.abort())、または レスポンスのカスタマイズ (request.respond())を決定できます。
この方法は帯域幅の消費を大幅に削減でき、特に クロール、 スクリーンショット取得、および パフォーマンス最適化 のシナリオに適しています。
のシナリオに適しており、帯域幅の消費を大幅に削減できます。
| リソースタイプ | 説明 | 例 | インターセプト後の影響 | 推奨度 |
|---|---|---|---|---|
image | 画像リソース | JPG/PNG/GIF/WebP画像 | 画像が表示されない | ⭐ 安全 |
font | フォントファイル | TTF/WOFF/WOFF2フォント | システムの既定フォントが代わりに使用される | ⭐ 安全 |
media | メディアファイル | 動画/音声ファイル | メディアコンテンツが再生できない | ⭐ 安全 |
manifest | Web App Manifest | PWA設定ファイル | PWA機能に影響が出る可能性がある | ⭐ 安全 |
prefetch | プリフェッチリソース | <link rel="prefetch"> | ページへの影響は最小限 | ⭐ 安全 |
stylesheet | CSS スタイルシート | 外部のCSSファイル | ページのスタイルが失われ、レイアウトに影響が出る可能性がある | ⚠️ 注意 |
websocket | WebSocket | リアルタイム通信接続 | リアルタイム機能が無効になる | ⚠️ 注意 |
eventsource | Server-Sent Events | サーバープッシュデータ | プッシュ機能が無効になる | ⚠️ 注意 |
preflight | CORSプリフライトリクエスト | OPTIONSリクエスト | クロスオリジンリクエストが失敗する | ⚠️ 注意 |
script | JavaScriptスクリプト | 外部JSファイル | 動的機能が無効になり、SPAがレンダリングされない可能性がある | ❌ 避ける |
xhr | XHRリクエスト | AJAXデータリクエスト | 動的データを取得できなくなる | ❌ 避ける |
fetch | Fetchリクエスト | 現代の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インターセプション戦略
- ドメインごとにインターセプト:特定のドメインからのすべてのリクエストをブロック
- パスごとにインターセプト:特定のパスからのリクエストをブロック
- ファイルタイプごとにインターセプト:特定の拡張子を持つファイルをブロック
- キーワードごとにインターセプト: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リソース | 静的リソースCDN | cdn.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:モバイル端末のシミュレーション
モバイル端末のシミュレーションは、モバイルサイトが通常軽量なページコンテンツを提供するため、別の有効なトラフィック最適化戦略です。
モバイル端末シミュレーションの利点
- 軽量なページバージョン:多くのウェブサイトはモバイルデバイス向けに簡潔なコンテンツを提供しています
- 小さい画像リソース:モバイル版では通常、より小さいサイズの画像が読み込まれます
- 簡略化されたCSSおよびJavaScript:モバイル版では通常、スタイルとスクリプトが簡略化されています
- 広告および非コアコンテンツの削減:モバイル版では、非コア機能の一部が削除されることがよくあります
- アダプティブなレスポンス:小画面向けに最適化されたコンテンツレイアウトを取得
モバイル端末シミュレーションの設定
以下は、よく使用されるモバイル端末の設定パラメータです:
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 % |
上記の最適化スキームを組み合わせることで、プロキシトラフィックの消費を大幅に削減し、スクレイピング効率を向上させ、必要なコアコンテンツを確実に取得できます。