理系院卒のネットワークなブログ

意外なところに「つながり」ってありますよね

Rubyでスクレイピング - 読書メーター(webページ)の情報を抽出するプログラム

 読んだ本の管理ができる「読書メーター」というサービスにおいて、それぞれの本にどれだけの読者数がいるのかを引っ張ってくるプログラムを書きました。こんな感じで出力されます。 

f:id:ytera22:20150706105034p:plain

 

Gist : 読書メーターに登録されている本の読者数を取得するスクリプト

 

プログラム作成の動機

 読書メーターは本好きのためのSNSです。「人」に重きを置いたサービスだと思っています。読んだ冊数の変化がグラフ化されていたり、友達として繋がったりできます。一方で、「本」そのものに着目した機能は少なめです。読み終わった読者の数を追うだけでも、おもしろいデータがとれるのではないかと考えました。

 そこで、1日ごとに登録読者数の変化を見てみようと思ったのですが、手動でやるのは思った以上に面倒でした。複数の本の変化を調べないと面白いデータは取れないと思ったのですが、注目する本が増えれば増えるほど手間がかかります。だから、プログラムを書いてなるべく自動化しようと試みました。

 

2015/8/22追記

 とりあえずデータをまとめてみました。よかったらこちらもどうぞ。

コーティングに関して

 まずはじめに、僕が書いたプログラムはこちらのサイトで紹介されているプログラムをベースにしています。先駆者がいたことが非常に幸運でした。ありがとうございます。

qiita.com

 

 Rubyに触れるのは初めてでした。オブジェクト指向の言語も初めて。僕は普段C言語でシミュレーションプログラムを作って研究しているので、関数型言語に慣れきっています。普段とはまったく違うコーディングは新鮮でした。

 Rubyはライブラリがいろいろと整っているのが素敵ですね。webサイトへアクセスして中身を切り取ってくることを考えると、いろいろ障害があるだろうと思ったのです。しかし、ライブラリがあれば複雑な処理も数行で実現できてしまって、感動しました。

プログラム動作

入力

 コマンドライン引数でファイルを指定します。入力ファイルの中身は数字の羅列です。読書メーターの本の個別ページのURLの一部です。" http://bookmeter.com/b/*** "の***の部分です。例えば冒頭の画像は以下のファイルを読み込んでいます。本の名前だけだと一意に特定することが難しく(Kindle版とかもありますし)、この方法がベストだと思います。

4041018889
4041018897
4041020484
4041020492
4048704699
4048708244
4048866583
4048914278
4048662260
4048691899
4167902923
4167902931
4062748681
406274869X

出力

 コンソールに出力すると同時に、取得したデータをまとめやすいようにcsvファイルにも書き出しています。「いつのデータなのか」が大事なのでcsvファイルのファイル名と出力には日時を入れています。

 毎日決められた時間にデータを計測するために、Windowsのタスクスケジューラでbatファイルを動かしています。

プログラム詳細

 

1~5行目

 漢字を使うので文字コードはたぶん大事です。僕はエディタの出力の文字コードの設定ミスもあって時間を食いました。requireでライブラリを読み込みます。mechanizeとnokogiriというライブラリがこのプログラムの肝です。ダウンロードしましょう。参考にさせて頂いたサイトで丁寧に説明されているのでそちらを参照してみてください。

 

7~27行目

 この部分はほとんど変えていなくて、非常に申し訳ないところです。僕はC言語を普段使っているので構造体を使うメリットはよくわかるのですが、この記述で何が起きているのかは現在勉強中です。大まかな動きは理解しているつもりです。とりあえず、ハッシュというものは便利ですね。はい。適当ですみません。

 

30~48行目

 入力ファイルから読み込んだ本のURLにアクセスし、読者数を取ってくる部分です。入力ファイルの段数分ループが回ります。webへのアクセスはmechanize、HTMLを加工するのはnokogiriの関数を使っています。スクレイピングと呼ばれる技術ですね。

 本の作品名はwebページのページタイトルから抽出し、読者数はHTMLの中からxpathを使って探し出しています。xpathメソッドが返すのはNodesetオブジェクトであり、今回はNodesetの1番目のものが目的とするnodeです。もっとスマートな記述で探索できそうな気がするのですが、現状これで動いているのでこのままにしてあります。xpathについても勉強中なので間違っていたら申し訳ないです。

 ページのタイトルは「【本の作品名】感想【作者・著者名】 - 読書メーター」というフォーマットになっているので、"感想"をキーワードにして文字列を分割しています。読者数に関しても、取得した文字列は「100登録」のようにスペース無しで"登録"という文字が続いているので、こちらも分割することで抽出しています。

 

51~58行目

 出力ファイルを作ります。作ったbookの構造体の要素を順に出力していきます。ここはお好みでお願いします。現在時刻もRubyは簡単に取ってこれて、しかも使いやすくて便利ですね。

 

 

 

xpathについてはこちらも参考にさせて頂きました。

(ヽ´ω`) < 3. 選択された要素から値を取り出す - (ヽ´ω`) < *****

 

 

 

そのほか、勉強っぽいこと