BeautifulSoupを使ってみたけど挫折した件

もっぱらHTMLからの情報取得はWeb::Scraperな訳ですが、GAE上でもスクレイピングできたら嬉しいのでBeautifulSoupを使ってみたのですが挫折したメモ書きです。

まずは手始めに以下のscraperでYahoo Japanのトップページから
タイトルとtopicsのURLを取得するperlスクリプトを移植してみる事にしました。

#!/usr/bin/perl

use strict;
use warnings;

use LWP::Simple;
use Web::Scraper;
use YAML;

my $content = get("http://yahoo.co.jp");
my $scraper = scraper {
	process '//title', 'title' => 'TEXT';
	process '//a[@href =~ /.+topics.+/]', 'topics[]' => '@href';
};
my $res = $scraper->scrape($content);
print Dump $res;

早速書いてみたのが以下のコードです。

#!/usr/bin/python
# -*- conding: utf-8 -*-

import re
import urllib
from BeautifulSoup import BeautifulSoup

content = urllib.urlopen('http://yahoo.co.jp')
bs      = BeautifulSoup(content.read())
title   = bs.find('title')
topics  = bs.findAll('a',  href=re.compile('.+topics.+'))
print title
for item in topics:
    print item

scraperのように複数のデータを連想配列として一括で取得は出来ないですが,コードも短く非常にすっきりとしています。このサンプルではXPathは使っていませんが、BeautifulSoupのXPath拡張も存在していて、特に不便な点は感じられません。

そこでもう少し実践的なデータを取得してみようと思いMLBのスタッツ情報をスクレイピングしようとしたのですが、ここで問題が発生しました。

url     = 'http://mlb.mlb.com/stats/historical/player_stats.jsp'
content = urllib.urlopen(url)
bs      = BeautifulSoup(content.read())
tables  = bs.findAll('table')

このコードを走らせると以下のようなエラーが発生します。
調べてみると、特定のjavascriptがhtml中に存在すると、パースに失敗してしまうようです。

Traceback (most recent call last):
  File "./bs_test3.py", line 18, in 
    bs      = BeautifulSoup(content.read())
  File "/Users/matsumototaichi/Documents/test/python_test/beautifulsoap_test/BeautifulSoup.py", line 1499, in __init__
    BeautifulStoneSoup.__init__(self, *args, **kwargs)
  File "/Users/matsumototaichi/Documents/test/python_test/beautifulsoap_test/BeautifulSoup.py", line 1230, in __init__
    self._feed(isHTML=isHTML)
  File "/Users/matsumototaichi/Documents/test/python_test/beautifulsoap_test/BeautifulSoup.py", line 1263, in _feed
    self.builder.feed(markup)
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/HTMLParser.py", line 108, in feed
    self.goahead(0)
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/HTMLParser.py", line 150, in goahead
    k = self.parse_endtag(i)
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/HTMLParser.py", line 314, in parse_endtag
    self.error("bad end tag: %r" % (rawdata[i:j],))
  File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/HTMLParser.py", line 115, in error
    raise HTMLParseError(message, self.getpos())
HTMLParser.HTMLParseError: bad end tag: u'', at line 104, column 114

もう少し調べてみると、これを回避する為にはhtml5libを使えば良いという情報があったので、実際に試してみました。以下がそのコードになります。

#!/usr/bin/python
# -*- conding: utf-8 -*-

import re
import urllib
from BeautifulSoup import BeautifulSoup
from html5lib import HTMLParser  
from html5lib import treebuilders  

url    = 'http://mlb.mlb.com/stats/historical/player_stats.jsp'
res    = urllib.urlopen(url)
parser = HTMLParser(tree=treebuilders.getTreeBuilder("beautifulsoup"))
bs     = parser.parse(res.read())
tables = bs.findAll('table')
for table in tables:
    print table

これで先ほどのコードもパースできます。import文が長くなってしまいましたが、
ソース自体は綺麗に保たれていて、万事解決かと思われましたがそうでもありませんでした。

では何が問題かというと、html5libを使った場合に解析対象のデータ(html)が
HTML5の形式に書き換えられてしまう事です。スクレイピングコードを書く時は多かれ少なかれ、HTMLソースを参照しながらの作業になります。それが書き換えられてしまうとなると、作業効率に少なくない影響が出そうで心理的に抵抗が大きい訳です。HTML5も全然理解してないですし。

html5libを使わずに先ほどのパースエラーが回避できないか調べてみましたが、見つけられなかったので、とりあえず、BeautifulSoupを使うのはペンディングする事にしました。

何かいい方法がないものかしらん。

Leave a Reply

Your email address will not be published. Required fields are marked *