読者です 読者をやめる 読者になる 読者になる

Pythonでブルックスコーヒーの豆価格をスクレイプする

Python初のHTMLパース・スクレイプに挑戦した結果、Beautiful Soupが鼻血が出そうなぐらい便利だったのでいきさつを書き記しておく。

発端

昨日(4/14)、研究室で先生がコーヒー豆が高くなった話をしていた。

先生「いや~ブルックスのコーヒー豆の価格が上がっちゃってさ、前なんかモカは800円ぐらいだったのに今は1250円だよ。」

ぼく「めっちゃ上がってますね。ドリップ1回あたりのお金も高くなっちゃいますね。」

うちの研究室では、使った豆の分だけお金を払えば自由にコーヒーを飲むことができる。豆を挽くミルだけでなく、ハリオ V60やサイフォンまで揃ったとてもよい環境だ。 ドリップ1回に使う豆の量はどの方法でも大体20[g]と変わらないが、豆の価格が変わるとドリップ1回で払う金額も変わってくる。

ぼく「こうもレートが乱高下すると、いくら払えばいいのかその都度計算しないとわからないですね。」

先輩「豆のレートの表示板とかあればいいんじゃね?」

ぼく「あ~それ面白そうですね!」

レートの表示板、面白そうだ。なんだか銀行の為替表示板みたい。

ハードづくりとかアイデアは広がるが、とりあえずブルックスのHPからコーヒー豆の価格をスクレイプしてみることにした。ちなみに、ブルックスはWeb APIなどは公開していなかった(当たり前)。

パース手法

Gistに今回書いたコードを上げたので、先に貼っておく。記事に載せるには地味に長いのでリンクで。

BROOKS Coffee price parser · GitHub

このコードは、ブルックスのストレートコーヒーカテゴリをスクレイプし以下のように出力する:

Kind: 豆
Name: モカ
Grams: 100 [g]
Price: 300 [Yen]

Kind: 豆
Name: モカ
Grams: 500 [g]
Price: 1250 [Yen]

Kind: 挽
Name: モカ
Grams: 100 [g]
Price: 300 [Yen]
.
.
.

コード内には3つの関数があり、そのうちget_priceは単一商品のページから価格を取り出す関数で、get_productsはカテゴリページ内の全商品の名前・種類・内容量・価格を取り出す。

ところでPythonには、標準ライブラリとしてその名もズバリhtml.parserというパーサがある。(Python 2だとHTMLParser) はじめはこれでパースを試みた。

<a>タグからリンクを取り出すなど超簡単なパースならこれで足りるが、あれこれパースするには力不足であることにすぐ気付いたので代替を探すことにした。すると、Beautiful Soupという変なパッケージを発見、最終的にパーサとしてこれを採用した。(Beautiful Soupのインストール手順などは割愛)

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

qiita.com

Beautiful Soupは非常に強力かつpythonicで、find関数を呼べば瞬時に該当するタグの塊を取り出せる。拙作でいうと11行目や23行目で実際に呼んでいる。条件の指定方法は上記のqiitaにも書いてあるが、タグ名やCSS属性、正規表現などが使える。

このBeautiful Soupに、HTTPリクエストを簡単に生成できるRequestsというパッケージを組み合わせれば、もうあっというまにパースができてしまう。例えば、TechCrunch Japanのトップページに載っている記事のタイトルを取得するには、以下のように使う:

import requests as req  #Requests
import re #正規表現ライブラリ
from bs4 import BeautifulSoup as bs  #BeautifulSoup

# HTTPリクエスト送信・レスポンスをパーサへロード
soup = bs(req.get('http://jp.techcrunch.com/'))

# aタグで、data-omni-sm属性にgbl_river_headline,(任意の文字)が設定されているものを抽出
# Python標準の正規表現ライブラリ(re)が利用できる
tc_titles = soup.find_all('a', {'data-omni-sm':re.compile('gbl_river_headline,*')})

# next_elementを使うことで、aタグで囲まれた内容を取り出すことができる
tc_titles = [x.next_element for x in tc_titles]
print(tc_titles)

上の実質3行しかないコードを実行すると、以下の出力が得られる。

['大企業は近づく「モバイルゲドン」に対応しているか?',
 '“今電話に出れるか出れないか”をブロードキャストするアプリでAppleが特許を取得',
 'GooglePlayの「家族向け」新プログラムは、子供に安心なアプリの認定を行う',
 '「利用まで数カ月待ち」定額制ファッションレンタルairClosetが資金調達',
 'アプリケーションのためのコミュニケーションAPI(電話、テキスト)を提供するTwilioからWebRTCによるビデオチャットが',
 'Intel、1Q売上横ばいも株価は2%アップ',
 'JavaScriptのためのパッケージマネージャnpmが$8Mを調達、企業向け有料サーバとプライベートモジュールをローンチ',
 'オンラインベッティングを手がけるKibow、CAVなどから1億円の資金調達——今冬にもイギリスでサービス開始',
 'Apple、今年のWWDCは6月8日~12日に開催',
 'Apple、カメラモジュールメーカーのLinXを買収、モバイルで一眼レフのような背景ぼかしを可能に',
 'Appleが全ての医療研究者にResearchKitを開放',
 'Nokia、ベル研の親会社Alcatel-Lucentと買収交渉中と認める―狙いは5Gか、規模の拡大か?',
 'イーロン・マスクのSpaceX、ロケット打ち上げは成功―Falcon 9は艀に激突、今回も再利用はならず',
 'この秋、ギター・ヒーローに新バージョン登場―大観衆相手に1人称視点でライブ演奏が楽しめる',
 'Microsoft、エンタープライズ向けコミュニケーション・ツールのSkype For Businessを正式公開',
 'iOS上にApple純正の「キッズモード」が搭載されればこんな感じかと考えてみた',
 '人工知能でサイトを分析して改善案を出す「AIアナリスト」、4月20日に公開',
 'Dockerが早くもシリーズDで$95Mを調達…エンタプライズ需要への対応に備える',
 '全地球を小型衛星で覆ってインターネット僻地を皆無にするPlanet LabsがシリーズCで$118Mの巨額を調達',
 'Appleがプロ用ムービーツールFinal Cut Pro XとMotionとCompressorをアップデート']

う〜ん、実にすばらしい!!!!

Pythonスクレイピングするときは、Beautiful Soupを使ってみてはいかがだろうか。