ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
| この記事の要点 |
|
方式一覧
| 方式 | 用途 | JS 実行 | 難易度 |
|---|---|---|---|
file_get_contents() | シンプルな GET | 不可 | 低 |
| cURL | 本格的 HTTP(Cookie / 認証) | 不可 | 中 |
| Guzzle | モダンな PHP HTTP クライアント | 不可 | 低 |
| DOMDocument + XPath | HTML パース(標準同梱) | - | 中 |
| Symfony DomCrawler | CSS セレクタ対応 | - | 低 |
| Goutte / symfony/browser-kit | cURL + DomCrawler 高レベル | 不可 | 低 |
| php-webdriver (Selenium) | ブラウザ自動操作 | 可 | 高 |
| chrome-php/chrome | Headless Chrome 直接制御 | 可 | 中 |
| Puppeteer (Node.js) | JS 製・PHP から exec | 可 | 中 |
1. file_get_contents + DOMDocument
loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
libxml_clear_errors();
// XPath で抽出
$xpath = new DOMXPath($dom);
$titles = $xpath->query('//h2[@class="article-title"]/a');
foreach ($titles as $node) {
echo $node->textContent . " => " . $node->getAttribute('href') . "\n";
}
// 全 img タグの src
$imgs = $xpath->query('//img');
foreach ($imgs as $img) {
echo $img->getAttribute('src') . "\n";
}
2. cURL(Cookie・認証・User-Agent)
$url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; MyBot/1.0; +https://example.com/bot)',
CURLOPT_COOKIEJAR => '/tmp/cookies.txt',
CURLOPT_COOKIEFILE => '/tmp/cookies.txt',
CURLOPT_HTTPHEADER => array_merge([
'Accept: text/html,application/xhtml+xml',
'Accept-Language: ja,en;q=0.9',
], $headers),
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_ENCODING => '', // gzip 等自動展開
]);
$body = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$err = curl_error($ch);
curl_close($ch);
if ($body === false) throw new RuntimeException("cURL: $err");
if ($code >= 400) throw new RuntimeException("HTTP $code");
return $body;
}
// 使用
$html = fetch('https://example.com/login');
3. Guzzle(モダン HTTP クライアント)
$jar,
'timeout' => 30,
'headers' => [
'User-Agent' => 'MyBot/1.0',
],
]);
// GET
$response = $client->get('https://example.com/');
$html = (string) $response->getBody();
// POST フォーム
$response = $client->post('https://example.com/login', [
'form_params' => [
'username' => 'taro',
'password' => 'secret',
],
]);
// JSON API
$response = $client->get('https://api.example.com/items', [
'headers' => ['Authorization' => 'Bearer xxx'],
]);
$data = json_decode((string) $response->getBody(), true);
4. Symfony DomCrawler (CSS セレクタ)
filter('h2.article-title a')->each(function (Crawler $node) {
echo $node->text() . " => " . $node->attr('href') . "\n";
});
// XPath
$crawler->filterXPath('//meta[@property="og:image"]')->each(function ($node) {
echo $node->attr('content') . "\n";
});
// 属性・テキスト
$title = $crawler->filter('title')->text();
$canonical = $crawler->filter('link[rel="canonical"]')->attr('href');
// 子要素
$crawler->filter('.product')->each(function (Crawler $product) {
$name = $product->filter('.name')->text();
$price = $product->filter('.price')->text();
echo "$name : $price\n";
});
5. Goutte / BrowserKit(高レベル API)
request('GET', 'https://example.com/login');
// フォーム
$form = $crawler->selectButton('ログイン')->form([
'username' => 'taro',
'password' => 'secret',
]);
$crawler = $browser->submit($form);
// リンククリック
$link = $crawler->selectLink('マイページ')->link();
$crawler = $browser->click($link);
// 結果のパース
$crawler->filter('.user-info')->each(function ($node) {
echo $node->text() . "\n";
});
6. 動的サイト: Headless Chrome
JavaScript で動的にレンダリングされる SPA や React/Vue サイトには Headless Chrome が必要です:
createBrowser([
'headless' => true,
'noSandbox' => true,
'windowSize' => [1920, 1080],
]);
try {
$page = $browser->createPage();
$page->navigate('https://spa.example.com')->waitForNavigation();
// JS 実行完了を待つ
$page->waitUntilContainsElement('.product-list');
// HTML 取得
$html = $page->getHtml();
// JS 実行で値取得
$title = $page->evaluate('document.title')->getReturnValue();
// スクリーンショット
$page->screenshot()->saveToFile('screenshot.png');
} finally {
$browser->close();
}
7. Puppeteer 連携(Node.js 経由)
// scraper.js (Node.js)
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
await page.goto(process.argv[2], { waitUntil: 'networkidle0' });
const data = await page.evaluate(() => {
return Array.from(document.querySelectorAll('.product')).map(el => ({
name: el.querySelector('.name').innerText,
price: el.querySelector('.price').innerText,
}));
});
console.log(JSON.stringify(data));
await browser.close();
})();
8. robots.txt を尊重する
9. レート制限とマナー
- sleep を入れる: 連続リクエストは最低 1〜2 秒待機
- User-Agent を明示: 連絡先 URL も含める(ブロック回避)
- Retry-After ヘッダ尊重: 429 / 503 のときは指定秒待つ
- 並列度を抑える: 同時 1〜3 接続まで
- キャッシュ活用: 同じ URL を何度も取らない
- 利用規約確認: スクレイピング禁止サイトでは API を探す
minInterval = $minIntervalSec;
}
public function wait(): void {
if ($this->lastRequest !== null) {
$elapsed = microtime(true) - $this->lastRequest;
if ($elapsed < $this->minInterval) {
usleep((int)(($this->minInterval - $elapsed) * 1_000_000));
}
}
$this->lastRequest = microtime(true);
}
}
$rl = new RateLimiter(2.0);
foreach ($urls as $url) {
$rl->wait();
$html = fetch($url);
// ...
}
PHP vs Python スクレイピング比較
| 項目 | PHP | Python |
|---|---|---|
| HTML パース | DOMDocument / DomCrawler | BeautifulSoup / lxml |
| HTTP クライアント | Guzzle / cURL | requests / httpx |
| クローラフレームワーク | Goutte (基本機能のみ) | Scrapy (フル機能) |
| JS レンダ | chrome-php / Puppeteer 連携 | Selenium / Playwright |
| 並列処理 | Guzzle Pool | asyncio / aiohttp |
| エコシステム | CMS 統合に強い | データ分析と連携 |
FAQ
Q: 文字化けする
A: mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8') を DOMDocument に渡す前にかける。 元 HTML が Shift_JIS なら mb_convert_encoding($html, 'UTF-8', 'SJIS-win')。
Q: 403 / 429 で弾かれる
A: User-Agent を本物のブラウザ風に、 Referer 設定、 Cookie 維持、 リクエスト間隔を空ける。 それでもダメなら API 利用を検討。
Q: 利用規約で禁止されているサイトをスクレイピングしてもよい?
A: 法的・倫理的に問題があります。 公式 API、 RSS、 データ提供サービスの利用を最優先に検討してください。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページ
同階層のページ
- インストール方法
- 文法
- Composerのインストール
- 内部関数
- フレームワーク
- エラー一覧
- 改行出力
- printとechoの違い
- シングルクォートとダブルクォートの違い
- returnとyieldの違い
- var_dumpをログ出力
- CSV読み込み
- 待機・処理の遅延
- ログファイルにエラーを出力する方法
- エラーログ出力関数
- URLパラメータの配列化
- empty, is_null. issetの判定比較表
- httpステータスコードの付与
- バージョンの確認
- php.ini
- APIを呼び出す方法
- 外部ファイルを呼び出す方法
- カンマ区切りの文字列を配列に変換
- 配列からランダムに値を取り出す方法
- Webスクレイピング
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript base URL 取得方法|window.location.origin と SSR/Node.js 対応
- 4 YouTube Data API v3 エラー一覧|403/400/404 の主要原因と切り分け
- 5 Spring Frameworkのアノテーション一覧
- 6 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- Laravel キャッシュクリア完全ガイド(cache:clear / config:clear / 2026-05-18 07:42:07
- プロジェクトの作成と削除 2026-05-18 07:42:07
- インストール直後にNetbeansが反応しない 2026-05-18 07:42:07
- 動画やチャンネルの検索 2026-05-18 07:42:07
- APIキー取得方法 2026-05-18 07:42:07
- チャンネル情報の取得 2026-05-18 07:42:07
- API 入門 — Web API(REST / GraphQL / gRPC / 2026-05-18 07:42:07
- インストール(eclipseプラグイン) 2026-05-18 07:42:07
- Laravel「Dotenv values containing spaces must be surrounded 2026-05-18 07:42:07
- エラー一覧 2026-05-18 07:42:07
- curl: (51) SSL: certificate subject name '~' does not match 2026-05-18 07:42:07
- インストール方法(Windows版) 2026-05-18 07:42:07
- JSONから配列に変換 2026-05-18 07:42:07
- 処理を一定時間待つ 2026-05-18 07:42:07
- A non well formed numeric value encountered 2026-05-18 07:42:07
コメントを削除してもよろしいでしょうか?