PythonでWEBページをクローリングする時のTips

PythonでWEBページをクローリングする時のTipsをまとめてみました。

urllib2.urlopenのデフォルトのユーザーエージェントを変更する

PythonでURLを開くには、urllib2.urlopenします。
urllib2.urlopenは、デフォルトで"Python-urllib/(Pythonのバージョン)"というユーザーエージェントを使用しますが、Wikipediaなど一部のページではこのユーザーエージェントに対し403 Forbiddenを返してきます。以下のコードによってデフォルトのユーザーエージェントを変更すると、403エラーを回避することができます。

import urllib2
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'your user agent string')]
urllib2.install_opener(opener)

HTMLからタグを除去する

PythonでHTMLをパースするライブラリではBeautifulSoupが有名ですが、HTMLのタグを除去する程度のタスクの場合、標準ライブラリのsgmllibでも比較的簡単に達成することができます。

import sgmllib
import StringIO

class TagRemover(sgmllib.SGMLParser):
  def remove_tag(self, some_html):
    self.stringio = StringIO.StringIO()
    self.feed(some_html)
    self.close()
    result = self.stringio.getvalue()
    self.stringio.close()
    return result

  def handle_data(self, data):
    self.stringio.write(data)

def clean_html(some_html):
  return TagRemover().remove_tag(some_html)

ElementTreeモジュールでXMLをパースする

PythonではXMLをパースするための標準ライブラリが複数ありますが、Python2.5以降で使用可能なElementTreeモジュールが一番使いやすいと思います。ただ、このモジュールのXML名前空間の扱いで少しハマったので、ここにメモにしておきます。

ElementTree.ElementTreeのfindやfindallなどのメソッドではタグの名前空間を{名前空間}で指定します。RSSAtom等からデータを抜き出す際には、タグの名前空間をきちんと指定する必要があります。

ElementTreeのサンプルプログラム

import urllib2
from xml.etree import ElementTree

url = 'http://d.hatena.ne.jp/saitodevel01/rss'
etree = ElementTree.fromstring(urllib2.urlopen(url).read())
print etree.find("item")
print etree.find("{http://purl.org/rss/1.0/}item")

実行結果

None
<Element {http://purl.org/rss/1.0/}item at 10101d248>

時刻情報をパースする

はてなRSSなどでは時刻情報がISO8601というフォーマットで出てきます。このフォーマットは標準ライブラリのtimeモジュールやdatetimeモジュールではパースすることが出来ないので、iso8601というライブラリを使用します。

iso8601のインストール

sudo easy_install.py iso8601
もしくは
sudo pip install iso8601

iso8601の使い方

>>> import iso8601
>>> iso8601.parse_date("2007-01-25T12:00:00Z")
datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>)
>>>

文字列をUnicodeに変換する際のTips

WEBページでは様々な文字列エンコードが使用されていますが、内部データはUnicodeで統一し、シリアライズutf-8で行うのが定石かと思います。文字列をUnicodeへ変換する際に文字列のエンコードを指定する必要がありますが、文字列エンコードを推定するためのchardetというライブラリを使えば、unicodeへの変換を簡単に行うことができます。

chardetのインストール

sudo easy_install.py chardet
もしくは
sudo pip install chardet

chardet.detectに文字列を渡すと、encodingとconfidenceの2つのキーが入ったディクショナリが返され、encodingキーの値はそのままunicode関数の第2引数に渡すことが出来ます。また、unicode関数の第3引数を'ignore'とすると、UnicodeDecodeErrorが発生する文字列を無視してくれます。

# -*- coding: utf-8 -*-
import chardet

def unicode2(raw_string):
  encoding = chardet.detect(raw_string)['encoding']
  if encoding is not None:
    return unicode(raw_string, encoding, 'ignore')
  else:
    raise ValueError, 'Can not detect encoding'

if __name__ == '__main__':
  text = 'あいうえお'
  print repr(text)
  print repr(unicode2(text))