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

orangain flavor

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

Glance NewsというAndroidアプリを公開しました

glance scraping

Glance Newsはサッと見るだけで世の中の流れをつかむためのニュースアプリです。 NHK NEWS WEBのトップニュース7つを表示するだけの機能を持っています。 デフォルトでは毎日4回、朝8時、昼12時、夕方18時、夜21時に更新され、通知が表示されます。

Android版をGoogle Playで無料公開しています。最近iPhoneに乗り換えて、iOS版は作りかけなのでそのうち公開するかもしれません。

なぜ今さらニュースアプリを作ったか

世の中にはニュースアプリはたくさんあります。しかしなかなか気に入るものがありませんでした。(使ってみた範囲では)どのアプリも基本的に滞在時間を長くするように作られています。無料アプリは滞在時間を長くしたほうが広告収入が増えますし、有料アプリも価格に見合う価値を提供しないといけないので豊富なコンテンツを持っています。

しかし根本的な問題として、私はニュースを見るのにそんなに時間を割きたくないのです。世の中の話題についていくための必要最低限のニュースを知ることができれば十分で、ダラダラといろんなニュースを読み続けるのは時間の無駄です。自身の専門分野であるIT系のニュースは別の方法で購読します。

また、特に無料のアプリにはタイトルで目を引くものの中身が少ない記事も目立ちます。このような記事を読むのは本当に時間の無駄なので、表示されないでほしいのです。

新聞のメリット

さて、このアプリを作るにあたって参考にしたのは新聞です。新聞は終わりゆくメディアみたいなイメージがありますが、良いところもあります。

  • 1日のニュースがまとまっているので、1日1回読めば良い
  • 編集者の手で重要度に応じて並べられている

インターネットではリアルタイムに更新されるのが当たり前でそれが正義という風潮がありますが、ニュースの内容を仕事にしているのでなければ、多少遅れて読んだところで困りません。それどころかリアルタイムで更新されないことが、読む時間を無駄にしないというメリットを生むと考えています。また編集者の手が入ることで、今知っておくべき重要なニュースだけを読めるので、時間を節約できます*1

しかし媒体が紙であることや、紙に由来するレイアウトであることには価値を見いだせないので、自分なりに新聞というメディアをスマホに置き換えてみたのがGlance Newsというアプリです。1回の通知で重要なニュース7個だけが表示されるので、短時間で世の中の流れを追いかけられます。

時間を無駄にしないために

とにかく短時間で重要なニュースだけを読めることにこだわりました。既存のアプリでよくあるのは、通知をタップしてからコンテンツの読み込みが始まって待たされるとか、気になる通知をタップしたときには別のニュースがトップに表示されていて、読みたかったニュースは見当たらないというケースです。

Glance Newsではバックグラウンドでニュースを読み込んでから通知を表示します。通知をタップしたタイミングでは通信は発生しないので待たされることはなく、通知の内容と異なるニュースが表示されることもありません。

ニュースの一覧画面には、ニュースのタイトルと画像の他にニュース本文の冒頭部分が表示されます。既存のアプリではタイトルと画像しか表示されないものが多いですが、冒頭部分が表示されることで興味がある内容かどうか一覧画面で判断できます。ニュースの一覧画面から詳細画面に遷移するときに通信が発生することもありません。

ニュースにお金をかけること

新聞を参考にしているのにNHKのニュースを表示するのは不思議に思われるかもしれません。広告収入で運営されているWebサイトのコンテンツを広告なしで読めるようにするツールを配布するのは、褒められた行為ではありません。その点NHKは受信料で運営されており、Webサイトに広告は表示されません。私も受信料を支払っているので、NHKのニュースを表示することにしました*2。目を引くだけで時間の無駄になるようなニュースがほとんど掲載されないというのも理由の1つです。

ニュースの制作にはお金がかかります。無料で享受するには対価として広告を見なくてはなりません。かと言って月数千円のサービスを契約するのも私にはオーバースペックでした。月数百円で重要なニュースだけを最高のUXで読めるサービスがあると嬉しいのですが。「スマホ時代の新常識!月々800円で世の中の流れを知ろう」みたいな新社会人や就活生向けのビジネスは儲からないでしょうか。

ちなみにこのアプリは無料ですが、あくまでツールとしての価格です。本当は有料にしたかったのですが、Google Playで有料アプリを販売するのに住所公開が必須なことと、ある日突然動かなくなるかもしれないアプリを有料で販売するのは面倒そうという理由で無料です。アプリを気に入って頂けた場合、ぜひNHKの受信料を快く支払ってください(もし支払う条件を満たしていて支払っていなかった場合)。言うまでもないかもしれませんが、私はNHKの関係者ではありません。

実装について

NHKのニュースにはAPIがありません。トップニュースのRSSは提供されていますが、URLとタイトルと概要のみで画像やニュース本文は含まれていません。仕方ないのでニュースの詳細ページのJavaScriptからDuktape Androidを使ってスクレイピングしています。Duktapeについては以下の記事に書きました。

orangain.hatenablog.com

スクレイピングはアプリ側で行っているので、Pythonで動いているわけではありません。が、最初にプロトタイプを作成するときにはPythonを使ってササッと作りました。クローリング・スクレイピングができると、このような活用方法もあります。

まとめ

Glance Newsはサッと見るだけで世の中の流れをつかむためのニュースアプリです。ご興味ありましたらぜひ試してみてください。

Get it on Google Play

*1:もちろんメディアの偏向みたいな話は当然起こり得るので、批判的な目も持ちながら読むのは大切です。

*2:まあ公共放送というのなら、APIを用意してニュースをオープンデータとして公開してほしいようにも思いますが。

Pythonクローリング&スクレイピングの増刷が決まりました

book python

おかげさまで売れ行き好調のようで、Pythonクローリング&スクレイピングの増刷が決まりました。

scraping-book.com

現在Amazonでは新品の紙の本が品切れで、在庫のステータスも表示されない状態になっていますが、2月1日以降は順次在庫が復活するとのことです。 ヨドバシ.comなど他のサイトやリアル書店には在庫がありますので、お急ぎの場合はそちらをご利用ください。電子書籍版も好評発売中です。

Code for Kobeや神戸Pythonの会などの所属コミュニティで話をする機会をいただいたときには、結構買ってくださった方がいて嬉しかったですが、著者の周辺以外でもちゃんと売れているようで本当にありがたいことです。

今後ともよろしくお願いします。

関連記事:

orangain.hatenablog.com

RequestsとBeautiful Soupでのスクレイピング時に文字化けを減らす

python scraping

多様なWebサイトからスクレイピングする際、Webサイトによっては文字化けが発生することがあります。 RequestsとBeautiful Soupを組み合わせる場合に、なるべく文字化けを減らす方法を解説します。

Beautiful Soupはパーサーを選択できますが、ここではhtml.parserに絞って解説します*1

結論

以下の2点を守ると概ね幸せです。 Content-Typeヘッダーのエンコーディングを参照するコードは下の方に掲載しています。

1. Chardetをインストールしておく。

$ pip install chardet

2. RequestsのResponseオブジェクトをrとしたとき、BeautifulSoupのコンストラクターには(r.textではなく)r.contentを渡す。

import requests
from bs4 import BeautifulSoup

r = requests.get(url)
soup = BeautifulSoup(r.content, 'html.parser')

環境

Webページのエンコーディング

Webページのエンコーディングの指定・推定方法は以下の3つがあります。どれも間違っていることがあるので、これらを組み合わせて正しそうなエンコーディングを選択します。

  1. HTTPレスポンスのContent-Typeヘッダーのcharsetで指定されたエンコーディング
    • この記事ではContent-Typeのエンコーディングと呼ぶ
    • 正しくなかったり、charsetが指定されてなかったりすることがある(特に静的ページの場合)
  2. HTMLの<meta>タグまたはXMLXML宣言で指定されたエンコーディング
  3. HTTPレスポンスのバイト列から推定されたエンコーディング
    • この記事では推定されたエンコーディングと呼ぶ
    • ある程度の長さがあれば概ね正しく推定できる
    • 上記2つに比べて処理に時間がかかる

Requestsとエンコーディング

RequestsではResponseオブジェクトをrとすると次のようになります。

以下の点は注意が必要です。

  • Requestsは<meta>タグのエンコーディングは見ない(HTTPのライブラリなので)
  • Content-Typeにtextが含まれていてcharsetがない場合、r.encoding'ISO-8859-1'となる *3*4
    • この場合、日本語のサイトではr.textが文字化けする

Beautiful Soupとエンコーディング

コンストラクターBeautifulSoup()の第1引数には、HTMLの文字列をstr型またはbytes型で指定できます。

str型のHTML文字列を渡した場合は、Beautiful Soup側ではエンコーディングに関しては特に何もしません。

bytes型のHTML文字列を渡した場合は、Beautiful Soup側でstr型にデコードされます。以下のエンコーディングによるデコードを順に試して、正しくデコードできたものが使われます*5

  1. キーワード引数from_encodingで指定したエンコーディング
  2. <meta>タグのエンコーディング
  3. 推定されたエンコーディング(Chardetがインストールされている場合)

どうしたら文字化けしないのか

ここまで見てきたように、Requestsのr.encodingr.textをそのまま使うと、文字化けしやすくなります。 基本的な戦略としては、以下の2点を守るのがオススメです。

  • Chardetをインストールしておく
  • BeautifulSoup()r.contentを渡してBeautiful Soup側でデコードする

大体OKなのでオススメなコード

記事冒頭にも書きましたが、Chardetをインストールした上で、次のようにすると大体の場合文字化けを回避できます。 シンプルなのでオススメです。

import requests
from bs4 import BeautifulSoup

r = requests.get(url)
soup = BeautifulSoup(r.content, 'html.parser')

このコードでは次の順でエンコーディングを見ます。

  1. <meta>タグのエンコーディング
  2. 推定されたエンコーディング

Content-Typeヘッダーの指定を尊重したい場合のコード

Content-Typeヘッダーの指定を尊重したい場合は、次のようにできます。

r.encoding'ISO-8859-1'の場合は無視することで、Content-Typeヘッダーにcharsetが指定されていない時に文字化けするのを回避できます。ただし、Content-Typeヘッダーが間違っていた場合には文字化けすることがあります*6

import requests
from bs4 import BeautifulSoup

r = requests.get(url)
content_type_encoding = r.encoding if r.encoding != 'ISO-8859-1' else None
soup = BeautifulSoup(r.content, 'html.parser', from_encoding=content_type_encoding)

このコードでは次の順でエンコーディングを見ます。

  1. Content-Typeのエンコーディング
  2. <meta>タグのエンコーディング
  3. 推定されたエンコーディング

まとめ

なるべく文字化けに遭遇することなくスクレイピングしたいですね。

Pythonクローリング&スクレイピングではライブラリを個別に紹介していて、組み合わせたときの話はあまり詳しく書いていませんでした。普段はBeautiful Soupよりlxml推しですが、Beautiful Soupを使う機会があったのでまとめておきました。

scraping-book.com

*1:軽く試した範囲では、lxmlはhtml.parserと同様の結果になり、html5libは推定されたエンコーディングの判別に失敗することがありました。html5libは先頭100バイトしかChardetに渡さないようです。html5libを使う場合は、「Content-Typeヘッダーの指定を尊重したい場合のコード」を使ったほうが良いかもしれません。

*2:requests/models.py参照

*3:requests/utils.py参照

*4:ISO-8859-1はRFC 2616で定められたデフォルト値です。RFC 7231ではこのデフォルト値はなくなったので、3.0に向けての議論はありますが、Kenneth Reitz氏はあまり乗り気ではなさそうです。

*5:詳しくはbs4/builder/_htmlparser.pybs4/dammit.pyを参照

*6:from_encodingに指定したエンコーディングで正しくデコードできない場合は無視されるので、文字化けしないこともあります。

2016年を振り返って

blog

2016年を振り返って

今年はやはり書籍「Pythonクローリング&スクレイピング」を出版できたのが大きかったです。お陰様で良い評価をいただけているようで、ありがたいことです。

scraping-book.com

2013年の振り返りの時点で、やりたいことの1つとして「ブログよりも長いまとまった記事を執筆する」を挙げており、なんとなく書籍の執筆とかやってみたいとは思ってました。ですが、こんなにも都合よく機会が来るとは思いもしませんでした。

コミュニティ活動や発表をしてたわけでもなく、ただブログを書いていただけで、面識のない編集者からオファーを頂けたのは、話すより書くほうが好きな自分にとっては幸せでした。2年かかりましたが、自分の実績として残るものができて嬉しいです。

今年からできた神戸Pythonの会にも参加しています。Pythonについてオフラインで話せる機会・人が増えて、毎回楽しいです。

あとは、今年からボードゲームをよくやるようになりました。カタンパンデミックから始めてどんどんハマっていきました。月に1回ほど集まって友人・知人とプレイしています。いつも気付くと夕方になっていて、世の中にある楽しいゲームの多さにただ驚くばかりです。ボードゲームの楽しさをまだうまく言語化できていませんが、結局のところ、自分なんかよりずっと賢いゲームデザイナーの手のひらの上で懸命に頭を使って踊るのが楽しいのではないかと思っています。

各方面で多くの方のお世話になりました。ありがとうございました。

2017年に向けて

長い間本を書いていて、他のことに使える時間が少なかったので、今後はいろいろ作りかけのものを完成させて公開したいです。2015年の振り返りにも同じことが書いてあり、成長していない感じが辛いですが、来年こそは進められるはずです。

書籍の執筆という大きな仕事も落ち着いたので、今後どのように生きていくかじっくりと考えたいと思います。

今後ともよろしくお願いします。

PhantomJSとか使わずに簡単なJavaScriptを処理してスクレイピング

python scraping javascript

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

JavaScriptが使われているWebページからスクレイピングする場合、PhantomJSなどのヘッドレスブラウザーを使うのが一般的です。 ただ、ちょっとしたJavaScriptを解釈できれば十分な場合、オーバーキルなこともあります。

この記事では、PhantomJSとかを使わずに簡単なJavaScriptを処理する方法を解説します。

どんな場合に役立つの?

NHKニュースのWebサイトを題材として取り上げます。

NHK NEWS WEBのRSSではニュースのタイトルとURL、概要しか提供されていません。画像のURLやニュースの本文を取得したい場合は、スクレイピングが必要です。

普通にスクレイピングしてもいいですが、ソースを見るとJavaScript__DetailProp__という変数が定義されています。この変数の値のオブジェクトをパースできればXPathCSSセレクターでパースするより楽ですし、変更に強くなりそうです。

<script type="text/javascript">
   var __DetailProp__ = {
       cate: '5',
       date: '12月15日 4時04分',
       datetime: '2016-12-15T04:04',
       //id: 'k10010807361000',
       //video: 'k10010807361_201612150440_201612150458.mp4',
       video: 'https:\/\/www3.nhk.or.jp\/news\/html\/20161215\/movie\/k10010807361_201612150440_201612150458.html',
       duration: 87,
       img: 'html\/20161215\/K10010807361_1612150440_1612150458_01_03.jpg',
       title: '\u7C73\uFF26\uFF32\uFF22 \u8FFD\u52A0\u5229\u4E0A\u3052\u6C7A\u5B9A \uFF11\u5E74\u3076\u308A',
       summary: '\u30A2\u30E1\u30EA\u30AB\u306E\u4E2D\u592E\u9280\u884C\u306B\u5F53\u305F\u308B\uFF26\uFF32\uFF22\uFF1D\u9023\u90A6\u6E96\u5099\u5236\u5EA6\u7406\u4E8B\u4F1A\u306F\u3001\uFF11\uFF14\u65E5\u307E\u3067\u958B\u3044\u305F\u91D1\u878D\u653F\u7B56\u3092\u6C7A\u3081\u308B\u4F1A\u5408\u3067\u3001\u30A2\u30E1\u30EA\u30AB\u7D4C\u6E08\u306F\u62E1\u5927\u3057\u3066\u3044...',
       more: '\uFF26\uFF32\uFF22\u306F\uFF11\uFF14\u65E5\u307E\u3067\u306E\uFF12\u65E5\u9593\u3001\u30EF\u30B7\u30F3\u30C8\u30F3\u3067\u91D1\u878D\u653F\u7B56\u3092\u6C7A\u3081\u308B\u516C\u958B\u5E02\u5834\u59D4\u54E1\u4F1A\u3092\u958B\u304D\u307E\u3057\u305F\u3002<br /><br />\u7D42\u4E86\u5F8C\u767A\u8868\u3055\u308C\u305F\u58F0\u660E\u306B\u3088\u308A\u307E\u3059\u3068\u3001\uFF26\uFF32\uFF22\u306F\u3001\u30A2\u30E1\u30EA\u30AB\u7D4C\u6E08\u306B\u3064\u3044\u3066\u3001\u96C7...',
       body: [{
           title: '\u4ECA\u5F8C\u306E\u91D1\u5229\u306E\u898B\u901A\u3057\u306F',
           img: '',
           align: '',
           text: '\uFF26\uFF32\uFF22\u306F\uFF11\uFF14\u65E5\u3001\u30A4\u30A8\u30EC\u30F3\u8B70\u9577\u306A\u3069\u91D1\u878D\u653F\u7B56\u3092\u6C7A\u3081\u308B\u4F1A\u5408\u306E\u53C2\u52A0\u8005\u304C\u4E88\u6E2C\u3057\u305F\u3001\u4ECA\u5F8C\u306E\u91D1\u5229\u306E\u898B\u901A\u3057\u3092\u516C\u8868\u3057\u307E\u3057\u305F\u3002<br /><br />\u305D\u308C\u306B\u3088\u308A\u307E\u3059\u3068\u3001\u6765\u5E74\u306E\u5229\u4E0A\u3052\u306E\u56DE\u6570\u306E\u4E2D\u5FC3\u7684\u306A\u60F3\u5B9A\u306F\uFF13\u56DE\u3068\u3001\uFF19\u6708\u306E\u6BB5\u968E\u306E\uFF12\u56DE\u3088\u308A\u3082\u5897\u3084\u3057\u3066\u3044\u3066\u3001\u30C8\u30E9\u30F3\u30D7\u6C0F\u306E\u7D4C\u6E08\u653F\u7B56\u306B...',
           textPos: ''
       },{
           title: '\u300C\u30BC\u30ED\u91D1\u5229\u653F\u7B56\u89E3\u9664\u300D\u304B\u3089\u306E\uFF11\u5E74\u306F',
           img: '',
           align: '',
           text: '\uFF26\uFF32\uFF22\u306F\u53BB\u5E74\uFF11\uFF12\u6708\u3001\u3044\u308F\u3086\u308B\u30EA\u30FC\u30DE\u30F3\u30B7\u30E7\u30C3\u30AF\u306E\u3042\u3068\u5C0E\u5165\u3057\u3066\u3044\u305F\u300C\u30BC\u30ED\u91D1\u5229\u653F\u7B56\u300D\u3092\u3001\u96C7\u7528\u306E\u6539\u5584\u306A\u3069\u3092\u7406\u7531\u306B\u89E3\u9664\u3059\u308B\u3053\u3068\u3092...',
           textPos: ''
       }]
   };
</script>

JavaScript以外の言語でこのオブジェクトをパースするのは少し面倒なので、JavaScriptエンジンを使います。

組み込みJavaScriptエンジン:Duktape

この程度のオブジェクトをパースするのであれば、ブラウザーの機能は不要でJavaScriptエンジンが動けば十分です。 幸い、いろいろな言語から使える組み込みのJavaScriptエンジンはいくつかあり、ここではDuktapeを取り上げます。

Duktape

真面目に比較検討したわけではないですが、ポータビリティの高さとフットプリントの小ささが特徴のようです。 いろいろな言語のバインディングがあります。

PythonでもPyPIduktapeで検索すると、以下のライブラリが見つかります。

ここでは一番よくメンテナンスされている感じのdukpyを使用します*1

動かしてみる

環境の準備

Python 3.5.1を使い、pipでdukpyをインストールします。バージョン0.0.6を使います。

(venv) $ pip install dukpy

ソースコード

以下のようなコードでスクレイピングできます。script要素内のJavaScriptのコードを取得し、dukpy.evaljs()で実行します。

nhknews.py

import urllib
import re

import dukpy

# Webページを取得する。
f = urllib.request.urlopen('http://www3.nhk.or.jp/news/html/20161215/k10010807361000.html')

# HTMLのボディをUnicode文字列にデコードする。
html = f.read().decode('utf-8')

# 正規表現でscript要素の中身を取得する。
# re.DOTALLで正規表現中の.が改行にもマッチするようになる。
m = re.search(r'<script type="text/javascript">(.*?)</script>', html, re.DOTALL)

# 正規表現でキャプチャしたscript要素の中身を取得する。
script = m.group(1)

# dukpyでJavaScriptを実行し、変数__DetailProp__の値を取得する。
js_obj = dukpy.evaljs([script, '__DetailProp__'])

# 取得できた値(タイトル、画像のURL、もっと読むの中身)を表示する。
print(js_obj['title'])  # タイトル
print(js_obj['img'])    # 画像のURL(/news/からの相対URL)
print(js_obj['more'])   # もっと読むの中身

実行

実行すると次のようになり、JavaScriptの変数に格納されていた値を取得できていることがわかります。

(venv) $ python nhknews.py
米FRB 追加利上げ決定 1年ぶり
html/20161215/K10010807361_1612150440_1612150458_01_03.jpg
FRBは14日までの2日間、ワシントンで金融政策を決める公開市場委員会を開きました。<br /><br />終了後発表された声明によりますと、FRBは、アメリカ経済について、雇用の伸びなどを背景に緩やかなペースで拡大しているとして、全会一致で政策金利を引き上げることを決めました。<br /><br />具体的には、0.25%から0.5%の範囲となっている今の政策金利を、0.25%引き上げて、0.5%から0.75%の範囲にします。<br /><br />FRBが利上げに踏み切るのは、去年12月にゼロ金利政策を解除して以来、1年ぶりとなります。...

解説

dukpy.evaljs()関数には、JavaScriptのコードを文字列、または文字列のリストとして渡します。文字列のリストを渡した場合は、同じコンテキストで順に実行されます*2。戻り値は、最後に評価された値です。

今回の場合、script要素の中身はvar文だけなので、そのままだと評価される値はundefinedPythonではNone)となります。このため、リストの2つ目の要素として変数名を与えることで、変数__DetailProp__を評価し、その値を得ます。

JavaScriptのオブジェクトはPythondictにいい感じに変換されます。 dukpyの詳しい使い方はドキュメントを参照してください。

まとめ

Duktapeを使うといろいろな言語から簡単にJavaScriptのコードを実行できます。 Ajaxのようなブラウザーに依存する機能は使えませんが、PlainなJavaScriptを実行できれば十分な場合には便利です。

正直JavaScriptの変数をサーバーサイドで書き出すのはバッドプラクティス*3なので、使える場面は少ないかもしれません。 しかし、こういう手段も知っておくと役に立つ日が来るかもしれません。

宣伝

Pythonクローリング&スクレイピングという本を書きました。紙版・電子書籍版共にちょうど本日発売でした。 この本の中では、オーソドックスにSeleniumとPhantomJSを使ってJavaScriptを使ったページに対応する方法を解説しています。

*1:これはProjects using Duktape (alphabetical order)からリンクされているkovidgoyal/dukpyとは別物です。kovidgoyal/dukpyはPyPIには登録されていないようです。

*2:リストの要素をセミコロン区切りで接続した文字列を実行するのと同等の結果になります。

*3:参考: HTMLのscriptタグ内に出力されるJavaScriptのエスケープ処理に起因するXSSがとても多い件について - 金利0無利息キャッシング – キャッシングできます - subtech

Pythonクローリング&スクレイピングの電子書籍版も12月16日発売です

book

Pythonクローリング&スクレイピング電子書籍版も紙版と同じく12月16日発売となりました。

書籍の詳しい中身については以下の記事もご参照ください。

orangain.hatenablog.com

書籍を書きながらOSSに貢献した話

book python

Pythonクローリング&スクレイピングでは、クローリング・スクレイピングやデータ解析のための様々なライブラリを紹介しています。 書籍でOSSのライブラリを紹介すると、そのライブラリに貢献する機会やインセンティブが生まれると考えています。

書籍で紹介すると貢献したくなる

書籍で解説するには、ドキュメントやソースコードを読んでライブラリの正確な挙動を知る必要があります。 ドキュメントが曖昧な場合、そうして得られた知識でドキュメント改善のための指摘や提案ができるようになります。

書籍で紹介するライブラリで変更の大きな新バージョンが公開される場合、Alpha版やBeta版、RC (Release Candidate) 版で検証して、書籍内のコードが正しく動くことを確認する必要があります。動かない場合は自分で直すなり、早めに直るよう報告するなりしたくなるでしょう。

ライブラリの挙動が不自然な場合、「〜については注意が必要です」のように注記するよりも、いっそ改善してしまったほうが解説がシンプルになることもあります。特に1つのライブラリだけが書籍で扱う実行環境から外れてしまうような場合は、そのライブラリに対応してもらえたら解説が楽になります*1

Pythonクローリング&スクレイピングの場合、全編でPython 3を使っていますが、執筆を始めた時点ではPython 2.7のみに対応しているライブラリもありました。Python 3に対応してもらえるようPull Requestを送ったものもあります。

貢献したライブラリなど

せっかくなので、貢献したライブラリをまとめておきます。 並び順はPythonクローリング&スクレイピング(以降で書籍内と言った場合はこの本を指します。)での登場順です。

Beautiful Soup

Beautiful Soupはスクレイピングのための有名なライブラリです。 CSSセレクターで,の挙動がおかしかったのを修正しました。

WikiExtractor

WikiExtractorはWikipediaダンプからテキストだけを抽出するコマンドです。 Python 2.xにしか対応してなかったので、Python 3にも対応させました。

python-amazon-simple-product-api

python-amazon-simple-product-apiAmazonProduct Advertising APIのラッパーです。 インストール時に依存ライブラリが正しくインストールされるようにしました。

PDFMiner.six

PDFMiner.sixはPDFからテキストやオブジェクトを抽出するライブラリです。 インストールが簡単になるようにしたり、コマンドの実行結果がおかしかった箇所を修正しました。

RoboBrowser

RoboBrowserはWebページの自動操作のためのライブラリです。 chardetというライブラリがインストールされていない環境でも文字化けが起きにくくする修正をマージしてほしかったですが、未反応のままです。

MechanicalSoup

MechanicalSoupもRoboBrowserと同様の自動操作のためのライブラリです。書籍内では名前が登場するのみです。 ドキュメントを修正しました。

BigQuery-Python

BigQuery-PythonPythonからGoogle BigQueryを扱うクライアントです。 credentials.jsonというファイルを置いた場合に、コードが簡潔になるようにしました。

Scrapy

Scrapyはクローリング・スクレイピングのためのフレームワークです。 ドキュメントのわかりにくい箇所を修正したり、Python 3に対応したVer. 1.1 RC1が出た時に問題を報告・修正したりしました。 1個目のドキュメントの目次を見やすくしたPull Requestはお気に入りです。

OpenCV

OpenCVはコンピュータービジョンのためのライブラリです。 Debian上でPython 3からOpenCVを使えるようにするpython3-opencvパッケージを実現するパッチを投稿しましたが、特に進展なしでした。

このため、Ubuntu 14.04と16.04向けにpython3-opencvパッケージを含むPPA (Personal Package Archives) を作成しました。

rq

rqはRedisをバックエンドとした軽量なメッセージキューを実現するライブラリです。 ドキュメントが古い箇所やサンプルコードがPython 3で動かない箇所などを修正しました。

ウォッチしてたけど進まなかった問題

以下は特にできることがないのでウォッチしてましたが、残念ながら特に進展のなかった問題です。

MeCabPython 3対応

MeCab公式のPythonバインディングPython 3をサポートするPull Requestがあり、ウォッチしてましたがマージされていません。書籍内では代わりにmecab-python3を紹介しています。

Ubuntu 16.04のVagrant Box

Pythonとは直接関係ないですが、Ubuntu 16.04のVagrant Box (ubuntu/xenial64) がVagrant向けに設定されてなく、まともに使えないという問題があります。 このため、書籍内で使用する環境はUbuntu 14.04のままです*2

まとめ

1つ1つは簡単なものばかりですが、少しでも世界を良くする手伝いができて良かったです。

Beta版やRC版を出しているにも関わらず正式版リリース後にバグ報告が来るという話をたまに聞きます。書籍を執筆することで、正式版リリース前に検証するインセンティブが働くのは非常に良いことだと思います。書籍を書く機会はあまりないので、もう少しカジュアルにこの効果を活用できないかと考えています(小さ目の電子書籍を書くとか?)。

また、個人で開発しているライブラリはドキュメントが不十分だったり、言語のツールチェインとの連携がスムーズでなかったり*3するので、貢献すると喜ばれるでしょう。

GitHubのおかげでOSSに貢献しやすい環境が整っているので、ぜひ気がついたことはどんどん報告・修正していきましょう。

おまけ:英語でのIssue/Pull Requestのコツ

英語のIssueやPull Requestでうまく伝わらないと感じることがあるかもしれません。 コツとしては、一定のフォーマットの小見出し*4に沿って書き、なるべく長い文章を書かないようにすることだと思います。

IssueであればEnvironmentとHow to Reproduceを示し、Expected BehaviorとActual Resultを、実際のコマンドの実行結果やスクリーンショットなどを示してわかりやすく対比させます。文章を読まなくてもわかるようにすると、英語の説明の拙さを補えます。

Pull Requestの場合はまず、変更を小さくして解決したい問題にフォーカスするのが重要です。 Issueと同様に、BeforeとAfterをわかりやすく対比させると良いでしょう。 変更の動機を説明するのが難しい場合は、まず問題を明確にするためのIssueを作成し、それをFixする形でPull Requestを作成するとシンプルに説明できるようになることもあります。

参考: 英語圏の開発者に初めてバグレポートを出す時の5つのポイント【連載:コピペで使えるIT英語tips】 - エンジニアtype

関連

*1:もちろんその実行環境が妥当な場合の話です。

*2:一部Ubuntu 16.04向けの補足もあります。

*3:Pythonであればpipでインストールできないとか。

*4:Issue TemplateやPull Request Templateがあればそれです。