2013年06月
Released HTML::HeadParser::Liberal
(追記)なんか最新版ではなおってたらしいです!
現時点のLWP::UserAgentで meta nameに":"が使用されている(例:twitter:cardに対応している)サイトのコードを取得しに行くと色々妙な事が起こる。何が問題かというと、LWP::UserAgentはコンテンツのデコードを行ったりする際にHTMLの中身を解析してメタタグから情報を引っ張ったりして文字コード判定のヒントを仕入れたりしてる際、HTML::HeadParserというモジュールがそのようなメタデータをHTTP::Headerオブジェクトに格納しようとしているのが問題を起こしている。
現時点のLWP::UserAgentで meta nameに":"が使用されている(例:twitter:cardに対応している)サイトのコードを取得しに行くと色々妙な事が起こる。何が問題かというと、LWP::UserAgentはコンテンツのデコードを行ったりする際にHTMLの中身を解析してメタタグから情報を引っ張ったりして文字コード判定のヒントを仕入れたりしてる際、HTML::HeadParserというモジュールがそのようなメタデータをHTTP::Headerオブジェクトに格納しようとしているのが問題を起こしている。
例えば、
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />という指定があれば
my $res = $lwp->get("...");には上記のメタタグの値が入る。内部的には以下のようにメタタグの中身をHTTP::Headerオブジェクトに格納している
print $res->header('Content-Type');
$headers->push_header("Content-Type", "...");
同様にhttp-equivでなくとも、name属性がある場合は "X-Meta-...."というヘッダにその値を格納するようになる。なので以下のようにtwitter:card用のメタタグが入っていた場合:
<meta name="twitter:card" content="summary" />この値をヘッダに入れようとするわけだが、このname属性、コロンが入っている。ということはヘッダ名は・・・"X-Meta-Twitter:Card"。ぎゃー!これRFC違反!ヘッダの名前に":"を入れちゃいけない!そんなところでHeadParserの処理がおかしなところでとまってしまうのであります。
これを回避するには現時点ではLWP::UserAgentのparse_head属性を偽にする必要がある
my $lwp = LWP::UserAgent->new(parse_head => 0);でもこれだとHTMLヘッドセクション内のヒントを使うな、って話になるんだけどまれに必要な時もあるし、それに段々twitter cardを実装しているサイトも増えてるし、おかげで僕は先日何時間も無駄にしたし・・・・ということでHTML::HeadParser::Liberalというモジュールを書いた。
# もしくは
# $lwp->parse_head(0);
使用方法はuseするだけ。
use HTML::HeadParser::Liberal;これでtwitter:cardもパースして、X-Meta-Twitter-Cardというヘッダ名でHTTP::Headersオブジェクトからアクセスする事ができます。
このモジュールはHTML::HeadParserの内部にかなり強引に手を入れて、 twitter:cardのような書式のメタタグ名があったら ":"を無理矢理"-"に変えてヘッダに格納するようにする、というだけ。将来的に他にも必要な調節があったら入れるつもりだけど、とりあえずはこれだけです。
ちなみに効果はグローバルです。というわけでtwitter:cardもいいからとりあえずパースしろよ!って時にはどうぞ。 そのうちHTML::HeadParser本体に修正が入る事を祈る・・・