2008年6月10日火曜日

Python で簡単なテキスト処理 (3) - Beautiful Soup を使ってスクレイピング

1. Beautiful Soup でデータを取得

気象庁の過去のデータから、特定の値を抽出したい。

においては、ブラウザ上でコピーした文字列 (データ) を、Python のソースコードに貼り付け、必要なデータを抽出した。

今回は、Python で Web にアクセスし、必要なデータを抽出してみる。

Python では、urllib2 を利用すると、 HTTP の POST メソッドを送信することができる。

ただし、urllib2 を使うのは面倒。代わりに、

を利用する。

 

2. スクレイピングとは

スクレイピングという言葉は、Ruby の scRUBYt! で知った。

スクレイピングとは - はてなダイアリー によると、

英語で"scrape"とは「削ること」。

特に、ウェブサイトのデータを必要な部分だけ抽出して利用すること。

Python では、先ほどの Beautiful Soup が有名なようだ。

Beautiful Soup documentation によると、

Beautiful Soup is an HTML/XML parser for Python that can turn even invalid markup into a parse tree. It provides simple, idiomatic ways of navigating, searching, and modifying the parse tree. It commonly saves programmers hours or days of work.

Beautiful Soup は、HTML/XML パーサで、解析木を作ってくれる。

Ruby 版もあるけれど、hpricot を使うことが推奨されている。

Note: Rubyful Soup is no longer being maintained. I recommend you use hpricot instead.

Rubyful Soup: "The brush has got entangled in it!" より)

 

3. 試してみる

Beautiful Soup を使うために、ソースコードを保存するディレクトリと同じ場所に、BeautifulSoup.py を置いた。

課題は、

「2008 年 1 月の東京の日ごと」の気象データが掲載されているページから、特定の観測対象のデータのみを抽出したい。ただし、抽出するとき、抽出データを後で表計算に貼り付けるため、「タブ区切り」にすること。対象の項目は、「現地の気圧」と「平均気温」とする。

スクレイピングの対象である HTML の構造を調べるために、Firefox のプラグインである Firebug を使う。調べた結果、タグと CSS を使った指定により、目的の要素を取得できそうだった。

HTML の要素を辿るには、

for 変数 in BeautifulSoupオブジェクト('タグ', {'属性':'値'}):

これにより、目的の要素を取得する。

from BeautifulSoup import BeautifulSoup
import urllib2

# 対象のアドレス
URL =  "http://www.data.jma.go.jp/obd/stats/etrn/view/daily_s1.php?prec_no=44&prec_ch=%93%8C%8B%9E%93s&block_no=47662&block_ch=%93%8C%8B%9E&year=2008&month=01&day=12&view=p1"

# 出力する日付
DATE_FROM = 1
DATE_TO = 5

# Web からデータを取得する
html = urllib2.urlopen(URL).read()
soup = BeautifulSoup(html)

records = []
# class 属性が mtx である tr タグを対象に
for tr in soup('tr', {'class':'mtx'}):
    rec = []
    # id 属性が p_print の div タグを対象に
    for div in tr('div', {'id':'a_print'}):
        # a タグを対象に
        for a in div('a'):
            # 日付を取得
            rec.append(a.renderContents())
    # class 属性が data_0_0 である td タグを対象に
    for td in tr('td', {'class':'data_0_0'}):
        # 各データを取得
        data = td.renderContents().strip()
        rec.append(data)
    if rec != []: records.append(rec)


# 取得したデータを出力する
for rec in records:
    # 指定された日付の期間以外は出力しない。
    if int(rec[0]) not in range(DATE_FROM, DATE_TO + 1): continue
    for i, data in enumerate(rec):
        if i in [1,6]: print unicode(data, 'utf-8'), "\t",
    print

データを取得したいページのアドレスを設定し、取得したい日付の期間を入力すると、日ごとの気圧と気温のデータを抽出することができる。

 

参考