lxmlでスクレイピングするときのコツ

目次
PythonでスクレイピングをしようとするとBeautifulSoup4やlxmlを使った例が多くあります。今回はlxmlでスクレイピングするときのコツをいくつか紹介します。
lxmlはpip install lxml
でインストールできます。
parse()にはURLを直接渡せる
urllibなどを使ってレスポンスをロードしてからlxmlに渡している例が多くありますが、parse()にURLを渡すとそのURLにアクセスして解析してくれます。
>>> import lxml.html
>>> tree = lxml.html.parse('http://example.com/')
base_urlオプションがURLをもとにセットされるので、相対リンクを絶対リンクに書き換えるでbase_urlを明示的に渡す必要がなくなります。
XPathをコンパイルして再利用する
同じXPathで複数回検索する場合はコンパイルしておくとパフォーマンスが上がります。変数を使うこともできます。
>>> count_elements = lxml.etree.XPath('count(//*[local-name() = $name])')
>>> count_elements(tree, name='h1')
1.0
>>> count_elements(tree, name='p')
2.0
文字コードを指定する
lxmlは文字コードをある程度自動で検出してデコードしてくれますが、検出がうまくいかないときは文字コードを指定すればlxml側でデコードも含めてやってくれます。
>>> parser = lxml.html.HTMLParser(encoding='utf-8')
>>> tree = lxml.html.parse('http://example.com/', parser)
ログインが必要なページを取得する
「ブラウザのCookieをPythonから利用する」でブラウザのCookieを読み込んでログイン中のセッションをPythonで使う方法を書きました。
サンプル
上記のテクニックを使ったサンプルです。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
import lxml.html | |
URLFORMAT = 'http://www.discas.net/netdvd/cd/goodsDetail.do?titleID=%010d' | |
# XPathをコンパイルして再利用する | |
find_title = lxml.etree.XPath( | |
'string(//head/meta[@property="og:title"]/@content)') | |
is_available = lxml.etree.XPath( | |
'boolean(id("sectionGoods")//form[@name="UpdateWishListForm"])') | |
def getinfo(titleid): | |
url = URLFORMAT % titleid | |
# libxml2がWindows-31Jを認識してくれないのでCP932を渡す | |
parser = lxml.html.HTMLParser(encoding='cp932') | |
tree = lxml.html.parse(url, parser) | |
# タイトルを取得 | |
title = find_title(tree) | |
# 「単品リストに追加」フォームを探す | |
available = is_available(tree) | |
return title, available | |
if __name__ == '__main__': | |
wishlist = [683928, 1209833, 1795844] | |
for titleid in wishlist: | |
title, available = getinfo(titleid) | |
print ('x', 'o')[available], title |