最近、imgタグのsrcだけ書き換えたいという事例が発生したので、HTML::Parserを使ってやってみた。
サンプル
例えばサンプルとして以下のコード。
package HTML::Converter; use utf8; use strict; use warnings; use parent qw(HTML::Parser); # HTMLのimg srcを変換する # my $converter = HTML::Converter->new; # my $result = $converter->convert($html); sub new { my ($class, $opts) = @_; my $self = $class->SUPER::new( api_version => 3, start_h => [ \&_start_tag, "self,tagname,attr,text" ], default_h => [ \&_default, "self,text" ], ); $self->{converted_html} = ''; $self->{prefix_url} = $opts->{prefix_url}; return $self; } sub convert { my ($self, $html) = @_; $self->{converted_html} = ''; $self->parse($html); return $self->{converted_html}; } sub prefix_url { my ($self) = @_; return $self->{prefix_url}; } sub _start_tag { my ($self, $tagname, $attr, $text) = @_; # img以外はそのまま通す if ($tagname ne 'img') { $self->{converted_html} .= $text; return; } $self->{converted_html} .= "<$tagname"; if ($attr->{src}) { $attr->{src} = $self->prefix_url . $attr->{src}; } for my $attr_name (keys %$attr) { next if $attr_name eq '/'; my $attr_val = $attr->{$attr_name}; $self->{converted_html} .= qq{ $attr_name="$attr_val"}; } $self->{converted_html} .= ' /' if $attr->{'/'}; $self->{converted_html} .= '>'; } sub _default { my ($self, $text) = @_; $self->{converted_html} .= $text; } 1;
以下のように使う。
my $converter = HTML::Converter->new({ prefix_url => 'http://example.com', }); my $html = $converter->convert(q[ <a href=""> <img src="/example.png" /> example </a>]); warn $html;
そうするとimgのsrcがhttp://example.com/example.pngのようになる。
やっていること
HTML::Parserはタグが開始した時というイベントで、その時パースしているタグの情報を取得できるので、それを使えば特定のタグの中身だけ書き換えたHTMLを作ることが出来る。
以下のようにstart_hで指定することでタグが開始した時に実行する関数を定義できる。
my $parser = HTML::Parser->new( api_version => 3, start_h => [ sub { ... }, "self,tagname,attr,text" ], );
start_hのリストの一番目に実行したい関数を渡して、二番目にどういう引数を渡して欲しいか書く。今回はタグ名とか属性のハッシュとか、生のhtmlを渡してもらうようにする。
そして以下のように関数を定義すれば、それらを使ってごちゃごちゃ出来る。
sub _start_tag { my ($self, $tagname, $attr, $text) = @_; warn $tagname; # ... }
あとはサンプルのようにタグ名や属性を使ってHTMLを書き換えて行ったら良い。
まとめ
今回はHTML::Parserを使ったHTMLの変換の話について書きました。たまにこういうのが必要になるので勉強になりました。