1. まとめトップ

HTMLをパースする

更新日: 2013年10月27日

twcritiqueさん

  • このまとめをはてなブックマークに追加
0 お気に入り 40 view
お気に入り追加

そこには I’ve never seen an HTML file parsed with a single line of Perl RegEx before! なんて言葉といっしょにこんなコードが。

foreach $element ($html =~/(.*?)(<(?:(?:!--.*?--)|(?:\/?[a-z0-9_:.-]+(?:\s+[a-z0-9_:.-]+(?:=(?:[^> '"\t\n]+|(?:'.*?')|(?:".*?")))?)*))\s*\/?\s*>)/isg)
{
$element = TrimWS($element) if $trim;
$dict->Add($i++, ParseTag($element, $trim)) if length $element;
}

XMLHttpRequest 仕様は HTML のパース処理をサポートしました (これまで XMLHttpRequest は XML のパース処理しかサポートしていませんでした)。これによって、Web アプリは XMLHttpRequest から HTML をパース済の DOM として取得できます。

XMLHttpRequest で HTML を DOM として取得するのは、XML を DOM として取得するのとそう変わりません。ただ、非同期モードを利用しなければいけないことと、XMLHttpRequest オブジェクトの open() をコールしたあと、send() をコールする前に responseType プロパティに "document" を指定し、明示的に文書をリクエストしなければいけないという違いがあります。

Rhino+JQueryでHTMLをスクレイピングしようと考えていたんだけど、Rhino+JQueryだとどうもJQUeryオブジェクトのガベージができていないように見えて(メモリリークしているような感じ)、実用に耐えられない雰囲気だったので、違う方法を模索していたら、jerichoが良さそうだったので書いてみました。
Rhino:jQueryでメモリリーク?
jerichoなんか、HTMLだけじゃなくて、XMLのパースにも便利に使えるようです。
HTMLも不完全なものもちゃんとパースできるみたいです。

「PHPに便利な関数ないかなー」と探していると、simplexml_load_stringという関数がありました。これは、XML文書をパースしてオブジェクト化する関数です。これで、HTMLも解析してくれないかな・・・と試してみます。

あるページをPHPで解析して、余分な箇所を除去したい。そんなことがありました。
そこでなーんかいい方法はないものかと調べたところ「PHP Simple HTML DOM Parser」なる物があることがわかりました。

PerlでHTML取得・解析したいときはLWP::UserAgentとHTML::TreeBuilderというのを使うと簡単にできます。
LWP::UserAgentを使うと、Webページの取得ができます。
HTML::TreeBuilderを使うと、HTMLのDOM解析ができます。

この2つのモジュールを使って、Yahoo!Japanのトピックス一覧を取得してみましょう。
コメント行と空行を除くとたったの13行です。

HTMLParserを利用してHTMLのタグ解析を行う。
特定のサイトにあるAタグを抽出して、リンクURLとアンカーテキストの組を作る。

HTML::Paserはオブジェクト指向Perlを全面的に採用しているため、今までの手続き型のライブラリとは使い方が違います。基本的なPaserとしての機能がHTML::Paserに実装されており、それをオーバーライドすることにより各自必要な機能を追加するというものです。
HTML::Paserは呼び出すだけでは何も出力しません。その逆にすべてを出力するモジュールとしてHTML::Fiterが用意されています。こちらのソースを見てみましょう。

JavaのHTMLパーサにはいろんなものがあるようなのですが、AndroidのSDKでも使用されているというTagSoupというものを使ってみようかと前回の記事でも触れていました。
 今回は、実際にTagSoupを使ってHTMLの読み込みを試してみました。 いろいろ試すのもめんどくさいので、使いやすそうであればそのまま使っちゃおうと。 そんな目論見。

今回<hr>で区切られて繰り返してるのですが、例えば[]の中身を抜きたいなんて時。
?> html.scan(/<hr.*?\[(.*?)\]/m)

=> [["huga"], ["hoga"]]

ら、入れ子もなんとか処理できるみたいです。以下はPerlのコード例。(「詳細 正規表現 第3版」を参考にしました)

$str = "(a (b, c)), (d), (e, f)";
$paren = qr/\([^()]*(?:(??{$paren})[^()]*)*\)/;
while ($str =~ /($paren)/g) {
print $1, "\n";
}

1




このまとめへのコメント0

1

いつもお気に入りいただきましてありがとうございます。ロドリゲス出版はリテラシーをテーマにした電子出版をしてます。最近書評ラジオ始めました
http://bit.ly/1c0PzlM

このまとめに参加する