perlでHTML5を解析して情報を抽出するコード(HTML::TagParser版)

公開日:2014/01/24 更新日:2014/01/24
perlでHTML5を解析して情報を抽出するコード(HTML::TagParser版)のサムネイル

この前、htmlファイルを解析して欲しい情報を抽出するためのperlコードをメモしました。(詳しくはこちら)ただ、この前のコードだとHTML5に対応できないことに後々気づいたので、今回はHTML5でも対応できるコードを改めてメモします。

HTML5になると何が変わるか

HTML5とそれ以前のHTMLの違いについて検索してサイトを見てみると、以下のように記述されていました。

例えば、 ヘッダを示す<header>、 フッタを示す<footer>、 一つのセクションであることを示す<section>、 記事であることを示す<article>、 ナビゲーションであることを示す<nav> などの要素が追加されており、それぞれの役割に応じて適切な要素に割り当てることができるようになります。 HTML 4との違い、HTML5で可能になること | HTMLクイックリファレンス

恥ずかしい話ですが、私は上記のことも含めてHTML5について全く知りませんでした。そのため、最近新しくしたこのサイトのテーマがHTML5であることも知らず、この前メモしたperlコード(こちら)を使って、新しいテーマを適用した後の自分のサイトのトップページから情報を抽出しようとしたら見事にできませんでした。具体的には、<article>というタグ名を指定して情報を抽出しようとしましたが、この前のコードでは情報を抽出できませんでした。この時点で始めて<article>がHTML5から追加されたものであり、さらにこのサイトの新しいテーマがHTML5対応であることに気づきました。。。

HTML::TagParserを使う

HTML5に対応するにはどうしたらいいのか検索して調べると、「HTML::TagParser」というモジュールが使えそうであることが分かりました。このHTML::TagParserは日本人の方が作成したようで、ドキュメントも日本語で記述されていて大変助かりました。以下に作成者様のサイトを載せておきます。

HTML::TagParserのインストール

インストールは以下のようにcpanmコマンドを使用しました。

cpanm HTML::TagParser

これでHTML::TagParserが使えるようになります。ただ、Perlのモジュールのパス指定がうまくいっていない場合は以下のようなエラー出る場合があります。

Can't locate HTML/TagParser.pm in @INC (@INC contains: /etc/perl /usr/local/lib/perl/5.14.2 /usr/local/share/perl/5.14.2 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.14 /usr/share/perl/5.14 /usr/local/lib/site_perl .) at parse.pl line 9.
BEGIN failed--compilation aborted at parse.pl line 9.

この場合の対策については、以下のサイトが参考になると思います。

上記のサイトに載っている方法を試しても以前同じエラーが出る場合やわからない場合は、とにかくHTML::TagParserのプログラムである「TagParser.pm」をperlモジュールのディレクトリ内に置いてみるとエラーが消えると思います。なお、perlモジュールのディレクトリは、上記のエラーの中にも記述されているように、「/etc/perl」、 「/usr/local/lib/perl/5.14.2」、 「/usr/local/share/perl/5.14.2」、「/usr/lib/perl5」 「/usr/share/perl5」、「/usr/lib/perl/5.14」、「/usr/share/perl/5.14」あたりです。ただし、これはubuntuの場合です。

また、TagParser.pmは、冒頭に載せた作成した方のサイトにtar.gz形式で置いてあります。このtar.gz形式のファイルをtarコマンドで解凍し、解凍後に出来たディレクトリ(「HTML-TagParser-0.20」という名前でした)の中にあります。

HTML::TagParserを使ったコード

今回作成したもコードも前回と同様に、このサイトのトップページのhtmlファイルの冒頭部分から、新着順に並んだ投稿記事に関する以下の情報を抽出してターミナル上に表示します。ただし、テーマを変更したため、タグ名などが変わっています。

  • ① 投稿日 → <div id="postdate">の中
  • ② URL → <h2 class="entry-title">の中
  • ③ 識別番号 → <h2 class="entry-title">のURLの中
  • ④ タイトル → <h2 class="entry-title">の中
  • ⑤ 記事説明 → <div id="ainseo">の中

今回情報の抽出を試す対象として使用したこのサイトのトップページのhtmlファイルを以下に載せておきます。以下のhtmlファイルは、この前と同様にこのサイトのトップページにある新着記事3個分を抜粋したhtmlファイルです。

<div class="odd">
          
<article id="post-1702" class="post-1702 post type-post status-publish format-standard category-programming">
  
  <header class="entry-header">
    <h2 class="entry-title"><a href="https://www.virment.com/programming/1702/" title="Permalink to perlでHTMLを解析して欲しい情報を抽出するためのコードをメモ" rel="bookmark">perlでHTMLを解析して欲しい情報を抽出するためのコードをメモ</a></h2>
    <div id="postdate">Date : 2014/01/21   Category : <a href="https://www.virment.com/category/programming/" title="programming の投稿をすべて表示" rel="tag">programming</a></div> 
  </header><!-- .entry-header -->

    
    <div class="entry-content post-content">

                <div class="imgthumb"><a href="https://www.virment.com/programming/1702/" rel="bookmark" title="perlでHTMLを解析して欲しい情報を抽出するためのコードをメモ"><img width="200" height="92" src="https://www.virment.com/wp-content/uploads/2014/01/virment_html_mod-300x139.png" class="attachment-200x175 wp-post-image" alt="virment_html_mod" /></a></div>       
      
            <div id="ainseo">HTMLを解析して特定のタグに囲まれている情報だけを抽出したり、リンクだけを抽出したりするコードを色々調べてperlで作成してみたのでメモしておきます。いわゆるスクレイピングするためのコードです。このコードでは、perlのHTML::TreeBuilderを使いました。</div>
    
      
      
      <div><a href="https://www.virment.com/programming/1702/" class="more-link">続きを読む</a></div>
      </div><!-- .entry-content -->
  
</article><!-- #post-1702 -->
          </div>
                  <div class="even">
          
<article id="post-1653" class="post-1653 post type-post status-publish format-standard category-virtualbox">
  
  <header class="entry-header">
    <h2 class="entry-title"><a href="https://www.virment.com/mac/virtualbox/1653/" title="Permalink to VirtualBoxにおける仮想マシンの仮想ディスク容量を拡張するための手順" rel="bookmark">VirtualBoxにおける仮想マシンの仮想ディスク容量を拡張するための手順</a></h2>
    <div id="postdate">Date : 2014/01/16   Category : <a href="https://www.virment.com/category/mac/virtualbox/" title="VirtualBox の投稿をすべて表示" rel="tag">VirtualBox</a></div> 
  </header><!-- .entry-header -->

    
    <div class="entry-content post-content">

                <div class="imgthumb"><a href="https://www.virment.com/mac/virtualbox/1653/" rel="bookmark" title="VirtualBoxにおける仮想マシンの仮想ディスク容量を拡張するための手順"><img width="200" height="169" src="https://www.virment.com/wp-content/uploads/2014/01/first_disp_mod-300x254.png" class="attachment-200x175 wp-post-image" alt="first_disp_mod" /></a></div>       
      
            <div id="ainseo">VirtualBoxでは、仮想マシン作成時に仮想ディスク容量を決めますが、仮想マシンを使用していると仮想ディスクの容量が不足してくることがあると思います。ここでは、このような場合に仮想マシンの仮想ディスク容量を拡張して増やすための手順をメモします。</div>
    
      
      
      <div><a href="https://www.virment.com/mac/virtualbox/1653/" class="more-link">続きを読む</a></div>
      </div><!-- .entry-content -->
  
</article><!-- #post-1653 -->
          </div>
                  <div class="odd">
          
<article id="post-1622" class="post-1622 post type-post status-publish format-standard category-linux">
  
  <header class="entry-header">
    <h2 class="entry-title"><a href="https://www.virment.com/linux/1622/" title="Permalink to Linux Mint 16で日本語入力環境を整えるまでの手順" rel="bookmark">Linux Mint 16で日本語入力環境を整えるまでの手順</a></h2>
    <div id="postdate">Date : 2014/01/15   Category : <a href="https://www.virment.com/category/linux/" title="Linux の投稿をすべて表示" rel="tag">Linux</a></div> 
  </header><!-- .entry-header -->

    
    <div class="entry-content post-content">

                <div class="imgthumb"><a href="https://www.virment.com/linux/1622/" rel="bookmark" title="Linux Mint 16で日本語入力環境を整えるまでの手順"><img width="176" height="175" src="https://www.virment.com/wp-content/uploads/2014/01/menu1_mod-300x297.png" class="attachment-200x175 wp-post-image" alt="menu1_mod" /></a></div>       
      
            <div id="ainseo">最近、Linux Mintというディストリビューションに興味を持ち、言語を英語に設定してインストールしました。ただ、日本語入力ができなかったので、日本語入力環境を整える必要がありました。ここに日本語入力できるようにするまでの手順をメモしておきます。</div>
    
      
      
      <div><a href="https://www.virment.com/linux/1622/" class="more-link">続きを読む</a></div>
      </div><!-- .entry-content -->
  
</article><!-- #post-1622 -->
          </div>

そして以下が作成したperlコードです。下記のperlコードが置いてある同じディレクトリに上記のhtmlファイルを「virtualiment_new.html」という名前で保存してあります。以下のコードは自己責任での使用をお願い致します。また、間違いなどがあればぜひ指摘して下さい。

use strict;
use warnings;
use HTML::TagParser;

# HTML::TagParserのオブジェクト作成
my $html = HTML::TagParser->new( "virtualiment_new.html" );

# オブジェクトの中から、~の間を取得
my @elem = $html->getElementsByTagName( "article" );

my $subhtml;
my $title;
my @tmp;
my $date;
my $date_tmp;
my $desc;
my $url;
my @id;

foreach my $tmp (@elem){

      # 取得した~の間部分の木構造オブジェクトを作成
   $subhtml = $tmp->subTree();

         # id要素の値が「postdate」である部分のテキストを取得(「Date : 2014/01/21   Category : 」が該当)
   $date_tmp = $subhtml->getElementById( "postdate" )->innerText();

         # $date_tmpの中の日付「年/月/日」を取得
   @tmp = grep{/(\d+)\/(\d+)\/(\d+)/}(split( /\s/, $date_tmp));
   $date = $tmp[0];

         # タグ名が「a」である部分のリンクを取得
   $url =  $subhtml->getElementsByTagName("a")->attributes->{href};

         # $urlの中の数字のうち3桁以上4桁以下の数字を取得
   @id = grep{/\d{3,4}/}(split( /\D+/, $url));

         # class要素の値が「entry-title」である部分のテキストを取得
   $title = $subhtml->getElementsByClassName("entry-title")->innerText();

         # id要素の値が「ainseo」である部分のテキストを取得
   $desc = $subhtml->getElementById( "ainseo" )->innerText();
   
    print "date is ".$date."\n";
    print "url is ".$url."\n";
    print "ID is ".$id[0]."\n";
    print "title is ".$title."\n";
    print "description is ".$desc."\n\n";

}

subTree()

23行目で使っているメソッドですが、これは、読み込んだhtmlファイルのうち<article>〜</article>の間にあるhtmlの木構造オブジェクトを作成して取得しているのだと思います。この前メモしたコードだと、この<article>を認識できませんでした。

ただし、正直なところ、これはsubtreeというメソッド名から推測して使ってみたら希望通りの動作をしただけで、ちゃんと確認していません。なので、ある場合にはちゃんと動作しないかもなので、ドキュメントをしっかり読むことをおすすめします。

getElement(s)By*

上記のコードで、getElementById( "postdate" )、getElementsByTagName("a")、getElementsByClassName("entry-title")というメソッドを使っています。これらは、それぞれid要素の値、タグ名、class要素の値を指定して、その中の文字列や値を取得しています。文字列を取得したい場合は、innerText()を使います。作成した方のサイトにも以下のように書かれています。

要は、new() でHTMLを開いて getElement(s)by*() して取り出した要素に innerText() するのが通常手順です。

hrefの後に続くURLを取得したい場合は、以下のように使う事で取得できました。

$url =  $subhtml->getElementsByTagName("a")->attributes->{href};

実行結果

以下のように、「virtualiment_new.html」の中に含まれる新着記事の情報がターミナルに表示されます。

date is 2014/01/21
url is https://www.virment.com/programming/1702/
ID is 1702
title is perlでHTMLを解析して欲しい情報を抽出するためのコードをメモ
description is HTMLを解析して特定のタグに囲まれている情報だけを抽出したり、リンクだけを抽出したりするコードを色々調べてperlで作成してみたのでメモしておきます。いわゆるスクレイピングするためのコードです。このコードでは、perlのHTML::TreeBuilderを使いました。

date is 2014/01/16
url is https://www.virment.com/mac/virtualbox/1653/
ID is 1653
title is VirtualBoxにおける仮想マシンの仮想ディスク容量を拡張するための手順
description is VirtualBoxでは、仮想マシン作成時に仮想ディスク容量を決めますが、仮想マシンを使用していると仮想ディスクの容量が不足してくることがあると思います。ここでは、このような場合に仮想マシンの仮想ディスク容量を拡張して増やすための手順をメモします。

date is 2014/01/15
url is https://www.virment.com/linux/1622/
ID is 1622
title is Linux Mint 16で日本語入力環境を整えるまでの手順
description is 最近、Linux Mintというディストリビューションに興味を持ち、言語を英語に設定してインストールしました。ただ、日本語入力ができなかったので、日本語入力環境を整える必要がありました。ここに日本語入力できるようにするまでの手順をメモしておきます。

Webからhtmlファイルを直接取得したい場合は、上記のコードの6行目を以下に置き換えます。

my $siteurl  = 'https://virment.com';
my $html = HTML::TagParser->new( $siteurl);

ただし、Webから直接取得する場合は、過度な回数で取得するとDoS攻撃になるので、気をつけて下さい。

HTML::TagParserを使ってみて

かなり楽に欲しい情報を取得できます。それに日本語のドキュメントが用意されているのも助かりました。まだこの前使ったHTML::TreeBuilderとHTML::TagParserがそれぞれ何が出来て何が出来ないのかをちゃんと確認していないので、どちらが一概に良いとは言えません。ただ、どちらも便利であることは変わりないです。本当に便利です。作成した方、ありがとうございます。今の所、私の使用範囲や目的ならばHTML::TagParserで十分なので、こちらを使っていこうと思います。

開発アプリ

nanolog.app

毎日の小さな出来事をなんでも記録して、ログとして残すためのライフログアプリです。