HTML文字列から何かを解析または抽出する方法を尋ねる質問を毎日目にします。最初の回答/コメントは常に「怒りを感じないように、正規表現を使用してHTMLを解析しないでください!」です。 (その最後の部分は時々省略されます)。
これは私にとってかなり混乱します。一般的に、複雑な文字列を解析する最良の方法は正規表現を使用することだといつも思っていました。では、HTMLパーサーはどのように機能しますか?正規表現を使用して解析しませんか。
正規表現を使用するための1つの特定の議論は、構文解析の代替手段が常にあるとは限らないということです(JavaScriptなど、DOMDocumentが普遍的に利用可能なオプションではありません)。たとえば、jQueryは、正規表現を使用してHTML文字列をDOMノードに変換することで問題なく管理できるようです。
これをCWするかどうかはわかりませんが、私が答えたいのは本物の質問であり、ディスカッションスレッドを意図したものではありません。
通常、トークナイザーを使用します。ドラフト HTML5仕様には広範なアルゴリズムがあります 「実世界のHTML」を処理するため。
では、HTMLパーサーはどのように機能しますか?正規表現を使用して解析しませんか?
うーん、ダメ。
脳内で計算理論のコースに戻った場合、1つ、コンパイラのコース、または同様のコースを受講した場合、さまざまな種類の言語と計算モデルがあることを思い出すかもしれません。私はすべての詳細に立ち入る資格はありませんが、あなたと一緒にいくつかの主要なポイントを確認することができます。
(これらの目的のための)最も単純なタイプの言語と計算は正規言語です。これらは正規表現で生成でき、有限オートマトンで認識できます。基本的に、これは、これらの言語の文字列の「解析」は状態を使用しますが、補助メモリは使用しないことを意味します。 HTMLは確かに正規言語ではありません。考えてみれば、タグのリストは任意に深くネストすることができます。たとえば、テーブルにはテーブルを含めることができ、各テーブルには多数のネストされたタグを含めることができます。正規表現を使用すると、タグのペアを選択できる場合がありますが、任意にネストされたものは確かにありません。
規則的ではない古典的な単純な言語は、正しく一致する括弧です。できる限り試してみてください。常に機能する正規表現(または有限オートマトン)を作成することはできません。入れ子の深さを追跡するには、メモリが必要です。
メモリ用のスタックを備えたステートマシンは、計算モデルの次の強みです。これはプッシュダウンオートマトンと呼ばれ、文脈自由文法によって生成された言語を認識します。ここでは、正しく一致する括弧を認識できます。実際、スタックはそのための完璧なメモリモデルです。
さて、これはHTMLには十分ですか?悲しいことに、いいえ。たぶん、実際には、すべてのタグが常に完全に並んでいる、非常に注意深く検証されたXMLの場合です。実際のHTMLでは、<b><i>wow!</b></i>
のようなスニペットを簡単に見つけることができます。これは明らかにネストしないので、正しく解析するために、スタックは十分に強力ではありません。
次のレベルの計算は、一般的な文法によって生成され、チューリングマシンによって認識される言語です。これは、事実上、最も強力な計算モデルであると一般に認められています。補助メモリを備えたステートマシンであり、そのメモリはどこでも変更できます。これがプログラミング言語でできることです。これは、HTMLが存在する複雑さのレベルです。
ここですべてを1つの文に要約すると、一般的なHTMLを解析するには、正規表現ではなく、実際のプログラミング言語が必要です。
HTMLは、他の言語が解析されるのと同じ方法で解析されます:字句解析と構文解析。字句解析ステップは、個々の文字のストリームを意味のあるトークンに分解します。解析ステップでは、状態とメモリを使用してトークンを組み立て、操作可能な論理的に一貫性のあるドキュメントにします。
正規表現は、パーサーの1つの形式にすぎません。正直なHTMLパーサーは、テキストを適切に解釈するために recursive descent 、prediction、およびその他のいくつかの手法を使用して、正規表現で表現できるよりもはるかに複雑になります。本当に入りたいのなら、 Lex&yacc や同様のツールをチェックしてみてください。
HTML解析に正規表現を使用することの禁止は、おそらく「HTMLを解析するためにnaive正規表現を使用しないでください...」(あなたがたが怒りを感じないように)「...そして結果を注意して扱ってください。」特定の特定の目標については、正規表現で十分な場合がありますが、正規表現の制限に注意し、解析するテキストのソースに適切なだけ注意する必要があります(たとえば、ユーザー入力、実際には非常に注意してください)。
HTMLの解析は、線形テキストをツリー構造に変換することです。正規表現は通常、ツリー構造を処理できません。次のトークンを取得するために各ポイントで必要な正規表現は常に変化します。パーサーで正規表現を使用できますが、可能な解析状態ごとに正規表現の配列全体が必要になります。
100%のソリューションが必要な場合:HTMLを文字ごとに反復する独自のカスタムコードを作成する必要があり、現在のノードを停止して開始する必要があるかどうかを判断するための膨大な量のロジックが必要です。次。
その理由は、これが有効なHTMLであるためです。
<ul>
<li>One
<li>Two
<li>Three
</ul>
しかし、これもそうです:
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
</ul>
「90%ソリューション」で問題がない場合:XMLパーサーを使用してドキュメントをロードすることは問題ありません。または、正規表現を使用します(ただし、コンテンツのマスターであれば、xmlの方が簡単です)。