Webスクレイピングにおけるブラウザプロキシトラフィック最適化ソリューション
はじめに
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アプリマニフェスト | PWA設定ファイル | PWA機能が影響を受ける可能性がある | ⭐ 安全 |
prefetch | プリフェッチリソース | <link rel="prefetch"> | ページへの影響は最小限 | ⭐ 安全 |
stylesheet | CSSスタイルシート | 外部CSSファイル | ページスタイルが失われ、レイアウトに影響する可能性がある | ⚠️ 注意 |
websocket | WebSocket | リアルタイム通信接続 | リアルタイム機能が無効になる | ⚠️ 注意 |
eventsource | サーバーセントイベント | サーバープッシュデータ | プッシュ機能が無効になる | ⚠️ 注意 |
preflight | CORSプリフライトリクエスト | OPTIONSリクエスト | クロスオリジンリクエストが失敗する | ⚠️ 注意 |
script | JavaScriptスクリプト | 外部JSファイル | 動的な機能が無効になり、SPAがレンダリングされない可能性がある | ❌ 避ける |
xhr | XHRリクエスト | AJAXデータリクエスト | 動的なデータを取得できなくなる | ❌ 避ける |
fetch | Fetchリクエスト | 最新の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(`Blocked: ${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('Scraping result:', data))
.catch(error => console.error('Scraping failed:', error));
最適化スキーム2:リクエストURLインターセプト
リソースタイプによるインターセプトに加えて、URLの特性に基づいてより詳細なインターセプト制御を実行できます。これは、広告、分析スクリプト、その他の不要なサードパーティリクエストをブロックするのに特に効果的です。
URLインターセプト戦略
- ドメインによるインターセプト: 特定のドメインからのすべてのリクエストをブロックする
- パスによるインターセプト: 特定のパスからのリクエストをブロックする
- ファイルタイプによるインターセプト: 特定の拡張子を持つファイルをブロックする
- キーワードによるインターセプト: URLに特定のキーワードが含まれるリクエストをブロックする
一般的なインターセプト可能なURLパターン
URLパターン | 説明 | 例 | 推奨度 |
---|---|---|---|
広告サービス | 広告ネットワークドメイン | ad.doubleclick.net , googleadservices.com | ⭐ 安全 |
分析サービス | 統計と分析スクリプト | google-analytics.com , hotjar.com | ⭐ 安全 |
ソーシャルメディアプラグイン | ソーシャル共有ボタンなど | platform.twitter.com , connect.facebook.net | ⭐ 安全 |
トラッキングピクセル | ユーザー行動を追跡するピクセル | pixel 、beacon 、tracker を含むURL | ⭐ 安全 |
大規模メディアファイル | 大規模なビデオ、音声ファイル | .mp4 、.webm 、.mp3 などの拡張子 | ⭐ 安全 |
フォントサービス | オンラインフォントサービス | fonts.googleapis.com , use.typekit.net | ⭐ 安全 |
CDNリソース | 静的リソースCDN | cdn.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(`Blocked domain: ${url.substring(0, 50)}...`);
return;
}
// パスをチェックする
if (BLOCKED_PATHS.some(path => url.includes(path))) {
request.abort();
console.log(`Blocked path: ${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('Scraping result:', data))
.catch(error => console.error('Scraping failed:', error));
最適化スキーム3:モバイルデバイスのシミュレーション
モバイルデバイスのシミュレーションは、モバイルウェブサイトは通常、より軽量なページコンテンツを提供するため、もう1つの効果的なトラフィック最適化戦略です。
モバイルデバイスシミュレーションの利点
- 軽量なページバージョン: 多くのウェブサイトは、モバイルデバイスに対してより簡潔なコンテンツを提供しています
- 小さい画像リソース: モバイルバージョンは通常、より小さい画像を読み込みます
- 簡素化された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, {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('Scraping result:', data))
.catch(error => console.error('Scraping failed:', 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(`Starting optimized scraping: ${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(`Blocked resource type: ${resourceType} - ${url.substring(0, 50)}...`);
request.abort();
return;
}
// ドメインをチェックする
if (BLOCKED_DOMAINS.some(domain => url.includes(domain))) {
console.log(`Blocked domain: ${url.substring(0, 50)}...`);
request.abort();
return;
}
// パスをチェックする
if (BLOCKED_PATHS.some(path => url.includes(path))) {
console.log(`Blocked path: ${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(`\nTraffic Usage Statistics:`);
console.log(`Used: ${(totalBytesUsed / 1024 / 1024).toFixed(2)} MB`);
await browser.close();
return data;
}
// 使用例
optimizedScraping('https://www.scrapeless.com')
.then(data => console.log('Scraping complete:', data))
.catch(error => console.error('Scraping failed:', 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(`Starting optimized scraping: ${url}`);
// Record traffic usage
let totalBytesUsed = 0;
const browser = await puppeteer.connect({
browserWSEndpoint: scrapelessUrl,
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
optimizedScraping('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% |
上記の方法を組み合わせることで、プロキシのトラフィック消費量を大幅に削減し、スクレイピング効率を向上させ、必要なコアコンテンツの取得を確保できます。