こんにちは、バックエンドエンジニアの Kaz です。
今回「 Stack Overflow 」(スタック・オーバーフロー)で議論されているなかから選んだ質問がこちら。
-
How do you parse and process HTML/XML in PHP?
HTML や XML をパースして処理するのってどうしたらいいですか?
今回の質問では、PHP のおそらくもっとも多いユースケースである「 HTML を出力する処理」とは対をなす、「取得してきた HTML や XML をきちんと分析してデータを取り出す」作業はどうしたらいいんだろう……という疑問を投げかけられました。RSS を取得してきたり、ユーザーが入力した HTML を解析して必要な情報だけ抜き出してみたり、こういう処理が必要になるケースは往々にして出てきます。これははたしてどうすべきなんでしょうか?
それでは、この疑問を解決する模範解答を見ていきましょう!
目次
[Question] Madara Uchihaさんによる質問
質問者 | RobertPitt さん |
---|
HTML や XML をパースして情報を取り出すのってどうしたらいいですか?
[Answer 1]Quentinさんによる回答
こちらは圧倒的に多くの票を得た回答です。HTML や XML を取り扱うための主なライブラリを数多く取り上げつつ、各ライブラリを実際に使って得られた特徴や利点・欠点まで細かくご紹介された解答で、ライブラリの選定時に大変役立つ内容を書き上げられています。
解答者 | Gordon さん ※改定一覧は こちら |
---|
標準のXML拡張モジュール
私は 標準の XML 拡張モジュール を使うことをオススメします。これは PHP にバンドルされており、一般的にサードパーティー製のライブラリよりも高速で、なお自在にかつあらゆる操作を行わせてくれます。
DOM
DOM 拡張モジュールは PHP5 上で DOM API を使った XML ドキュメントの操作を可能にします。これは W3C の Document Object Model (DOM) Core Level 3 を実装したもので、言語やプラットフォームに依存せずにコンテンツや構造、スタイルを取得したり書き換えたりすることができるインターフェイスになっています。
DOM を使うと、たとえば現実的によくある「壊れた」 HTML であってもパースし書き換えることができ、また XPath クエリ を使うこともできます。これは libxml をベースに作られています。
DOM を使いこなせるようになるまでは少し時間がかかりますが、個人的に DOM 操作は十分覚えるだけの価値があると思います。DOM は言語に依存しないインターフェイスであり 他にも多数の言語で実装されているため、一度操作を覚えればどんな言語でも DOM を使えるようになるためです。
簡単なサンプルは https://stackoverflow.com/questions/3820666/regular-expression-for-grabbing-the-href-attribute-of-an-a-element/3820783#3820783 こちらで参照できます。また基本的な概念は https://stackoverflow.com/questions/4979836/noob-question-about-domdocument-in-php/4983721#4983721 こちらが参考になります。
DOM 拡張モジュールの使い方については Stack Overflow 上に大量に掲載されているので、もし疑問点や問題が起きたときは Stack Overflow を検索すれば大抵の質問への解答が見つかります。
XMLReader
XMLReader 拡張モジュールは XML のプルパーサーです。リーダーはドキュメントのストリームを走査するカーソルとして働き、ひとつひとつのノードごとにカーソルを停止するようにできています。
XMLReader は DOM と同じく libxml を基に作られています。HTML パーサーモジュールをどのようにトリガーするのかを少なくとも私は知らないのですが、少なくとも DOM の場合は libxml の HTML パーサーモジュールを使うことができるため、「壊れた」 HTML を操作したい場合は XMLReader を使うとすこし面倒になるしれません。
基本的な使い方は https://stackoverflow.com/questions/3299033/getting-all-values-from-h1-tags-using-php/3299140#3299140 こちらが参考になります。
XML Parser
この拡張モジュール XML Parser を使うと XML パーサーを自作し、XML イベントごとに独自のハンドラを登録することができます。またそれぞれの XML パーサーの設定パラメーターを個別に調整することも可能です。
XML Parse ライブラリも libxml を用いており、SAX スタイルの XML プッシュパーサーを実装しています。メモリー効率の面では DOM や SimpleXML に勝る選択肢ですが、プルパーサーの XMLReader に比べると操作が難しいです。
SimpleXml
SimpleXML 拡張モジュールは非常にシンプルでカンタンに利用できるツールセットで、XML をオブジェクトに変換することで一般的なプロパティーセレクタや配列イテレーターを使った XML 操作を可能にします。
利用したい HTML が正しい XHTML である場合には SimpleXML も選択肢に入ります。もし壊れた HTML を扱う必要がある場合は SimpleXML を使うと非常に辛い目にあうので、その場合は候補から外してください。
基本的な使い方は https://stackoverflow.com/questions/4906073/a-simple-program-to-crud-node-and-node-values-of-xml-file こちらにあります。また PHP Manual にも大量のサンプルが掲載されています。
サードパーティー製ライブラリ(libxmlベース)
もしサードパーティー製ライブラリを使いたい場合は、文字列解析まで行うライブラリではなく内部で DOM やlibxml を使っているライブラリを採用することをおすすめします。
FluentDom
FluentDom は PHP で DOMDocument を jQuery のように滑らかに扱えるようにしてくれる XML インターフェイスです。セレクターは Path または CSS を使って記述します(内部的に CSS と Path の変換を行っています)。現在のバージョンでは DOM による標準インターフェイスを拡張し、DOM Living Standard の機能を追加しています。FluentDOM は JSON や CSV、JsonML、RabbitFish などの形式を読むことができ、Composer を使ってインストールすることができます。
HtmlPageDom
HtmlPageDom は HTML ドキュメントをカンタンに操作できる PHP ライブラリーで、DOM ツリーをトラバースするための Symfony2 コンポーネントの DomCrawler を拡張し、ツリーを操作するためのメソッドを追加したものとなっています。
phpQuery
phpQuery(何年も更新停止中)はサーバーサイドで動作する、メソッドチェーン可能な CSS3 セレクター駆動の DOM API です。jQuery JavaScript ライブラリーをベースに PHP5 で書かれており、いくつかのコマンドラインインターフェイス( CLI )も提供しています。
参照:https://github.com/electrolinux/phpquery
Zend_Dom
Zend_Dom は DOM ドキュメントおよび構造体操作のためのツールを提供しています。現在は Xpath と CSS を使って DOM ドキュメントを参照するための東郷インターフェイスである Zend_Dom_Query を提供しています。
QueryPath
QueryPath は XML と HTML を操作するための PHP ライブラリーです。ローカル上のファイルだけでなく Web サーバーやデータベース上のデータさえも取り扱えるように設計されています。CSS 形式のセレクタを含む多くの jQuery インターフェイスも実装していますが、特にサーバーサイドでの利用に特化したチューニングが行われています。 Composer からインストール可能です。
fDOMDocument
fDOMDocument は標準 DOM を拡張し、PHP の Warning や Notice のかわりにすべてのエラーを例外で返すように作られています。またいくつかのカスタムメソッドや便利なショートカットメソッドを用意し、DOM 操作をシンプルで便利に行えるようにしています。
sabre/xml
sabre/xml は XMLReader と XMLWriter クラスを包含し拡張し、XML をオブジェクトや配列へとシンプルにマッピングするシステムとデザインパターンを実装しています。XML の読み書きはシングルパスのため、巨大な XML ファイルであってもわずかなメモリしか消費せず、かつ高速に処理することが可能です。
FluidXML
FluidXML は XML を簡潔かつスムーズに操作するための PHP ライブラリーです。Xpath を強化し、Fluent Interface のデザインパターンに沿ったインターフェイスにより効率的で楽しく書けるように設計されています。
サードパーティー製ライブラリ(libxmlベースではないもの)
DOM や libxml ベースで構築されたライブラリはパース処理にネイティブコードが利用されるため効率よく高速に動作します。しかしすべてのライブラリがこの手法を採用しているわけではありません。以下にいくつかの例を掲載します
PHP Simple HTML DOM Parser
- PHP5+で書かれた、HTMLをとても簡単に操作するためのHTML DOMパーサーです。
- PHP 5+ 必須
- 壊れたHTMLにも対応
- jQueryのようなセレクタを使ってHTMLページ上のタグを探索
- 1行のコードでHTMLからのデータ取り出し
私は通常このパーサーの利用は推奨しません。実装コードは酷いもので、パーサー自体の動作も遅くメモリーを大量に消費します。なおかつすべての jQuery セレクター(たとえば 子セレクタ)が使えるわけではありません。独自パーサーはなく libxml を利用しているライブラリは、だいたいどれもこれよりもかなりマシに動作します。
PHP Html Parser
PHP Html Parser はシンプルでフレキシブルな HTML パーサーで、jQuery のようにあらゆる CSS セレクターを使ってタグを選択することができます。HTML が壊れていようがいまいが、気にせずカンタンにデータの取り出しを行えるツールの開発を目標としています。このプロジェクトは当初 sunra/php-simple-html-dom-parser によってサポートされていましたが、それが止まってしまっているようなので、現在は私が改良を行っています。
繰り返しになりますが、私はこのパーサーの利用も推奨しません。CPU を大量に使用しパースも低速な上に、作成した DOM オブジェクトをメモリから開放する機能さえも用意されていません。ネストされたループ処理では特にこれが問題になってきます。ドキュメントは不正確でスペルもぐちゃぐちゃ、修正提案にも 2016 年 4 月 14 日以降まったく返事がなくなりました。
Ganon
- HTML/XML/RSS用のユニバーサルトークナイザー兼DOMパーサー
- 要素や属性の操作をサポート
- 壊れたHTMLやUTF-8のサポート
- jQueryのようなCSS3ライクの要素セレクタを利用可能 (名前空間をサポート)
- HTML TidyのようにHTMLの整形も可能
- CSSやJavascriptの圧縮
- 属性のソート、大文字小文字の変更、インデントの修正など
- 拡張性
- 現在の文字・トークンに基づいた、コールバックによるドキュメントのパース
- 簡単に処理を上書きできるよう、細かい単位で分割された操作関数
- 高速で簡単
これは使ったことがないため、良し悪しは分かりません。
HTML 5
上記のものを使って HTML5 をパースすることはできますが、HTML5 で許された新しい記法のため 互換性の問題が起きることがあります。そのため、HTML5を 取り扱いたい場合は専用のパーサーの利用を検討する必要があります。
メジャーなデスクトップ Web ブラウザとの互換性を最大限に確保するために、WHATWG の HTML5 仕様に基づいて実装された Python および PHP 向けの HTML パーサーです。
HTML5 仕様が確定したら、より多くの専用パーサーが登場するかもしれません。W3 のブログ記事「 How-To for html 5 parsing 」をぜひみてみてください。
Webサービス
これらの処理を PHP で書きたくなければ、Web サービスを使うこともできます。ただ私の経験上では、あまり使えるユーティリティの数は多くありませんでした。
YQL
YQL はインターネットを通じて異なるソースからデータを取得し、フィルタし、結合してくれるサービスです。SQL に似た構文の YQL ステートメントを使えるので、データベースの利用経験がある開発者にとって親しみやすいものになっています。
ScraperWiki
ScraperWiki の外部インターフェイスは Web 上や自身のアプリケーション上のデータを取得することができ、またスクレイパーの状態なども得ることができます。
正規表現
最後に、一番オススメできない方法ではありますが、正規表現を使って HTML からデータを取得することも可能です。ただし一般的に HTML に対する正規表現の利用は推奨されません。
Web 上で見つかる HTML 用の正規表現コードは、そのほとんどが諸刃の剣です。たいてい HTML のごく一部分の切り出しに対してのみ正しく動作するようなものだったりしますし、完全に正しく作られていないものは、空白がひとつ入ってしまったり属性をわずかに書き換える程度の小さな変更が起こっただけでたちまち動かなくなったりしてしまいます。HTML に対して正規表現を使う前に、あなたが本当に何をしたいのかをまず考えるべきです。
HTML パーサーは HTML の構文ルールなどをすでに実装していますが、正規表現を使う場合は何をどう扱うべきなのかをひとつひとつ理解しながら正規表現を書く必要があります。もちろん正規表現で十分なケースもありますが、実際にそうなのか否かは用途によって完全にケースバイケースとなってきます。
もちろん 信頼できるパーサーを作ることも不可能ではありません が、すでに前述のいくつものライブラリーが存在しそれらがより良い仕事をこなしてくれるなかで、完全に仕様に準拠し完璧に信頼をおけるカスタムパーサーを正規表現で作り上げるのは完全に時間のムダになります。
こちらもご参照ください >Parsing Html The Cthulhu Way
書籍
もし書籍にお金を払えるのであれば、この本を読んでみてください。
なお、私は PHP Architect や著者のアフィリエイトは行っていません。
スタック・オーバーフローの最新情報を翻訳掲載!
主にプログラミング技術に関するナレッジを共有するコミュニティサイト「 Stack Overflow 」。2014 年に開設された日本語版サイトを利用しているエンジニアもいらっしゃることでしょう。ここでは、LIG のエンジニアが気になった本国版の投稿のなかから、プログラマーの悩みや課題を解消できるようなネタをピックアップし、翻訳して解説するコンテンツを手がけていきます。
>> スタック・オーバーフロー 本国サイト
>> スタック・オーバーフロー 日本語版サイト
LIGはWebサイト制作を支援しています。ご興味のある方は事業ぺージをぜひご覧ください。