ホームページ >ウェブフロントエンド >htmlチュートリアル >Jsoupコード解釈その6-パーサー(その2)_html/css_WEB-ITnose

Jsoupコード解釈その6-パーサー(その2)_html/css_WEB-ITnose

WBOY
WBOYオリジナル
2016-06-24 11:26:461375ブラウズ

最近生活が少し忙しくて、いつも夜中に起きていて精神状態があまり良くありません。仕事もうまくいきません。アイデアはたくさんありますが、コードほどうまくいかないものもあります。忘れてください、私はまだ正しい姿勢を持っています。結局のところ、私の資格はまだ若いので、私は私の資格を続けます。

Jsoup のソース コードを読むのは退屈ではありません。結局のところ、パーサーもクローラーの重要なコンポーネントの 1 つです。コードを読んだ後、多くのことを得ることができ、HTML の知識も向上しました。

DOM ツリー生成処理

ここで TreeBuilder の部分を分けて構文解析処理と呼ぶのは少し不適切かもしれませんが、実際には Token に基づいて DOM ツリーを生成する処理です。このコンパイラでは。

TreeBuilder もファサード オブジェクトです。実際の構文解析は次のコードです。

protected void runParser() { while (true) { Token token = tokeniser.read();process(token);if (token.type == Token.TokenType.EOF) break; }}

TreeBuilder には、HtmlTreeBuilder と XmlTreeBuilder という 2 つのサブクラスがあります。 XmlTreeBuilder は当然、XML ツリーを構築するためのクラスです。実装は基本的にスタックを維持し、さまざまなトークンに従ってノードを挿入します。

@Overrideprotected boolean process(Token token) { // start tag, end tag, doctype, comment, character, eof switch (token.type) { case StartTag: insert(token.asStartTag()); break; case EndTag: popStackToClose(token.asEndTag()); break; case Comment: insert(token.asComment()); break; case Character: insert(token.asCharacter()); break; case Doctype: insert(token.asDoctype()); break; case EOF: // could put some normalisation here if desired break; default: Validate.fail("Unexpected token type: " + token.type); } return true;}

insertNode のコードはおおよそ次のとおりです (表示の都合上、一部のメソッドは次のようになります)。統合):

Element insert(Token.StartTag startTag) { Tag tag = Tag.valueOf(startTag.name()); Element el = new Element(tag, baseUri, startTag.attributes); stack.getLast().appendChild(el); if (startTag.isSelfClosing()) { tokeniser.acknowledgeSelfClosingFlag(); if (!tag.isKnownTag()) // unknown tag, remember this is self closing for output. see above. tag.setSelfClosing(); } else { stack.add(el); } return el;}

HTML 解析ステート マシン

XmlTreeBuilder と比較して、HtmlTreeBuilder は実装がより複雑です。同様のスタック構造に加えて、HtmlTreeBuilderState は HTML を分析するためのステート マシンの構築にも使用されます。どうしてこれなの? HtmlTreeBuilderState でどのような状態が使用されているかを確認することもできます (コード内で状態を示します)。

<!-- State: Initial --><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!-- State: BeforeHtml --><html lang='zh-CN' xml:lang='zh-CN' xmlns='http://www.w3.org/1999/xhtml'><!-- State: BeforeHead --><head> <!-- State: InHead --> <script type="text/javascript"> //<!-- State: Text --> function xx(){ } </script> <noscript> <!-- State: InHeadNoscript --> Your browser does not support JavaScript! </noscript></head><!-- State: AfterHead --><body><!-- State: InBody --><textarea> <!-- State: Text --> xxx</textarea><table> <!-- State: InTable --> <!-- State: InTableText --> xxx <tbody> <!-- State: InTableBody --> </tbody> <tr> <!-- State: InRow --> <td> <!-- State: InCell --> </td> </tr> </table></html>

ここで、HTML タグには a34de1251f0d9fe1e645927f19a896e8、b6c5a531a458a2e790c1fd6421739d1c などのネスト要件があることがわかります。

を組み合わせて使用​​します。 Jsoup コードによると、HtmlTreeBuilderState が次のことを行うことがわかります:

文法チェック

たとえば、tr が table タグ内でネストされていない場合、構文エラーになります。以下のタグがInBody状態に直接出現するとエラーが発生します。 Jsoup でこの種のエラーが発生した場合は、トークン解析を見つけてエラーを記録し、直接終了せずに次のコンテンツの解析を続けます。

InBody { boolean process(Token t, HtmlTreeBuilder tb) { if (StringUtil.in(name, "caption", "col", "colgroup", "frame", "head", "tbody", "td", "tfoot", "th", "thead", "tr")) { tb.error(this); return false; } }

タグの補完

たとえば、head タグが閉じられておらず、本文にのみ表示できるタグがいくつか書かれている場合、自動的に 9c3bca370b5104690d9ef395f2c5f8d1 が閉じられます。 HtmlTreeBuilderState には、自動タグ補完を提供するメソッド anythingElse() があります。たとえば、InHead 状態の自動終了コードは次のとおりです。

private boolean anythingElse(Token t, TreeBuilder tb) { tb.process(new Token.EndTag("head")); return tb.process(t); }

次のようなタグ終了メソッドもあります。

private void closeCell(HtmlTreeBuilder tb) { if (tb.inTableScope("td")) tb.process(new Token.EndTag("td")); else tb.process(new Token.EndTag("th")); // only here if th or td in scope}

ケース スタディ

タグがなくなったらどうなりますか?

さて、パーサーのソース コードをたくさん読んだ後は、日常のアプリケーションに戻ったほうがよいでしょう。ページ上にさらに 1 つまたは 2 つの閉じられていないタグを記述するのが通常であることはわかっていますが、それらはどのように解析されるのでしょうか?

dc6dce4a544fdca2df29d5ac0ea9906b タグを例に挙げます:

  • 開始タグを忘れて終了タグだけを書きました
おめでとうございます、この 16b28748ea4df4d9c2150843fecfba68 はエラーとして扱われるため、ページは間違いなくめちゃくちゃでした!もちろん、単に 16b28748ea4df4d9c2150843fecfba68 を追加しただけでは、何の影響もありません。 (タグが閉じられないようにするために、ページの一番下にもう少し 16b28748ea4df4d9c2150843fecfba68 を書くという話を誰かがしてくれたのを覚えています)

    開始タグを書きましたが、終了タグを見逃していました
分析しましょうこの状況はもう少し複雑です。内部にコンテンツをネストできないタグの場合、受け入れられないタグが見つかったときに閉じられます。 dc6dce4a544fdca2df29d5ac0ea9906b タグにはほとんどのタグを含めることができ、その場合、その有効範囲は HTML の最後まで続きます。

さて、パーサーシリーズの解析は終わりましたが、この期間でHTMLやステートマシンの内容はかなり勉強しましたが、実際に使うには程遠いです。まずは選択部分から始めましょう。これは日常的に使用する場合により意味があると思われます。

最後に、私の Jsoup シリーズのブログとソース コードのアドレスを添付します: http://github.com/code4craft/jsoup-learning

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。