【PHP】DOMDocumentを使ったWebスクレイピングのサンプルコードを解説
PHPのDOMDocumentを使ったWebスクレイピングの基礎を解説します。HTMLを解析し、XPathでウェブページ内の特定の要素(タイトルやURLなど)を効率的に探し出す技術を、具体的なサンプルコードを通して学びます。さらに、取得したURLの整形方法まで網羅し、Webからの情報収集に必要なプログラミングスキルを身につけることができます。
開発環境
- Visual Studio Code:version 1.73.0
- OS:Windows10
PHPのDOMDocumentとは
DOMDocument(ドムドキュメント)とは、PHPというプログラミング言語を使って、ウェブページ(HTML)やデータファイル(XML)の中身を読み解き、望むように操作するための機能を提供するクラスです。
DOMはDocument Object Model(ドキュメントオブジェクトモデル)の略称です。これは、HTMLやXMLで書かれた文書の構成要素、例えばウェブページの見出しや段落、画像、リンクといった各部分を、プログラミングで自由に扱えるようにするための考え方や仕組みのことを指します。この仕組みを利用することで、プログラムから文書の構造を理解し、特定の部分の情報を読み取ったり、新しい情報を書き加えたりすることが可能になります。
PHPでウェブサイトから情報を自動的に集めてくる「スクレイピング」という技術を行う際にも、DOMDocumentは利用されます。以前はPHPQueryというライブラリもよく使われていましたが、PHPQueryは2009年から更新が停止しており、現在のPHPのバージョンに対応していません。そのため、新しいPHPのバージョンで利用すると、正しく動作しないことやセキュリティ上の問題が発生する可能性があります。
一方、DOMDocumentはPHPの標準機能として用意されています。したがって、特別な追加のインストール作業なしで、PHPをインストールした時点でそのまま利用を開始することができます。これにより、環境構築の手間を省き、すぐに開発に取り掛かれるという大きなメリットがあります。
公式ドキュメント:https://www.php.net/manual/ja/class.domdocument.php
PHPのDOMDocumentでスクレイピングする
次にPHPのDOMDocumentでスクレイピングするサンプルコードを解説していきます。
スクレイピングとは、Webページから特定の情報を自動的に収集する技術のことです。ここでは、PHPのDOMDocumentという機能を使って、WebページのHTML構造を解析し、必要な情報を抜き出す方法を学びます。
スクレイピングのサンプルコード
下記はDOMDocumentを使用して「PHPとは」のGoogle検索結果ページを取得し、検索結果のタイトルとURLの一覧を取得するサンプルコードです。
1<?php 2// キーワード 3$keyword = urlencode('PHPとは'); 4 5//DOMオブジェクトを新規に作成する 6$dom = new DOMDocument('1.0', 'utf-8'); 7 8//データを抽出したいURLを入力する 9$html = file_get_contents("https://www.google.com/search?q=" . $keyword); 10 11//$htmlに指定したHTMLの内容を$domに取り込む 12libxml_use_internal_errors(true); // HTMLの形式が一部正しくない場合などのエラーを無視 13$dom->loadHTML($html); 14libxml_clear_errors(); 15 16// XPathを使ってリンク要素を検索(タイトルが含まれているh3タグを選択) 17$xpath = new DOMXPath($dom); 18$nodes = $xpath->query('//h3'); 19 20foreach ($nodes as $node) { 21 // タイトルを取得 22 $title = $node->textContent; 23 24 // h3タグの親ノードからURLを取得 25 $linkNode = $node->parentNode; 26 27 while ($linkNode && $linkNode->nodeName !== 'a') { 28 $linkNode = $linkNode->parentNode; 29 } 30 31 if ($linkNode) { 32 $url = $linkNode->getAttribute('href'); 33 34 // URLがGoogleのリダイレクト形式の場合、クリーンアップ 35 if (strpos($url, '/url?q=') === 0) { 36 $cleanUrl = explode('&', substr($url, 7))[0]; 37 echo "Title: " . trim($title) . "\n"; 38 echo "URL: " . $cleanUrl . "\n\n"; 39 } 40 } 41}
作成したファイルは、以下のコマンドで実行できます。
1php index.php
検索URLを作成してHTMLを取得する
まずは検索キーワードをURLエンコードし、Google検索結果ページのHTMLを取得します。
1$keyword = urlencode('PHPとは'); 2 3$html = file_get_contents("https://www.google.com/search?q=" . $keyword);
urlencode()関数は、Webアドレス(URL)に含めることができない特殊な文字(日本語のスペースなど)を、URLで安全に扱える形式に変換する役割があります。ここでは「PHPとは」というキーワードをエンコードしています。
Googleの検索結果ページのURLはhttps://www.google.com/search?q=の後ろに検索キーワードが入る形になっているので、urlencodeでエンコードしたキーワードを連結しています。
file_get_contents()関数は、指定されたURLにアクセスし、そのWebページのHTMLの内容をテキストデータとして丸ごと取得するものです。この取得したHTMLデータが、後の処理で使われることになります。
DOMDocumentにHTMLを取り込む
次にDOMDocumentのインスタンスを作成し、取得したHTMLを取り込みます。
1$dom = new DOMDocument('1.0', 'utf-8'); 2 3libxml_use_internal_errors(true); 4$dom->loadHTML($html); 5libxml_clear_errors();
DOMDocumentは、WebページのHTMLやXMLをプログラムで扱いやすい「オブジェクト」という形式に変換し、その構造を解析するためのPHPの機能です。これにより、HTMLのどの部分にどのような情報があるかを効率的に探し出すことができます。
DOMDocumentのコンストラクタ(new DOMDocument(...)の部分)では、第1引数にXMLのバージョン(通常は'1.0')、第2引数に文字エンコーディング(ここでは'utf-8')を指定できます。
$dom->loadHTML($html)は、file_get_contentsで取得したHTMLのテキストデータ($html)をDOMDocumentオブジェクトに読み込み、Webページの構造を解析してプログラムで操作できる形に変換します。
実際のWebページのHTMLは、記述形式が厳密には正しくない場合がよくあります。そのため、そのままloadHTMLを実行すると警告が表示されることがあります。libxml_use_internal_errors(true)を設定することで、これらの警告を一時的に無視し、処理を続行できます。HTMLの読み込みが終わったら、libxml_clear_errors()でエラー情報をクリアしておくのが良いプラクティスです。
XPathでタイトルを取得する
次に、XPathを使ってタイトルの要素を取得します。
1$xpath = new DOMXPath($dom); 2$nodes = $xpath->query('//h3'); 3 4foreach ($nodes as $node) { 5 $title = $node->textContent;
XPathは、HTMLやXML文書の中から特定の要素(タグ)や属性を指定するための書き方です。これにより、Webページの複雑な構造の中から必要な情報だけをピンポイントで探し出すことができます。
ブラウザの開発者ツール(多くのブラウザでF12キーを押すと開けます)でGoogle検索結果のページを確認すると、検索結果のタイトルは通常<h3>タグで表示されていることがわかります。
new DOMXPath($dom)でDOMXPathオブジェクトを作成し、query('//h3')メソッドを使って、HTMLドキュメント全体(//)からすべての<h3>要素を検索し、それらを$nodesという変数に取得しています。
取得した<h3>要素は複数ある可能性があるため、foreachループを使って1つずつ取り出します。各<h3>要素($node)からtextContentプロパティを使うことで、その要素に含まれるテキスト、つまり検索結果のタイトルを取得し、$title変数に格納しています。
親ノードを遡ってURLを取得する
次に、h3タグの親ノードを遡ってリンク(aタグ)を見つけ、URLを取得します。
1$linkNode = $node->parentNode; 2 3while ($linkNode && $linkNode->nodeName !== 'a') { 4 $linkNode = $linkNode->parentNode; 5} 6 7if ($linkNode) { 8 $url = $linkNode->getAttribute('href');
WebページのHTML構造では、タグが親子関係を持っています。例えば、<p><span>テキスト</span></p>の場合、<p>は<span>の親、<span>は<p>の子です。直接取得した<h3>タグにはURL(<a>タグのhref属性)が含まれていないため、その親要素をたどって<a>タグを見つける必要があります。
$node->parentNodeは、現在の$node(<h3>タグ)の直接の親要素を取得します。
whileループは、取得した要素が<a>タグではない場合($linkNode->nodeName !== 'a')に、さらにその親要素を$linkNode = $linkNode->parentNode;で取得し続ける処理です。これにより、<h3>タグの親、そのまた親…と、HTMLツリーを上方向にたどっていき、目的の<a>タグを見つけ出します。
if ($linkNode)で<a>タグが見つかったことを確認した後、$linkNode->getAttribute('href')を使って、見つかった<a>タグのhref属性の値、つまりリンク先のURLを取得し、$url変数に格納しています。
GoogleのリダイレクトURLをクリーンアップする
最後に、取得したURLを整えて出力します。
1if (strpos($url, '/url?q=') === 0) { 2 $cleanUrl = explode('&', substr($url, 7))[0]; 3 echo "Title: " . trim($title) . "\n"; 4 echo "URL: " . $cleanUrl . "\n\n"; 5}
Googleの検索結果ページで表示されるリンクのURLは、直接その記事のURLを指しているのではなく、/url?q=記事のURL&...というGoogle独自のリダイレクト形式になっている場合があります。これはGoogleがクリック数を追跡するためなどの目的で使われますが、スクレイピングで取得した情報を利用する際には、純粋な記事のURLに「クリーンアップ」する必要があります。
strpos($url, '/url?q=') === 0は、取得した$urlの文字列が/url?q=という文字列で始まっているかどうかをチェックしています。
もし/url?q=で始まっていれば、クリーンアップ処理を行います。
substr($url, 7)は、$urlの文字列の先頭から7文字目以降を切り取ります。/url?q=はちょうど7文字なので、これにより/url?q=の部分が取り除かれ、記事のURL&...という部分が残ります。
explode('&', ...)[0]は、先ほど切り取った文字列を&という文字で分割し、その分割された最初の部分([0])だけを取得します。これにより、&以降に続く余計なパラメータが取り除かれ、純粋な「記事のURL」だけが$cleanUrlに格納されます。
最後に、echo文を使って、取得したタイトル(trim($title)で前後の空白文字を削除)とクリーンアップされたURLをターミナルに表示しています。\nは改行を意味します。
これらの処理を経て、実行するとターミナルに検索結果のタイトルとURLが出力されます。
おわりに
この記事では、システムエンジニアの基礎となる開発環境の準備と、PHPのDOMDocumentを使ったWebスクレイピングの基礎を学びました。file_get_contentsで取得したHTMLをDOMDocumentに読み込み、DOMXPathを使って目的の要素(h3タグなど)を見つける手順を解説しました。そして、親ノードを辿ってURL(aタグのhref属性)を取得し、GoogleのリダイレクトURLのような特殊な形式をstrposやexplodeでクリーンアップする具体的な方法も身につけたことでしょう。今回学んだDOMDocumentによるHTML解析と情報抽出のスキルは、Webからのデータ収集において大変役立ちます。