現代のウェブサイトは、自動化されたブラウザ(Selenium、Puppeteer、Playwright)を認識し、そのリクエストをブロックする方法を学びました。ヘッドレスブラウザは、アンチボットシステムがミリ秒単位で自動化を検出するためのデジタル痕跡を数十残します。このガイドでは、PythonとJavaScriptのコード例を用いて、ヘッドレスブラウザのマスキング手法をすべて解説し、あなたのパーサーが安定してブロックなしで動作するようにします。
この記事は、ウェブスクレイピング、自動化テスト、または保護されたサイトからのデータ収集を行う開発者を対象としています。検出の技術的詳細と保護を回避するための実用的な解決策を考察します。
ウェブサイトがヘッドレスブラウザを検出する方法: 主な手法
アンチボットシステムは、ブラウザの多層チェックを使用し、数百のパラメータを分析します。ヘッドレスブラウザは、普通のブラウザとは異なる多くの特徴を持っており、単純にUser-Agentを置き換えるだけでは隠すことができません。検出手法を理解することは、効果的なマスキングへの第一歩です。
自動化のJavaScriptマーカー
最も一般的な方法は、自動化されたブラウザにのみ現れるJavaScriptプロパティのチェックです:
navigator.webdriver— SeleniumおよびPuppeteerではtrueを返しますwindow.chrome— ヘッドレスChromeには存在しませんnavigator.plugins.length— ヘッドレスモードでは0ですnavigator.languages— よく空の配列か「en-US」だけを含みますnavigator.permissions— APIはヘッドレスモードで異なる動作をします
Chrome DevToolsプロトコルの分析
PuppeteerとPlaywrightは、Chrome DevToolsプロトコル(CDP)を介してブラウザを制御します。CDP接続の存在は、window.cdc_オブジェクトを分析する特別なJavaScriptチェックを通じて検出できます。また、マウスやキーボードのイベントの異常な動作をチェックします。
CanvasおよびWebGLフィンガープリンティング
ヘッドレスブラウザは、ソフトウェアレンダリングを使用するため、同じCanvasおよびWebGLフィンガープリントを生成します。アンチボットシステムは、見えないCanvas要素を作成し、その上にテキストや図形を描画し、画像のハッシュを計算します。もし何千人ものユーザーが同じハッシュを持っている場合、それはボットの兆候です。
行動分析
現代のシステム(DataDome、PerimeterX、Cloudflare Bot Management)は、マウスの動き、スクロール速度、クリックパターンを分析します。ヘッドレスブラウザは、自然な遅延なしに瞬時にアクションを実行するため、自動化が明らかになります。また、イベントも分析されます: 通常のブラウザでは、クリックの前に必ずmousemoveイベントが発生しますが、ボットはしばしばマウスの動きなしにクリックします。
重要: 現代のアンチボットシステムは、同時に数百のパラメータを分析するために機械学習を使用します。単一のパラメータ(例えばUser-Agent)をマスキングするだけではブロックから保護されません — 包括的なアプローチが必要です。
navigator.webdriverおよびその他のJavaScriptマーカーの削除
navigator.webdriverプロパティは、Seleniumや他のWebDriverツールを検出する最も簡単な方法です。通常のブラウザではこのプロパティはundefinedを返しますが、自動化されたブラウザではtrueを返します。ページの読み込み前にJavaScriptコードを実行することで削除できます。
Selenium(Python):CDPを介してwebdriverを削除
Seleniumでは、ページの読み込み前にJavaScriptを実行するためにChrome DevToolsプロトコルを使用する必要があります:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('--disable-blink-features=AutomationControlled')
driver = webdriver.Chrome(options=options)
# CDPを介してnavigator.webdriverを削除
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': '''
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
'''
})
driver.get('https://example.com')
オプション--disable-blink-features=AutomationControlledは、Chromeが自動化モードで追加するフラグを無効にします。これは、他の方法と組み合わせる必要がある基本的な保護です。
Puppeteer(Node.js):Page.evaluateOnNewDocumentを介してマスキング
Puppeteerでは、ページの読み込み前にコードを実行するためにpage.evaluateOnNewDocument()メソッドを使用します:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--disable-blink-features=AutomationControlled']
});
const page = await browser.newPage();
// webdriverおよびその他のマーカーを削除
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
// chromeオブジェクトの追加
window.chrome = {
runtime: {}
};
// プラグインのエミュレーション
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5]
});
});
await page.goto('https://example.com');
})();
Playwright: 組み込みのマスキングオプション
Playwrightは、デフォルトでより高度なマスキングを提供しますが、追加の設定が依然として必要です:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({
headless: true,
args: ['--disable-blink-features=AutomationControlled']
});
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
});
const page = await context.newPage();
await page.addInitScript(() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
});
await page.goto('https://example.com');
})();
Chrome DevToolsプロトコルのマスキング
PuppeteerとPlaywrightは、CDP接続の痕跡を残し、これをwindowオブジェクトを分析することで検出できます。アンチボットシステムは、cdc_、$cdc_、または__webdriver_というプレフィックスを持つ変数を探します。これらは、DevToolsプロトコルを介して接続する際にChromeによって作成されます。
CDP変数の削除
次のスクリプトは、windowオブジェクトから自動化に関連するすべての変数を削除します:
await page.evaluateOnNewDocument(() => {
// cdc_で始まるすべての変数を削除
const cdcProps = Object.keys(window).filter(prop =>
prop.includes('cdc_') ||
prop.includes('$cdc_') ||
prop.includes('__webdriver_')
);
cdcProps.forEach(prop => {
delete window[prop];
});
// CDPを隠すためにdocument.__proto__を再定義
const originalQuery = document.querySelector;
document.querySelector = function(selector) {
if (selector.includes('cdc_')) return null;
return originalQuery.call(this, selector);
};
});
パッチされたChromiumバージョンの使用
CDPの痕跡を残さない修正されたChromiumビルドが存在します。例えば、puppeteer-extraライブラリは、puppeteer-extra-plugin-stealthプラグインを使用して、CDPをマスキングするための数十のパッチを自動的に適用します。
User-AgentおよびHTTPヘッダーの設定
ヘッドレスブラウザは、古いまたは非現実的なUser-Agent文字列を使用します。例えば、PuppeteerはデフォルトでUser-Agentに「HeadlessChrome」という単語を追加します。さらに、通常のブラウザのリクエストに存在する追加のヘッダーを設定する必要があります。
マスキング用の最新のUser-Agent
現実のブラウザの新しいUser-Agentを使用してください。2024年の例は以下の通りです:
# Windows 10のChrome 120
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
# macOSのChrome 120
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
# WindowsのFirefox 121
Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Seleniumでのヘッダーの設定
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36')
# CDPを介した追加のヘッダー
driver.execute_cdp_cmd('Network.setUserAgentOverride', {
"userAgent": 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
"platform": 'Win32',
"acceptLanguage": 'en-US,en;q=0.9'
})
Puppeteerでのヘッダーの設定
await page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36');
await page.setExtraHTTPHeaders({
'Accept-Language': 'en-US,en;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-User': '?1',
'Sec-Fetch-Dest': 'document'
});
Sec-Fetch-*ヘッダーは非常に重要です — これらはChrome 76以降に登場し、これが欠如していると古いブラウザやボットが明らかになります。
CanvasおよびWebGLフィンガープリンティングのエミュレーション
CanvasおよびWebGLフィンガープリンティングは強力な検出手法です。なぜなら、ヘッドレスブラウザは同一のフィンガープリンティングを生成するからです。アンチボットシステムは、見えないCanvasを作成し、その上にテキストを描画し、ピクセルのハッシュを計算します。もし何千ものリクエストが同じハッシュを持っている場合、それはボットです。
Canvasにノイズを追加
次のスクリプトは、Canvasフィンガープリンティングにランダムなノイズを追加し、各リクエストをユニークにします:
await page.evaluateOnNewDocument(() => {
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
const originalToBlob = HTMLCanvasElement.prototype.toBlob;
const originalGetImageData = CanvasRenderingContext2D.prototype.getImageData;
// ノイズを追加する関数
const addNoise = (canvas, context) => {
const imageData = originalGetImageData.call(context, 0, 0, canvas.width, canvas.height);
for (let i = 0; i < imageData.data.length; i += 4) {
imageData.data[i] += Math.floor(Math.random() * 10) - 5;
imageData.data[i + 1] += Math.floor(Math.random() * 10) - 5;
imageData.data[i + 2] += Math.floor(Math.random() * 10) - 5;
}
context.putImageData(imageData, 0, 0);
};
HTMLCanvasElement.prototype.toDataURL = function() {
addNoise(this, this.getContext('2d'));
return originalToDataURL.apply(this, arguments);
};
});
WebGLパラメータのエミュレーション
WebGLは、グラフィックカードやドライバーに関する情報を明らかにします。ヘッドレスモードでは、これらのパラメータはソフトウェアレンダリングを示します:
await page.evaluateOnNewDocument(() => {
const getParameter = WebGLRenderingContext.prototype.getParameter;
WebGLRenderingContext.prototype.getParameter = function(parameter) {
// 実際のグラフィックカードをエミュレート
if (parameter === 37445) {
return 'Intel Inc.';
}
if (parameter === 37446) {
return 'Intel Iris OpenGL Engine';
}
return getParameter.call(this, parameter);
};
});
パラメータ37445はUNMASKED_VENDOR_WEBGL、37446はUNMASKED_RENDERER_WEBGLです。ヘッドレスモードでは、これらは「Google SwiftShader」を返し、自動化を示します。
Selenium Stealth: Python用の既製ソリューション
selenium-stealthライブラリは、Seleniumのマスキングのために数十のパッチを自動的に適用します。これは、Python開発者にとって最も簡単なソリューションで、各パラメータの手動設定を必要としません。
インストールと基本設定
pip install selenium-stealth
from selenium import webdriver
from selenium_stealth import stealth
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
driver = webdriver.Chrome(options=options)
# ステルスパッチを適用
stealth(driver,
languages=["en-US", "en"],
vendor="Google Inc.",
platform="Win32",
webgl_vendor="Intel Inc.",
renderer="Intel Iris OpenGL Engine",
fix_hairline=True,
)
driver.get("https://bot.sannysoft.com")
driver.save_screenshot("test.png")
このライブラリは自動的にnavigator.webdriverを削除し、window.chromeを追加し、プラグインをエミュレートし、WebGLをマスキングし、さらに20以上のパッチを適用します。これは、80%の検出ケースをカバーします。
プロキシを使用した高度な設定
完全なマスキングのために、selenium-stealthをレジデンシャルプロキシと組み合わせて使用してください — これにより、実際の家庭ユーザーのIPアドレスが提供され、進化したアンチボットシステムを回避するために重要です:
from selenium.webdriver.common.proxy import Proxy, ProxyType
proxy = Proxy()
proxy.proxy_type = ProxyType.MANUAL
proxy.http_proxy = "ip:port"
proxy.ssl_proxy = "ip:port"
capabilities = webdriver.DesiredCapabilities.CHROME
proxy.add_to_capabilities(capabilities)
driver = webdriver.Chrome(desired_capabilities=capabilities, options=options)
stealth(driver, languages=["en-US", "en"], vendor="Google Inc.", platform="Win32")
Puppeteer Extra Stealth Plugin for Node.js
Puppeteerには、puppeteer-extra-plugin-stealthというプラグインがあり、JavaScriptエコシステムにおけるヘッドレスブラウザのマスキングのための最も進んだソリューションです。これは、各自動化の側面をマスキングする23の独立したモジュールを含んでいます。
インストールと基本的な使用法
npm install puppeteer-extra puppeteer-extra-plugin-stealth
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-setuid-sandbox']
});
const page = await browser.newPage();
await page.goto('https://bot.sannysoft.com');
await page.screenshot({ path: 'test.png' });
await browser.close();
})();
Stealth Pluginがマスキングするもの
プラグインは自動的に以下のパッチを適用します:
navigator.webdriverの削除window.chromeオブジェクトの追加navigator.permissionsAPIのエミュレーションnavigator.pluginsおよびnavigator.mimeTypesのマスキング- CanvasおよびWebGLフィンガープリンティングのエミュレーション
- User-Agentおよび言語のマスキング
- iframe contentWindowの異常の修正
- Battery API、Media Devices、WebRTCのエミュレーション
プロキシおよび追加パラメータを使用した設定
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker');
puppeteer.use(StealthPlugin());
puppeteer.use(AdblockerPlugin({ blockTrackers: true }));
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: [
'--proxy-server=http://your-proxy:port',
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process'
]
});
const page = await browser.newPage();
// プロキシの認証
await page.authenticate({
username: 'your-username',
password: 'your-password'
});
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('https://example.com', { waitUntil: 'networkidle2' });
})();
Playwright: アンチデテクションの設定
Playwrightは、Puppeteerに比べてより洗練されたアーキテクチャを持ち、自動化の痕跡を少なくします。しかし、進化したアンチボットシステムを回避するためには追加の設定が必要です。Playwrightには、stealthプラグインのポートであるplaywright-extraがあります。
playwright-extraのインストール
npm install playwright-extra playwright-extra-plugin-stealth playwright
const { chromium } = require('playwright-extra');
const stealth = require('playwright-extra-plugin-stealth');
chromium.use(stealth());
(async () => {
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
viewport: { width: 1920, height: 1080 },
locale: 'en-US',
timezoneId: 'America/New_York'
});
const page = await context.newPage();
await page.goto('https://bot.sannysoft.com');
await page.screenshot({ path: 'test.png' });
await browser.close();
})();
最大のマスキングのためのブラウザコンテキストの設定
Playwrightは、個別の設定を持つ隔離されたブラウザコンテキストを作成することを可能にします。これは、マルチアカウントや並行パースに便利です:
const context = await browser.newContext({
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36',
viewport: { width: 1440, height: 900 },
locale: 'en-US',
timezoneId: 'America/Los_Angeles',
permissions: ['geolocation'],
geolocation: { latitude: 37.7749, longitude: -122.4194 },
colorScheme: 'light',
deviceScaleFactor: 2,
isMobile: false,
hasTouch: false,
// コンテキストのためのプロキシ設定
proxy: {
server: 'http://your-proxy:port',
username: 'user',
password: 'pass'
}
});
geolocationおよびtimezoneIdのパラメータは、プロキシのIPアドレスに一致する必要があります。そうしないと、アンチボットシステムが不一致を検出します(例えば、カリフォルニアのIPですが、タイムゾーンがニューヨーク)。
ブロックリスクを減らすためのプロキシのローテーション
完璧にマスキングされたヘッドレスブラウザでも、数百のリクエストに対して単一のIPアドレスを使用している場合、ブロックされる可能性があります。現代のアンチボットシステムは、単一のIPからのリクエストの頻度を分析し、疑わしい活動をブロックします。プロキシのローテーションは、パース時の保護の必須要素です。
パース用プロキシの種類: 比較
| プロキシの種類 | 速度 | 信頼スコア | 最適な用途 |
|---|---|---|---|
| データセンター | 非常に高い(50-200 ms) | 低い | 簡単なサイト、大量パース |
| レジデンシャル | 中程度(300-1000 ms) | 高い | 保護されたサイト、ソーシャルメディア |
| モバイル | 低い(500-2000 ms) | 非常に高い | モバイルアプリ、Instagram、TikTok |
保護されたサイト(マーケットプレイス、ソーシャルメディア、広告プラットフォーム)をパースするには、レジデンシャルプロキシを推奨します。これにより、リアルな家庭ユーザーのIPアドレスが提供され、アンチボットシステムのブラックリストに載ることがありません。
Puppeteerでのプロキシローテーションの実装
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const proxyList = [
'http://user1:pass1@proxy1:port',
'http://user2:pass2@proxy2:port',
'http://user3:pass3@proxy3:port'
];
async function scrapeWithRotation(urls) {
for (let i = 0; i < urls.length; i++) {
const proxy = proxyList[i % proxyList.length];
const browser = await puppeteer.launch({
headless: true,
args: [`--proxy-server=${proxy}`]
});
const page = await browser.newPage();
try {
await page.goto(urls[i], { waitUntil: 'networkidle2' });
const data = await page.evaluate(() => document.body.innerText);
console.log(data);
} catch (error) {
console.error(`Error on ${urls[i]}:`, error);
} finally {
await browser.close();
}
// リクエスト間の遅延
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
scrapeWithRotation([
'https://example1.com',
'https://example2.com',
'https://example3.com'
]);
セッションベースのプロキシによるローテーション
一部のプロキシプロバイダー(ProxyCoveを含む)は、セッションベースのローテーションを提供しています — 各リクエストは、ブラウザを再起動することなく自動的に新しいIPを取得します。これは、特別なプロキシURLフォーマットを介して実現されます:
// フォーマット: username-session-RANDOM:password@gateway:port
const generateSessionProxy = () => {
const sessionId = Math.random().toString(36).substring(7);
return `http://username-session-${sessionId}:password@gateway.proxycove.com:12321`;
};
const browser = await puppeteer.launch({
args: [`--proxy-server=${generateSessionProxy()}`]
});
マスキングの質を確認する方法: テストツール
マスキングの設定後、あなたのヘッドレスブラウザがどれだけ普通のユーザーを模倣しているかを確認する必要があります。ブラウザの数十のパラメータを分析し、自動化の痕跡がどれだけ残っているかを示す専門のサイトがいくつかあります。
主なテストツール
- bot.sannysoft.com — webdriver、Chromeオブジェクト、プラグイン、Canvasを含む15以上の検出パラメータをチェックします
- arh.antoinevastel.com/bots/areyouheadless — ヘッドレスChromeの検出に特化しています
- pixelscan.net — すべてのパラメータの視覚化を伴う高度なフィンガープリンティング分析
- abrahamjuliot.github.io/creepjs — 最も詳細な分析(200以上のパラメータ)、ブラウザの信頼レベルを示します
- iphey.com — プロキシおよびVPNへのIPアドレスの帰属を確認します
テストの自動化
設定変更後にマスキングを自動的にチェックするスクリプトを作成します:
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
async function testStealth() {
const browser = await puppeteer.launch({ headless: true });
const page = await browser.newPage();
// テスト1: Sannysoft
await page.goto('https://bot.sannysoft.com');
await page.screenshot({ path: 'test-sannysoft.png', fullPage: true });
// テスト2: Are you headless
await page.goto('https://arh.antoinevastel.com/bots/areyouheadless');
const headlessDetected = await page.evaluate(() => {
return document.body.innerText.includes('You are not Chrome headless');
});
console.log('ヘッドレスが検出されました:', !headlessDetected);
// テスト3: Webdriverプロパティ
const webdriverPresent = await page.evaluate(() => navigator.webdriver);
console.log('navigator.webdriver:', webdriverPresent);
// テスト4: Chromeオブジェクト
const chromePresent = await page.evaluate(() => !!window.chrome);
console.log('window.chromeが存在します:', chromePresent);
await browser.close();
}
testStealth();
成功したマスキングのチェックリスト
あなたのヘッドレスブラウザが正しくマスキングされているかどうかは以下の通りです:
navigator.webdriverがundefinedを返しますwindow.chromeが存在し、runtimeオブジェクトを含みますnavigator.plugins.lengthが0より大きいです- WebGLベンダーとレンダラーが実際のグラフィックカードを示し、SwiftShaderではありません
- Canvasフィンガープリンティングが各セッションでユニークです
- User-Agentが最新のChrome/Firefoxのバージョンに一致します
- プロキシのIPアドレスがブラックリストに載っていません(iphey.comで確認)
- タイムゾーンとロケールがIPアドレスのジオロケーションと一致します
結論
ヘッドレスブラウザのマスキングは、数十のパラメータに注意を払う必要がある複雑なタスクです。現代のアンチボットシステムは機械学習を使用し、同時に数百のブラウザの特性を分析するため、単純にUser-Agentを置き換えるだけではもはや機能しません。成功したパースのためには、複数の保護手法を組み合わせる必要があります。
効果的なマスキングの主な要素は、自動化のJavaScriptマーカー(navigator.webdriver、CDP変数)の削除、CanvasおよびWebGLフィンガープリンティングのエミュレーション、現実的なHTTPヘッダーの設定、質の高いプロキシの使用です。既製のソリューション(Python用のselenium-stealth、Node.js用のpuppeteer-extra-plugin-stealth)は80%のケースをカバーしますが、進化した保護を回避するためには追加の設定が必要です。
重要なポイントは、プロキシの選択です。完璧にマスキングされたブラウザでも、ブラックリストに載ったIPアドレスを使用したり、単一のIPから過剰なリクエストを行ったりするとブロックされます。保護されたサイトをパースするには、自動ローテーション機能を持つレジデンシャルプロキシを使用することをお勧めします。これにより、高い信頼スコアと最小限のブロックリスクが確保され、サーバーアドレスの代わりに実際の家庭ユーザーのIPが使用されます。
定期的に専門のサービス(bot.sannysoft.com、pixelscan.net)を通じてマスキングの質をテストし、アンチボットシステムの変化に応じて設定を調整してください。パースは、ボット開発者と保護作成者の間の武器競争であり、今日機能する構成も数ヶ月後には更新が必要になるかもしれません。