エス技研

WordPress、CakePHP、PHP、baserCMSなどの Web系システムを中心に情報を提供します!


PHPのスクレイピングライブラリ「PHP Simple HTML DOM Parser」の使い方

      2020/02/08

PHPのスクレイピングライブラリ「PHP Simple HTML DOM Parser」の使い方

 

PHP Simple HTML DOM Parserに関連する解説、および、関連記事

 
PHPでスクレイピングをする際には、「PHP Simple HTML DOM Parser」というライブラリを利用すると簡単にスクレイピングをすることが出来ます。
 
この記事では「PHP Simple HTML DOM Parser」の基本的な使い方を始め、実用的な使い方、エラーの原因になるポイントなどを解説していきます。
 
 
また、「PHP スクレイピング」で検索すると「phpQuery」というライブラリの記事が大量にヒットするわけです。
しかし、「phpQuery」より「PHP Simple HTML DOM Parser」をおすすめする理由や、「PHP Simple HTML DOM Parser」の基本的な使い方についての解説を下記の記事に書いていますので、こちらも併せて読んでいただく方がいいかと思います。
PHPでスクレイピング。phpQueryとphp-simple-html-dom-parserの比較と設置方法
 
「PHP Simple HTML DOM Parser」を CakePHP3で使う場合の導入方法については下記に記事を書きました。
CakePHP3でPHP Simple HTML DOM Parserを使ってスクレイピングする方法
 
 
また、スクレイピングを行う対象のページへアクセスするときは、「file_get_contents」や「cURL」を利用します。
ですが、断然「cURL」の方をオススメする!という理由や、cURLで SSL化されたページへのアクセス、User Agentを必要とするサイトへのアクセス方法などについて、下記の記事に書いていますので、こちらも併せて読んでいただく方がいいかと思います。
PHPのcURLでAPIやWebサイトへのアクセス方法。file_get_contentsとの比較
 
 
また、この記事は、下記のオフィシャルマニュアルを参考にしています。
https://simplehtmldom.sourceforge.io/manual.htm
 
 

DOMオブジェクトを生成する方法

 
「PHP Simple HTML DOM Parser」では、まず最初に HTMLから DOMオブジェクトを生成します。
 
そして、作成した DOMオブジェクトに対して「find()」メソッドを使ってほしい項目を取得する、という流れになります。
 
 

DOMオブジェクトを生成する基本形

 
その最初の DOMオブジェクトを作成する方法は下記の 3種類あります。
 

 
 

DOMオブジェクトを生成するサンプルソース

 
URLを指定して Webサイトにアクセスし DOMオブジェクトを作成する場合は、「file_get_html()」が用意されていますが、私は cURLを使い別に HTMLソースコードを取得した上で、「str_get_html()」を使って DOMオブジェクトを生成する方法をオススメします。
 
なぜか。
指定の URLから情報が取得できなかった場合にエラー制御をするためには cURLの方がやりやすいからです。
詳しくは、「PHPのcURLでAPIやWebサイトへのアクセス方法。file_get_contentsとの比較」か「PHPでスクレイピング。phpQueryとphp-simple-html-dom-parserの比較と設置方法」を参考にしてください。
 
 
cURLを利用した場合の DOMオブジェクトの生成するサンプルソースは以下のようになります。
 

 
関数「getApiDataCurl()」の中身については、「PHPのcURLでAPIやWebサイトへのアクセス方法。file_get_contentsとの比較」か「PHPでスクレイピング。phpQueryとphp-simple-html-dom-parserの比較と設置方法」を参考にしてください。
 
 

DOMオブジェクトから find()メソッドを使ってノード(項目)を取得する方法

 

DOMオブジェクトから find()メソッドを使ってノード(項目)を取得する処理の基本系

 
DOMオブジェクトからノード(項目)を取得するときは「find()」メソッドを使います。
その使い方の基本形は下記のようになります。
 

 
 

要素の条件を指定するフィルタ

 
$html->find("div[id=foo]");」の「[id=foo]」で指定している箇所はフィルタとなっておりまして、「=」以外の条件を指定することも出来ます。
指定方法とその条件は、以下のようになります。
 
[attribute]
指定された属性を持つ要素に一致するものを取得
 
[!attribute]
指定された属性を持つ要素に一致しないものを取得
 
[attribute=value]
指定された値である属性を持つ要素を取得
 
[attribute!=value]
指定された値ではない属性を持つ要素を取得
 
[attribute^=value]
指定された値から始まる属性を持つ要素を取得
 
[attribute$=value]
指定された値で終わる属性を持つ要素を取得
 
[attribute*=value]
指定された値を含む属性を持つ要素を取得
 
 

CSSの IDセレクタ、CLASSセレクタを指定する方法

 
CSSの IDセレクタや CLASSセレクタを条件として指定する方法もあります。
 

 
 
「,(カンマ)」で区切りることで、複数条件の指定ができるようになります。
 

 
 

テキストブロックとコメントの取得

 
テキストブロックやコメントを取得するときは下記のように指定します。
 

 
$html->plaintext」は、全てのタグの plaintextを取得するものです。
$html->find("text")」とは違う結果になります。
 
 

子孫セレクタやネストされたタグの取得

 
スペースで区切ることで子孫セレクタ、ネストされた要素を取得することが出来ます。
 

 
 
ネストされた要素は下記のように foreach()で取得することも可能です。
 

 
 

取得した要素の属性にアクセスする方法

 
前項では、DOMオブジェクトから条件に従って要素を取得する方法を解説しました。
この後では、取得した要素の中から指定した属性の値を取得する方法を解説します。
 
 

要素の属性を指定して値を取得する方法の基本形

 
要素の属性を指定して値を取得する基本形は以下になります。
 

 
 

要素のテキストの情報を取得する方法

 
指定した要素のテキスト情報を取得する方法が 3種類用意されています。
テキスト情報のみを取得するのか、タグも含めて取得するのか、などを選択することができます。
 

 
 
「plaintext」「innertext」「outertext」を指定した際の具体的な結果は以下のようになります。
 

 
 
ちなみに、「$html->find("a",0)->plaintext;」と「$html->find("a",0)->innertext;」とした場合は、両方とも同じ「リンクの例」となります。
これは、「a」タグの中には「リンクの例」しかないためです。
 
 

属性を設定、削除する方法、属性の存在の有無チェックの方法

 
属性から値を取得するだけではなく、属性に値を設定したり、削除したりすることもできます。
 

 
 

DOM拡張モジュールに定義された関数を使用して要素を取得することも可能

 
DOMオブジェクトから要素を取得する方法としては、「find()」メソッドを利用する方法を紹介しました。
 
ですが、PHP Simple HTML DOM Parserでは、DOM拡張モジュールに定義された関数を使用して要素を取得することもできます。
 
例えば、下記のように「getElementById()」で「id」の値を指定して要素を取得することもできます。
 

 
 
DOM拡張モジュールに定義された関数は多数ありますので、下記の PHPの関数レファレンスを参考にしてください。
https://www.php.net/manual/ja/book.dom.php
 
 

取得した HTMLを保存する方法

 
取得した HTMLファイルを簡単にファイルとして出力(保存)する方法も用意されています。
 

 
解説はしましたが、果たしてこの機能を使う機会はあるのだろうか?という疑問はありますね。
 
 

PHP Simple HTML DOM Parserを使うときの注意点

 
ここ以降は、マニュアルには載っていない内容ですが、実際に使ってみて気づいた点がいくつかありましたので、それをお伝えしたいと思います。
 
 

DOMオブジェクトの中身の確認には echo、printを使う

 
先のサンプルでは、取り出した値を「print_r()」を使って表示しています。
ですが、DOMオブジェクトの中身を確認したいときは、print_r()、var_dump()ではなく、echo、printを使いましょう。
 
 
具体的には、下記の記述を実行すると「Fatal error: Allowed memory size of 2097152000 bytes exhausted (tried to allocate 1046482944 bytes) in C:\xampp...」のエラーがでます。
 

 
下記の記述方法であればエラーは発生しません。
 

 
 
詳しいエラーの原因は調べていませんが、すごい勢いで CPUとメモリを消費してオーバーフローをしてしまいますので、ローカル環境以外で「print_r()」を使うのはかなり危険です。
 
 

HTMLタグの大文字、小文字は気にしなくて問題なし

 
HTMLタグは大文字、小文字のどちらで記述しても問題ない仕様になっています。(XHTMLは小文字で記述する仕様になっています。)
 
そのため、HTMLタグが大文字のサイトがあったり、小文字のサイトがあったりしますが、「$html->find("title",0)->plaintext」などで指定するタグは、大文字小文字の区別をしません。
(HTMLのソース側も、find()に指定する側も大文字、小文字の区別は行われません。)
 
そのため、大文字小文字は気にせず記述することが出来ます。
 
逆に、大文字小文字が混在したサイトで「大文字で書かれた「H2タグ」の 3つめを取得したい」といった指定をすることは出来ません。
 
 

PHP Simple HTML DOM Parserで処理できる文字数には上限がある

 
PHP Simple HTML DOM Parserには処理できる文字数に上限があるようです。
 
とあるサイトのスクレイピングをしようとアクセスしたところ、「Fatal error: Uncaught Error: Call to a member function find() on bool in ...」というエラーが発生しました。
 
エラーの内容を調査すると、「$html->find("h3",0)->plaintext」を実行している「$html」が空であることが確認できました。
そして、「$html」が空になる理由が、HTMLのソース量が大きすぎて、「str_get_html()」で処理できていないことが分かりました。
 
そのため「mb_substr()」を使用して、取得した HTMLを文字数で制限して取得してみたところ、520,000文字以下であれば処理できることが確認できました。
(環境によって「520,000文字」が異なるか否かは確認をしていません。)
 
 
より具体的には...
 
520,261文字以上ではエラーがでました。
「mb_substr」で処理をしていますので、バイト数ではなく、全角半角の区別なく文字数です。(バイト数で制限があるか否かは確認していません。)
php.ini の memory_limit を変更しても制限される文字数が変わることはありませんでした。
 
 
このエラーの難点は、エラーメッセージが「$html が空です」というものであるため、HTMLのソース量が大きいことが原因で処理できていない、ということになかなか気づかない点が挙げられます。
 
 

大量の HTMLソースを持つページに対処するサンプルソース・その1

 
大量の HTMLソースを持つページの情報を取得する必要がある場合の具体的な対処方法のサンプルソースとしては以下のようになります。
 

 
HTMLファイルの最初の方はグローバルメニューや検索条件を入力するエリアで、下の方に検索結果一覧が表示される、というサイトなどを想定しています。
そのため、検索結果が表示される HTMLの下の方だけ取得できればいい、といった場合はこの方法が簡単なのではないか、と思います。
 
「mb_substr」を用いて、最後から「520,000文字」を取得しています。
 
サンプルのヤフーのサイトは「520,000文字」もありませんが。
 
 
また、関数「getApiDataCurl()」は、オリジナルの関数です。詳細は「PHPのcURLでAPIやWebサイトへのアクセス方法。file_get_contentsとの比較」か「PHPでスクレイピング。phpQueryとphp-simple-html-dom-parserの比較と設置方法」を確認してください。
 
 

大量の HTMLソースを持つページに対処するサンプルソース・その2

 
前項では「mb_substr」を用いてざっくり後半部分!という感じで取得する処理でした。
 
その2では、例えば「<h2>一覧表示</h2>」移行の行を対象として取得する、といった具体的な目印がある場合の対応方法です。
 
下記のように「foreach()」で上から 1行ずつチェックして必要な部分を取得する方法もあるかとも思います。
 

 
 
長くなりましたが、以上になります。

 - PHP・Smarty・ECCUBE

GoogleAdwords

GoogleAdwords

最後までお読みいただきましてありがとうございます。
この記事が参考になったと思いましたらソーシャルメディアで共有していただけると嬉しいです!

Message

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

下記の空欄を埋めてください。 * Time limit is exhausted. Please reload CAPTCHA.

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

※入力いただいたコメントは管理者の承認後に掲載されます。

  関連記事

路線・駅検索をPHPで実装する方法解説。GoogleMapsの緯度経度から計算し検索
路線・駅検索をPHPで実装する方法解説。GoogleMapsの緯度経度から計算し検索

路線・駅検索の仕組みの構築は大変。登録する側も最寄り駅が多い場合は大変。なので簡易に実装するため緯度経度に基づき直線距離を計算する処理を考案して実装して、その処理を解説。

PHPでスクレイピング。phpQueryとphp-simple-html-dom-parserの比較と設置方法
PHPでスクレイピング。phpQueryとphp-simple-html-dom-parserの比較と設置方法

「PHP スクレイピング」で検索すると「phpQuery」ばかりヒットするが、10年以上も放置されている。なので今も開発が続いている「PHP Simple HTML DOM Parser」をオススメする。

include、requireのパス指定をdirname(__FILE__)、__DIR__と書く理由

include、requireのパスの指定を dirname(__FILE__)、__DIR__で記述する理由に付いて解説。相対パス、絶対パスを直書き、パスを書かない場合は何が問題かを説明。

リダイレクトループが原因で「ERR_TOO_MANY_REDIRECTS」「このページを表示できません」が出たときの対策12事例+α

リダイレクトループ、自動転送設定ループの原因の解説とその対応方法を含め事例 12例を挙げて説明。

Smartyの修飾子regex_replaceで正規表現の後方参照・PHPではpreg_replace

ECCUBEで使われているSmartyで文字列を正規表現で置換し後方参照で値を利用する装飾子regex_replaceの解説です。細かな条件がありますので注意が必要です。

連想配列のキーも値もまとめてhtmlspecialchars()でサニタイズする関数の作成解説
連想配列のキーも値もまとめてhtmlspecialchars()でサニタイズする関数の作成解説

PHPの配列・連想配列のキーと値をまとめてhtmlspecialchars()関数でサニタイズ(無害化、無毒化)を行う関数を作成。連想配列のキーはarray_map()関数でのサニタイズは無理。

ECCUBE2.13.3で商品規格の在庫数が無制限から変更できないバグがある

2.13.3固有のバグである商品規格の在庫数の入力エリアがアクティブにならない不具合を解消する解説です。product_class.tplの2行を修正するだけの簡単対応です。

ECCUBEの管理画面のSSL設定をインストール後に変更する方法

ECCUBEをインストールした後から管理画面のSSL設定を変更する方法を解説します。config.phpファイルのHTTPS_URLとADMIN_FORCE_SSLの値を変更すればOK。

Basic認証の.htaccess、.htpasswd生成ツールと解説

Basic認証を設定する際に必要となる.htaccess、.htpasswdファイルを生成するツール。ID、PASS、.htpasswdへのパスを入力することで編集する情報を生成します。

フォルダを指定してファイルのパーミッションを変更するプログラム

フォームからフォルダ、パーミッションを指定しパーミッションを変更するサンプルプログラムの解説です。