メメメモモ

プログラミング、筋トレ、ゲーム、etc

perlでスクレイピング「HTML解析してデータを取得」

HTMLからデータを取ってくることは正規表現でもできます。
しかし、ちょっとでも表記が変わってしまうとうまく動作しなくなってしまいます。
また、正規表現を読み解くのは結構面倒くさいので、保守するのも大変です。


javascriptにはDOMやXPathがあります。
HTML内のデータにアクセスする記述は、正規表現より分かりやすいです。
perlでもモジュールを用いれば、同じようにアクセスできるようになります。


DOM操作を可能にしてくれるモジュールが「HTML::TreeBuilder」です。
下記のような感じで使用します。

#!/usr/bin/env perl                                                             
use strict;
use warnings;
use utf8;
use Encode;
use HTML::TreeBuilder;

my $html=<<'HTML';
<html>                                                                          
<head>                                                                          
<title>Test Page</title>                                                        
</head>                                                                         
<body>                                                                          
<div id="content">                                                              
<h1>Test Page</h1>                                                              
<h2>主なトピックス</h2>                                                         
<ul class="clr">                                                                
<li><span class="dateRight">8月30日</span><a href="/topics/title1.html">Title1<\
/a></li>                                                                        
<li><span class="dateRight">8月29日</span><a href="/topics/title2.html">Title2<\
/a></li>                                                                        
<li><span class="dateRight">8月28日</span><a href="/topics/title3.html" class="\
newTitle">Title3</a></li>                                                       
<li><span class="dateRight">8月27日</span><a href="/topics/title4.html">Title4<\
/a></li>                                                                        
</ul>                                                                           
</div>                                                                          
</body>                                                                         
</html>              
HTML                                                                            

my $tree = HTML::TreeBuilder->new;
$tree->parse($html);

my @items = $tree
    ->look_down('id', 'content')
    ->look_down('class', 'clr')
    ->find('li');

print encode_utf8($_->as_text)."\n" for @items;
# 8月30日Title1
# 8月29日Title2
# 8月28日Title3
# 8月27日Title4

メソッドチェーンでアクセスできるようになります。



また、「HTML::TreeBuilder::XPath」を用いれば、XPathでアクセスできるようになります。

use HTML::TreeBuilder::XPath;

my $tree = HTML::TreeBuilder::XPath->new;
$tree->parse($html);
my @xitems = $tree->findnodes(q{//div[@id='content']//ul[contains(concat(' ', @class, ' '), ' clr ')]//li
});
print encode_utf8($_->as_text."\n") for @xitems;


XPathの記述が難しければ、CSSセレクタからXPathに変換する「HTML::Selector::XPath」を使用します。

use HTML::TreeBuilder::XPath;
use HTML::Selector::XPath 'selector_to_xpath';

my $tree = HTML::TreeBuilder::XPath->new;
$tree->parse($html);
my @xitems = $tree->findnodes(selector_to_xpath('div#content ul.clr li'));
print encode_utf8($_->as_text."\n") for @xitems;

ウェブページを作っていればCSSセレクタが一番なじみがあるので分かりやすいですね。


ということで、本格的に運用するプログラムであれば、正規表現を使うよりHTML解析するモジュールを使用した方がいいかもしれません。