orangain flavor

じっくりコトコト煮込んだみかん2。知らないことを知りたい。

UNIXという考え方を読んだ

もともとUNIXの考え方は理解しているつもりでしたが、ちゃんと言語化されているものを読んだことがなかったので読んでみました。新しい発見があった箇所や非常に頷ける箇所をピックアップします。

UNIXという考え方―その設計思想と哲学

UNIXという考え方―その設計思想と哲学

定理3:できるだけ早く試作を作成する

あらゆる試作の目的は、「第三のシステム」に早く到達することだ。

  • 第一のシステム:数人で作られ、尖っていて小さく軽い
  • 第二のシステム:委員会方式で作られ、多機能で大きくて重い
  • 第三のシステム:専門家に作られ、コンセプトは常識となり、正しく作られる

早く第三のシステムに到達するために、素早く試作して素早く失敗することが大事という話。

定理4:効率より移植性

シェルスクリプトの持つ思わぬ利点は、C言語で書かれたプログラムよりずっと移植性が高いことだ。

効率が良くても移植性のないソフトウェアの価値は、新しいハードウェアが出た途端に大暴落するので、シェルスクリプトを書くべきという話。

定理6:ソフトウェアの梃子(てこ)を有効に活用する

1時間働いたら、5時間分、100時間分、いや1000時間分の結果を生み出すようにしなさい
既存のアプリケーションをゼロから設計し直すことは模倣ではあっても創造とは言わない

独自技術症候群(Not Invented Here Syndrome)を避け、他人の成果を利用し、他人に成果を利用されるのを認めることで個人の力を増幅できるという話。

定理8:過度の対話的インタフェースを避ける

UNIXは、人間とマシンのどちらがボトルネックなのかを分かっている
最も重要なことに、拘束的ユーザーインタフェースはソフトウェアの梃子の効果を利用できない

 対話的インタフェースを使うプログラムは、大きく醜くく他のコマンドと連携しづらいものになるので、避けたほうが良いという話。

定理9:全てのプログラムをフィルタにする

入出力をハードワイアするという裏には、プログラムの使い道が完全に分かっているという思い上がりがある。
ユーザーがソフトウェアをどう使うかは、決して予測できない。設計者である自分の意図に全員が従ってくれるはずだ、などとは絶対に考えてはならない。

プログラムをフィルタとして使えるようにしておくことで、開発者の想定を超える使い方にも対応できるという話。

まとめ

全体的に頷けるところが非常に多い書籍でした。書かれた年代が古いため、事例はピンと来ないものも多いですが、それほど気にならずに読めました。

プログラミングしながら運動できるエアロバイク FitDesk X-2.0を買った

2016-01-16追記: 購入した当初は高いと思っていたサドルですが、座り方で多少改善されるようです。身長160cmの妻でも漕げています。

平日の家に帰ってご飯を食べてから寝るまでの時間はコードを書いていることが多いのですが、まったく運動しないことが気にかかっていました。 運動するためにジムに行くのは面倒だし、コードを書く時間を減らしたくはないので、運動しながらコードを書ける方法がないか考えていました。

そんなときに以下の記事を見て、運動しないとなぁと思っていると、「サイクリングデスク」という言葉が目に入りました。

「The Healthy Programmer」を読んで自宅を快適なプログラミング環境にする - 八発白中

 オフィスでは1種類以上のイスを使うこと -- 普通のオフィスチェア、バランスボール、可動式のイス、さらに言えばサイクリングデスク

サイクリングデスクってなんだ?と思ってググって先頭に出てきたもの*1は家庭用ではなかったですが、要するにエアロバイクにPCを置ける机がついていれば良いことに気付きました。

というわけでFitDesk X-2.0という製品を買いました。

以下のページのように普通のエアロバイクに机を足すのでも良いかもしれませんが、安定性やスペースを考えると、多少高くても最初から一体となっている製品を買うのが良いと判断しました。

組み立て

組み立てには大人の男性1人で2時間弱かかりました。 必要な工具は一緒に入っているので問題ありません。

組み立てマニュアルは中国の方が英語から日本語に訳したようで、若干元の英語を想像しながら読解する必要がありますが、なんとかなります。

地味に大変だったのはサドルに棒をくっつけるところです。角度が急な箇所のナットを閉める必要があり、ちょっとずつしか進まないため時間がかかりました。

サイズ的には、アメリカンサイズでかなり大きめです。私は身長170cmですが、サドルを一番下げて、机を一番手前に引いてちょうどいいぐらいです。 小柄な人は注意したほうがいいかもしれません。

また、Amazonのページには以下のように書かれていますが、この横と長さは床に接する部分のサイズです。

サイズ 横41cmx長さ71cmx高さ114cm

実際に組み立てると机の部分が本体より前にせり出すため、空間としては横53cmx長さ110cmぐらい必要です。

 サポート

いざ組み立ててしばらく漕いでみるとカタカタと異音がしました。 代理店のWebサイトからサポートに問い合わせてみると、迅速に本体を交換して頂けました。

Amazon.comでは$300以下で販売されている一方、日本では5万円を超えているのも気になっていましたが、サポートを考えると日本の代理店で買って良かったです。

感想

漕ぎながら作業できるのかは若干疑問もありましたが、慣れれば集中して作業できます。この記事も半分ぐらいは漕ぎながら書きました。 逆に疲れたことに気づかずに漕ぎ続けてしまう危険もあると思っていて、最初のうちは意識的に30分ぐらいで辞めるよう気をつけています。

何にせよ、日常生活に運動を取り入れられるのは嬉しいです。 エアロバイクは低めの負荷でスルスルと漕ぐのが良いらしいので、10段階中の下から3段目で漕いでいます。

肝心の机の使い心地は上々です。流石にエアロバイクと一体になっているだけあって安定しています。アームレストがついているので腕がつかれることもありません。机にはMacbook Pro 13 inch がちょうど良く収まります。

f:id:mi_kattun:20150222224712j:plain

ノートPCを置くとメーターは見えなくなるので全く使ってません。アームバンドも置いてあるだけです。机の下の引き出しも使ってないですが、滑りが悪く、使いやすくはなさそうです。

音はほとんど気になりませんが、床を保護するために以前Kinect用に買ったマットを敷いています。

ALINCO(アルインコ)  エクササイズ フロアマット 厚さ 9mm EXP150

ALINCO(アルインコ) エクササイズ フロアマット 厚さ 9mm EXP150

Re:VIEWとDockerとCircleCIで原稿を継続的インテグレーション

Re:VIEW で執筆する原稿を継続的インテグレーションしたかったので、以下の図のような仕組みを作りました。

f:id:mi_kattun:20150113001123p:plain

ローカル執筆環境の前提条件

  • Docker 1.3以降がインストールされている
  • Re:VIEW形式の原稿のフォルダがある

Re:VIEWやLaTeXはDockerイメージのものを使うので、インストール不要です。

私はMac OS X 10.9とBoot2docker 1.4.1で動かしましたが、WindowsLinuxでも動くのではないかと思います。

Re:VIEWをDockerで動かす

vvakameさんがDocker Hubにイメージを公開してくれていますので、これを使います。

https://registry.hub.docker.com/u/vvakame/review/

dockerコマンドさえ使える状態であれば、以下のようにしてサンプル書籍コンパイルしてbook.pdfを生成できます。

$ git clone -b docker-circleci https://github.com/orangain/review-sample-book
$ cd review-sample-book/src
$ docker run \
     --rm \
     -v $(pwd):/work \
     -v $(pwd)/.texmf-var:/root/.texmf-var \
     vvakame/review:latest /bin/sh -c "cd /work && review-pdfmaker config.yml"

初回実行時は2GB超のイメージをダウンロードするので、結構時間がかかりますが、2回目以降は数秒で終わります。

ホストのカレントディレクトリをコンテナ内の /work としてマウントしてその中で実行するので、コンテナ内であることをほとんど意識せずに使えます。

review-pdfmaker 実行の際に、コンテナ内の /root/.texmf-var フォルダにフォントのキャッシュ?(詳しくは知りません)が作られます。これをホスト側に永続化できるよう設定してやることで、2回目以降は高速にPDFを生成できます。

CircleCIでDockerを使う

CircleCI ではDockerを使えます。DockerネイティブなDrone.io のほうがやりやすいかもしれませんが、プライベートリポジトリでも1並列なら無料で使えるCircleCIを選択しました。(と思いましたが、Shippable などでも良かったかもしれません。いずれ試してみたいです。)

サンプル書籍では、以下のような circle.yml を置くことでビルドを自動化できました。

# Dockerを使う
machine:
  services:
    - docker

# Dockerイメージを毎回プルしなくても良いようにキャッシュする
# See: https://circleci.com/docs/docker#caching-docker-layers
dependencies:
  cache_directories:
    - "~/docker"
  override:
    - docker info
    - if [[ -e ~/docker/image.tar ]]; then docker load --input ~/docker/image.tar; fi
    - docker pull vvakame/review
    - mkdir -p ~/docker; docker save vvakame/review > ~/docker/image.tar

# 執筆環境で使うコマンドとは以下の2点が異なる
# 1. --rm オプションがエラーになるので使わない
#    See: https://github.com/docker/docker/issues/4897
# 2. .texmf-varフォルダをマウントしない
#    キャッシュするためにはdependenciesでキャッシュを作らないといけない
test:
  override:
    - cd src; docker run -v $(pwd):/work vvakame/review:latest /bin/sh -c "cd /work && review-pdfmaker config.yml"

# 生成したPDFを成果物として保存する
general:
  artifacts:
    - "src/*.pdf"

ジョブは以下の場所から閲覧できます。成果物は自分しか見えないようですが。

forkしたソースコードは以下の場所に置いてあります。

参考

2014年を振り返って

2014年を振り返って

2014年は対外的に評価される機会があって嬉しかったです。

MBSハッカソンは特にそうなんですが、新しい人との出会いがあり、これから楽しくなりそうです。

一方で個人で数ヶ月開発していたサービスはまだ世に出せていないので何とかしないといけません。

また、一番仲の良かった友人を亡くし、忘れられない年になりました。

 2015年に向けて

2015年に向けて面白い話を頂いていて、そのうちお知らせできるかもしれません。

最近はやりたいことの量の割に、使える時間が足りていないので、やりたいことを絞って一つ一つ実現していきたいです。

体力的にも色々ガタが来てる気がするので、健康にも気をつかって、使える時間を最大化していく必要があります。

特にPCを見続けると眼のリソースが消耗していると感じるので、ポッドキャストのように眼を使わずにできることを増やしていきたいです。

来年もよろしくお願いします。

Webサイトのクローラビリティをチェックする

これはクローラー/スクレイピング Advent Calendar 2014の18日目の記事です。

Webサイトをクローリング、スクレイピングしたいと思ったとき、はじめに何をするでしょうか?

私はとりあえずブラウザの開発者ツールでDOMを覗きますが、その後robots.txt利用規約をチェックします。

そういう作業を繰り返すうちに面倒になってきたので、URLを与えるだけで自動的にクローラビリティ(クロールしやすさ)をチェックするWebサービスをやっつけで作りました。

f:id:mi_kattun:20141219003040p:plain

Crawlability · Check the crawlability of web sites

適当にクロールしたいURLを入力してCheckボタンを押してみてください。

できること

クローラビリティとは言ってもまだコンセプトレベルで、以下のことができるだけです。

  • 指定したURLの情報を表示する
  • トップページの情報を表示する
  • robots.txtを表示する
  • よくあるXML SitemapのURLにアクセスして存在するか確かめる
  • 利用規約っぽいページにアクセスしてみる

もう少しいい感じに情報を取得できるようにしたいです。

中身

ソースコードGitHubに置いてあります。

https://github.com/capybala/crawlability

ちなみに内部では以前の記事で紹介したaiohttpを使っています。Python 3.3から使えるasyncioを使って並列にアクセスしています。

aiohttpはHTTPクライアントだけでなく、HTTPサーバーの機能も実験的ながら付いているので、Webインターフェイスはこれで作ってみました。

クローラーをデーモンとして動かす ― Scrapyd

この記事はクローラー/スクレイピング Advent Calendar 2014の12日目の記事です。

ScrapyPythonにおけるクローリング・スクレイピングフレームワークとして有名ですが、Scrapydという興味深い機能があるので今日はこれを紹介します。

Scrapydはその名の通り、Scrapyのデーモンです。サーバーにおいてサービスとして動作し、Scrapyで作ったクローラーのジョブ管理ができます。

多くのページをクロールするクローラーにおいては、1回の実行時間が1日を超えることもしばしばあるので、ジョブ管理は重要です。このように運用を考慮した機能まで備えているのがScrapyの特徴的なところです。

Scrapydの概要

Scrapydは簡単なWebインターフェイスを提供しており、主にcurlを使ってAPIを呼び出します。

http://Scrapydをインストールしたホスト:6800 にアクセスすると、以下のようなシンプルな画面が表示されます。

f:id:mi_kattun:20141212224359p:plain

詳しくは後述しますが、Scrapyプロジェクトをデプロイしてある状態で以下のコマンドを実行すると、ジョブを実行できます。

curl http://Scrapydをインストールしたホスト:6800/schedule.json -d project=プロジェクト名 -d spider=スパイダー名

「Jobs」というリンクをクリックした先で実行中のジョブの状況を見られます。ジョブごとにログやスクレイプしたアイテムを見ることもできます。

f:id:mi_kattun:20141212224405p:plain

Scrapydのメリット

通常クローラーをデーモンとして動かすときは、Cronを使うと思います。ScrapydはCronと競合するツールではなく、Cronと組み合わせて使えるツールです。Scrapydを使うことで、Cron単体で使うときに比べて以下のメリットが得られます。

  • ジョブの実行状況を一覧で把握できる。
  • 同時に実行するジョブ数を制限できる。
  • Cronで定期的にジョブを実行するだけでなく、アドホックに実行できる。
  • HTTPのAPIで外部から簡単に操作できる。

Scrapydの使い方

以下のイメージでサーバーにデプロイします。

f:id:mi_kattun:20141212225022p:plain

Scrapydのインストール(サーバー側)

普通にpipでインストールしてsupervisordなどで管理することもできますが、Ubuntu向けにaptのパッケージが提供されています。せっかくなのでこれを使って楽をしてみましょう。

$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 627220E7
$ echo 'deb http://archive.scrapy.org/ubuntu scrapy main' | sudo tee /etc/apt/sources.list.d/scrapy.list
$ sudo apt-get update
$ sudo apt-get install scrapyd

探せばDockerイメージやDockerfileを公開している人もいるので、それを使ってもよいでしょう。

※プロジェクトでScrapy以外のサードパーティライブラリ(例えばDBへのアダプタ)を使っている場合は、Scrapydと同じPython環境にインストールする必要があります。グローバル環境が汚れるのが気になる場合は、Virtualenv内にScrapydをインストールしてsupervisordなどで管理するのが良いでしょう。

初期設定(クライアント側)

Scrapyプロジェクトのフォルダにある scrapy.cfg[deploy] セクションにあるurlをScrapydのURLに書き換えます。

# Automatically created by: scrapy startproject
#
# For more information about the [deploy] section see:
# http://doc.scrapy.org/en/latest/topics/scrapyd.html

[settings]
default = helloscrapy.settings

[deploy]
# ここのurlを書き換える
url = http://192.168.33.10:6800/
project = helloscrapy

ブラウザで http://Scrapydをインストールしたホスト:6800 にアクセスして上で紹介したスクリーンショットのような画面が表示されることを確認しましょう。

デプロイ(クライアント側)

Scrapyプロジェクトのディレクトリで、以下のコマンドを実行するだけで現在のプロジェクトがegg化され、Scrapydにデプロイされます。

$ scrapy deploy
Packing version 1418386468
Deploying to project "helloscrapy" in http://192.168.33.10:6800/addversion.json
Server response (200):
{"status": "ok", "project": "helloscrapy", "version": "1418386468", "spiders": 2}

ジョブの実行(クライアント側)

以下のようにしてAPIを叩くことで、クローラーが動き始めます。

$ curl http://Scrapydをインストールしたホスト:6800/schedule.json -d project=プロジェクト名 -d spider=スパイダー名
{"status": "ok", "jobid": "722f9a7281f811e4b62808002743bc76"}

HTTPのAPIを叩くだけなので、Cronからも簡単に起動できます。Cronでありがちな環境変数の問題にハマることも少ないでしょう。また、外部のホストからも簡単にトリガーできるので、私はCronではなくJenkinsを使って定期的にクローラーを起動しています。

まとめ

いかがでしたでしょうか。若干使いにくいところもあるScrapydですが、クローラーの実行状況をまとめて管理できるので見通しが良くなります。Scrapydを使って快適なScrapy運用ライフを送りましょう!

Scrapydの機能はそんなに多くないですが、紹介しきれてはいないので詳しくはドキュメントをご覧ください。

http://scrapyd.readthedocs.org/en/latest/

今回使用したScrapyのバージョンは0.24ですが、Scrapydのドキュメントには古い記述が混ざっているようなのでお気をつけ下さい。

Pythonでクローリング・スクレイピングに使えるライブラリいろいろ

2016-12-09追記

Pythonクローリング&スクレイピング」という本を書きました!


これはクローラー/スクレイピング Advent Calendar 2014の7日目の記事です。

Pythonでクローリング・スクレイピングするにあたって、いろいろなライブラリがあるので一覧でまとめてみます。

以下の4つのカテゴリにわけて紹介します。

  • Webページを取得する
  • Webページからデータを抜き出す
  • Webページの自動操作
  • 総合的なフレームワーク

なんでこれが載ってないの?この説明はおかしい!などありましたらお気軽にお知らせください。なお、この記事はいろいろなライブラリを紹介することを目的にしているので、各ライブラリの細かい説明は他に譲ります。

サンプルで使用するPythonのバージョンは特に断りのない限り3.4で、一部3.xに対応していないライブラリは2.7を使います。

Webページを取得する

urllib.request

21.6. urllib.request — URL を開くための拡張可能なライブラリ — Python 3.4.2 ドキュメント

標準ライブラリです。Python 2.xではurllib2に相当します。

>>> from urllib.request import urlopen
>>> f = urlopen('http://qiita.com/advent-calendar/2014')
>>> f.code
200
>>> f.getheader('content-type')
'text/html; charset=utf-8'
>>> f.info().get_content_charset()
'utf-8'
>>> f.read()
b'<!DOCTYPE html><html xmlns:og="http://ogp.me/ns#"><head><meta charset="UTF-8" /><title>2014\xe5\xb9\xb4\xe3\x81\xaeAdvent Calendar\xe4\xb8\x80\xe8\xa6\xa7 - Qiita</title><meta charset="UTF-8" />...

requests

Requests: HTTP for Humans — Requests 2.5.0 documentation

HTTP for Humans、人間のためのHTTP Clientです。このサンプルではurllibとの違いがわかりにくいですが、Basic認証を使うなどHTTPヘッダーを扱うときには簡単さが際立ちます。

pip install requests
>>> import requests
>>> r = requests.get('http://qiita.com/advent-calendar/2014')
>>> r.status_code
200
>>> r.headers['content-type']
'text/html; charset=utf-8'
>>> r.encoding
'utf-8'
>>> r.text
'<!DOCTYPE html><html xmlns:og="http://ogp.me/ns#"><head><meta charset="UTF-8" /><title>2014年のAdvent Calendar一覧 - Qiita</title><meta charset="UTF-8" />...

aiohttp

KeepSafe/aiohttp

Python 3.3から使用可能なasyncioを使って非同期にページを取得できます。多数のWebサイトを高速にクロールしたいときに力を発揮するでしょう。

pip install aiohttp
>>> import asyncio
>>> import aiohttp
>>> def get_body(url):
...     response = yield from aiohttp.request('GET', url)
...     return (yield from response.text())
...
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(get_body('http://qiita.com/advent-calendar/2014'))
'<!DOCTYPE html><html xmlns:og="http://ogp.me/ns#"><head><meta charset="UTF-8" /><title>2014年のAdvent Calendar一覧 - Qiita</title><meta charset="UTF-8" />...

scrapelib

sunlightlabs/scrapelib

クロール先のサーバーに負荷をかけ過ぎないように、間隔を自動的に調整したりエラー時に自動でリトライしてくれます。内部ではrequestsを使っています。

pip install scrapelib
>>> import scrapelib
>>> s = scrapelib.Scraper(requests_per_minute=10)
>>> r = s.get('http://qiita.com/advent-calendar/2014')
>>> type(r)
<class 'requests.models.Response'>
>>> r.status_code
200
>>> while True:
...     r = s.get('http://qiita.com/advent-calendar/2014')
...     r.status_code
... 

200
200
200
...

Webページからデータを抜き出す

html.parser

20.2. html.parser— HTML および XHTML のシンプルなパーサー — Python 3.4.2 ドキュメント

標準ライブラリです。SAX形式のイベント駆動型のAPIなので、あまり複雑な処理には向いていませんが、開始タグだけが必要な時なら手軽に使えます。

Python 2.xではHTMLParserというモジュール名でした。

>>> from html.parser import HTMLParser
>>> from urllib.request import urlopen
>>> class MyHTMLParser(HTMLParser):
...     def handle_starttag(self, tag, attrs):
...         if tag == 'a':
...             print(dict(attrs).get('href'))
...
>>> f = urlopen('http://qiita.com/advent-calendar/2014')
>>> parser = MyHTMLParser()
>>> parser.feed(f.read().decode('utf-8'))
/
/login?redirect_to=%2Fadvent-calendar%2F2014
/signup?redirect_to=%2Fadvent-calendar%2F2014
/advent-calendar
/advent-calendar/2014
/advent-calendar/2014/new
/advent-calendar/2014/muda/feed
/advent-calendar/2014/muda
/advent-calendar/2014/softlayer2/feed
...

lxml

lxml - Processing XML and HTML with Python

libxml2とlibxsltのPythonバインディングです。処理の高速さと機能の豊富さでは他の追随を許しません。

pip install lxml
>>> import lxml.html
>>> root = lxml.html.parse('http://qiita.com/advent-calendar/2014').getroot()
>>> root.cssselect('title')[0]
<Element title at 0x10b391c78>
>>> root.cssselect('title')[0].text
'2014年のAdvent Calendar一覧 - Qiita'
>>> for a in root.xpath('//a'):
...     print(a.get('href'))
...
/
/login?redirect_to=%2Fadvent-calendar%2F2014
/signup?redirect_to=%2Fadvent-calendar%2F2014
/advent-calendar
/advent-calendar/2014
/advent-calendar/2014/new
/advent-calendar/2014/muda/feed
/advent-calendar/2014/muda
/advent-calendar/2014/softlayer2/feed
...

BeautifulSoup4

Beautiful Soup: We called him Tortoise because he taught us.

3まではPure Pythonのため処理の遅さが弱点でしたが、4からはパーサーを選択できるので、lxmlを使えば高速に処理できます。標準ライブラリのパーサーを使えばC拡張を使えない環境でも役立ちます。

日本語ドキュメントが存在するのも、とっつきやすいでしょう。

pip install beautifulsoup4
>>> from urllib.request import urlopen
>>> from bs4 import BeautifulSoup
>>> f = urlopen('http://qiita.com/advent-calendar/2014')
>>> soup = BeautifulSoup(f)
>>> soup.title
<title>2014年のAdvent Calendar一覧 - Qiita</title>
>>> soup.title.string
'2014年のAdvent Calendar一覧 - Qiita'
>>> for a in soup.find_all('a'):
...     print(a.get('href'))
...
/
/login?redirect_to=%2Fadvent-calendar%2F2014
/signup?redirect_to=%2Fadvent-calendar%2F2014
/advent-calendar
/advent-calendar/2014
/advent-calendar/2014/new
/advent-calendar/2014/muda/feed
/advent-calendar/2014/muda
/advent-calendar/2014/softlayer2/feed
...

pyquery

pyquery 1.2.9 : Python Package Index

jQueryライクなAPIを提供するので、jQueryに慣れている人には馴染みやすいでしょう。内部ではlxmlが使われます。

pip install pyquery
>>> from pyquery import PyQuery as pq
>>> d = pq(url='http://qiita.com/advent-calendar/2014')
>>> d('title')
[<title>]
>>> d('title').text()
'2014年のAdvent Calendar一覧 - Qiita'
>>> for a in d('a').items():
...     print(a.attr('href'))
...
/
/login?redirect_to=%2Fadvent-calendar%2F2014
/signup?redirect_to=%2Fadvent-calendar%2F2014
/advent-calendar
/advent-calendar/2014
/advent-calendar/2014/new
/advent-calendar/2014/muda/feed
/advent-calendar/2014/muda
/advent-calendar/2014/softlayer2/feed

feedparser

feedparser 5.1.3 : Python Package Index

RSSなどのフィードをパースするための定番ライブラリです。フィードの種類に依らずに同じ書き方ができるため、標準ライブラリのxml.etree.ElementTreeなんかでパースするより簡単です。

pip install feedparser
>>> import feedparser
>>> d = feedparser.parse('http://qiita.com/advent-calendar/2014/crawler/feed')
>>> d.feed.title
'クローラー/スクレイピング Advent Calendarの投稿 - Qiita'
>>> for entry in d.entries:
...     print(entry.link)
...
http://blog.takuros.net/entry/2014/12/06/235232
http://blog.takuros.net/entry/2014/12/05/061034
http://happyou-info.hatenablog.com/entry/2014/12/04/005504
http://qiita.com/nezuq/items/3cc9772118ad112c18dc
http://blog.takuros.net/entry/2014/12/02/234959

Webページの自動操作

Mechanize

mechanize

PerlのWWW:MechanizeのPython版です。ログインが必要なページのスクレイピングに向いています。最終更新は2011年で、Python 3に対応していません。以下のサンプルはPython 2.7で動かしたものです。6日目のdkfjさんの記事と同様にAmazonアソシエイトから売上を取得するサンプルです。

pip install mechanize
pip install lxml  # lxmlはサンプルで使用しているだけで必須ではありません
>>> import mechanize
>>> import lxml.html
>>> br = mechanize.Browser()
>>> br.addheaders = [('User-agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36')]
>>> br.open('https://affiliate.amazon.co.jp')
<response_seek_wrapper at 0x1020b95f0 whose wrapped object = <closeable_response at 0x1020bd878 whose fp = <socket._fileobject object at 0x100d1a350>>>
>>> print(br.title())
Amazonアソシエイト(アフィリエイト)プログラムに参加しよう!
>>> br.select_form(name='sign_in')
>>> br['username'] = 'YOUR_EMAIL'
>>> br['password'] = 'YOUR_PASSWORD'
>>> response = br.submit()
>>> print(br.title())
Amazon アソシエイト(アフィリエイト) - ホーム
>>> root = lxml.html.parse(response).getroot()
>>> print(root.cssselect('#mini-report .line-item-total .data')[0].text)
¥0

selenium

selenium 2.44.0 : Python Package Index

SeleniumPythonバインディングです。FirefoxChromeなどのブラウザを自動操作したり、PhantomJSのようなヘッドレスブラウザを使うことができます。JavaScriptを使ったページにも対応できる点が強みです。

pip install selenium
>>> from selenium import webdriver
>>> driver = webdriver.Firefox()
>>> driver.get('https://affiliate.amazon.co.jp')
>>> driver.title
'Amazonアソシエイト(アフィリエイト)プログラムに参加しよう!'
>>> driver.find_element_by_name('username').send_keys('YOUR_EMAIL')
>>> driver.find_element_by_name('password').send_keys('YOUR_PASSWORD')
>>> driver.find_element_by_name('password').submit()
>>> driver.title
'Amazon アソシエイト(アフィリエイト) - ホーム'
>>> driver.find_element_by_css_selector('#mini-report .line-item-total .data').text
'¥0'

Splinter

Splinter — Splinter 0.7.0 documentation

SeleniumFirefox, Chrome, Remote, PhantomJSの各種ドライバーに加えて、zope.testbrowserなどもラップしており目的に応じて使い分けられます。シンプルで使いやすいAPIが特徴です。

pip install splinter

デフォルトではFirefox WebDriverが使われますが、以下のサンプルではPhantomJS WebDriverを使います。*1

PhantomJSがインストールされていない場合はインストールします。Mac以外の方はググってください。。

brew install phantomjs
>>> from splinter import Browser
>>> browser = Browser('phantomjs', user_agent='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36')
>>> browser.visit('https://affiliate.amazon.co.jp')
>>> browser.title.decode('utf-8')
'Amazonアソシエイト(アフィリエイト)プログラムに参加しよう!'
>>> browser.fill('username', 'YOUR_EMAIL')
>>> browser.fill('password', 'YOUR_PASSWORD')
>>> browser.find_by_value('サインイン').click()
>>> browser.title.decode('utf-8')
'Amazon アソシエイト(アフィリエイト) - ホーム'
>>> browser.find_by_css('#mini-report .line-item-total .data').text
'¥0'

総合的なフレームワーク

Scrapy

Scrapy | A Fast and Powerful Scraping and Web Crawling Framework

Pythonが誇るスクレイピングフレームワークです。Python 3への対応作業は進められていますが、現在はPython 2.7のみ対応です。

pip install scrapy

他のライブラリと違いフレームワークなので、インタラクティブシェルを使わずにファイルを作成します。

advent_spider.py

from scrapy import Spider, Item, Field

class AdventCalendar(Item):
    title = Field()

class AdventCalendarSpider(Spider):
    name = 'advent_spider'
    start_urls = ['http://qiita.com/advent-calendar/2014']

    def parse(self, response):
        return [AdventCalendar(title=e.extract()) for e in response.css('td.adventCalendar_calendarList_calendarTitle a:nth-child(2)::text')]
$ scrapy runspider advent_spider.py
...
2014-12-07 22:47:47+0900 [advent_spider] DEBUG: Crawled (200) <GET http://qiita.com/advent-calendar/2014> (referer: None)
2014-12-07 22:47:47+0900 [advent_spider] DEBUG: Scraped from <200 http://qiita.com/advent-calendar/2014>
{'title': u'1\u5186\u306b\u3082\u306a\u3089\u306a\u3044\u7121\u99c4\u306a\u6280\u8853'}
2014-12-07 22:47:47+0900 [advent_spider] DEBUG: Scraped from <200 http://qiita.com/advent-calendar/2014>
{'title': u'1\u5206\u3067\u5b9f\u73fe\u3067\u304d\u308b\u6709\u7528\u306a\u6280\u8853'}
2014-12-07 22:47:47+0900 [advent_spider] DEBUG: Scraped from <200 http://qiita.com/advent-calendar/2014>
{'title': u'2\u679a\u76ee SoftLayer '}
2014-12-07 22:47:47+0900 [advent_spider] DEBUG: Scraped from <200 http://qiita.com/advent-calendar/2014>
{'title': u'Abby'}
2014-12-07 22:47:47+0900 [advent_spider] DEBUG: Scraped from <200 http://qiita.com/advent-calendar/2014>
{'title': u'Adobe'}
2014-12-07 22:47:47+0900 [advent_spider] DEBUG: Scraped from <200 http://qiita.com/advent-calendar/2014>
{'title': u'AngularJS'}
...

このサンプルはフレームワークとしての通常の使い方ではないので、以前書いた記事もどうぞ。

PythonとかScrapyとか使ってクローリングやスクレイピングするノウハウを公開してみる! - orangain flavor

まとめ

いかがでしたでしょうか。いろいろな選択肢を知り、適切なものを選ぶ助けになれば幸いです。

複数のライブラリを紹介した3つのカテゴリにおいて1つを選ぶとしたら、個人的には以下のものが定番かと思います。

  • Webページを取得する:requests
  • Webページからデータを抜き出す:lxml
  • Webページの自動操作:selenium

今回調べて初めて知ったライブラリもありました。他にももっと良いものがありましたらぜひ教えて下さい。

*1:PhantomJSを使うことでFirefoxを使うよりもコードが複雑になり、seleniumのサンプルよりも若干複雑に見えますが、これに他意はありません。本来であればseleniumでもヘッドレスのPhantomJSを使いたかったのですが、seleniumでUser-agentヘッダを変更するのが面倒すぎたので仕方なくFirefoxを使いました。