子育て情報アプリを作れるように vol5 ~cookpadのクローラー~

今回は、
子育てと言えば、赤ちゃん!
赤ちゃんと言えば、離乳食!
ということで、
クックパッド(cook pad)のサイトから
料理の情報を取ってくるクローラーを作っていきます。

今回の目標

  1. cookpadから料理情報を取得する

1. cookpadから料理情報を取得する

前回、YouTubeクローラーを作る時に
簡単に拡張できるように設計しましたので、
今回は簡単です。

まずは、cookpad用のクラスを作成します。
cookpad-site.rb
他にも料理サイトのクローラー
作成するかもしれないなので継承元クラスも用意します。
cook-site.rb

次に、cookpadクローラーを呼び出すための処理をmain.rbに追加します。

 1 : # _*_ coding: utf-8 _*_
 2 : require 'capybara'
 3 : require 'capybara/dsl'
 4 : require 'selenium-webdriver'
 5 : 
 6 : # Site
 7 : require './main/site/site.rb'
 8 : require './main/site/base/movie-site.rb'
 9 : require './main/site/base/cook-site.rb'
10 : require './main/site/youtube-site.rb'
11 : require './main/site/cookpad-site.rb'
12 : 
13 : # Capybaraの初期設定
14 : Capybara.current_driver = :selenium
15 : Capybara.default_wait_time = 10
16 : 
17 : # ブラウザをGoogle Chromeに変更する
18 : # Capybara.register_driver :selenium do |app|
19 : #   # http://code.google.com/p/chromedriver/downloads/list
20 : #   # sudo mv ~/Downloads/chromedriver /usr/bin/
21 : #   Capybara::Selenium::Driver.new(app, :browser => :chrome)
22 : # end
23 : 
24 : # -------------------------------------------------------
25 : # クローリングするサイトを定義
26 : # -------------------------------------------------------
27 : sites = []
28 : sites << Site.new(YouTubeSite.new)
29 : sites << Site.new(CookpadSite.new)
30 : # sites << Site.new(YahooChiebukuroSite.new)
31 : # sites << Site.new(HatenaBlogSite.new)
32 : 
33 : # -------------------------------------------------------
34 : # 実行
35 : # -------------------------------------------------------
36 : sites.each do |site|
37 :   site.run
38 : end

修正はたった3行。
9行目
11行目
29行目
です。  
最後にクローラーを作っていきます。
まずは、ソースコードを。
YouTubeクローラーの時とやることは同じなのでコピペして、違うところだけ修正します。

(1) Step1 サイトを開く

visit(@base_url)

(2) Step2 検索ワードをセットする

今回はとりあえず離乳食というキーワードで検索します。
find(:xpath, '//*[@id="keyword"]').set("離乳食")

(3) Step3 検索ボタンをクリックする

find(:xpath, '//*[@id="submit_button"]').click

(4) Step4 検索結果を絞り込む

cookpadでは有料会員のみ絞り込みが行えるようになっています。
なので、今回は絞り込みは行いません。
飛ばします。

(5) Step5 検索結果をソートする

ソートも有料会員のみなので、飛ばします。

(6) Step6 URLを取得する
all(:xpath, '//div[@id="main_content"]/div[@class="recipe-preview"]/div[@class="recipe-text"]/span[1]/a').each do |movie_element|
      urls << movie_element[:href]
end
(7) Step7 動画情報を取得する

 a. 最初に
  visitメソッドを使ってurlのページを開きます。
 b. url
  事前に取得したurlをセットします。
 c. title
  @title = find(:xpath, '//*[@id="recipe-title"]/h1').text
 d. description
  @description = find(:xpath, '//*[@id="description"]/div[1]').text
 e. tags
  現在は何もセットしていません。
 f. release_date
  @release_date = find(:xpath, '//*[@id="published_date"]').text
 g. thumbnail
  @thumbnail = find(:xpath, '//*[@id="main-photo"]/img')[:src]  

基本的にXPathを指定するだけで取得できました。
今回は簡単でしたね。

 
 
次回は、Yahoo知恵袋クローラーを作っていきたいと思います。

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

子育て情報アプリを作れるように vol4 ~Youtubeのクローラー~

前回からかなり時間が経ってしまいました。
言い訳すると、
仕事が忙しかったり、
子育てでいっぱいいっぱいだったり、
お盆休みだったからです。
すみません…

今回は、Youtubeから子育て動画を取得してみたいと思います。

今回の目標

  1. 簡単に全体を設計する
  2. Youtubeから動画情報を取得する

1. 簡単に全体を設計する

今回は、YouTubeのサイトをクローリングしますが、
今後YouTubeのサイト以外もクローリングしていくことになるので、
サイトが増えても簡単に追加できる設計にしておきたいと思います。
とは言っても全体がまだ見えていないので、 雰囲気で作ります笑
作りながらメンテしていけば良いかなと思います。
 
(1) サイトを決める
まず、子育て情報どういうサイトから取得するか考えました。
パッと思いついたのが以下。
そして、具体的なサイトも考えます。

(2) 構造設計する
次に構造設計していきますが、
やりたい事は3つ。

  • 各サイトをクローリングする
  • 各サイトをまとめて実行する
  • 各サイトで共通の処理はまとめる

これらを踏まえたファイル構成はこんな感じ。
f:id:tumiki_jp:20150819004958p:plain
githubを使っている関係上、色んなファイルが表示されていますが、
構造設計で重要なのは以下のファイルです。

ファイル 説明
main.rb クローラーをまとめて実行するためのファイル
site.rb 各サイトで共通の処理をまとめたファイル
movie-site.rb 動画サイトで共通の処理をまとめたファイル
youtube-site.rb YouTube用のクローラーファイル

それぞれのファイルの関係を文章でうまく説明できないので、
ソースコードを見てもらったほうが早いと思います。

main.rb

f:id:tumiki_jp:20150819013429p:plain
main.rbはrubyで実行するファイルになります。
実行したいクローラー(YouTubeSite)のインスタンスを生成
それをSiteクラスのコンストラクタに渡す。
それをsitesにセット。これで準備完了
次に、sitesをループさせ、runメソッドを呼び出しているだけです。

site.rb

f:id:tumiki_jp:20150819014156p:plain site.rbもシンプルです。
runメソッドが呼び出されると、
initializeで受け取ったsiteのrunメソッドが呼び出されるだけです。

movie-site.rb

f:id:tumiki_jp:20150819014540p:plain movie-site.rbは継承元クラスになります。
正直、とりあえず用意してみたという感じで、
現時点ではなくても問題ないです。
動画サイトから取得できる情報を保持する変数を用意しています。

youtube-site.rb

この後説明していきます。

 
ここまででとりあえず構造設計は終わりです。
あとからいくらでも修正可能なので次にいきます!

 

2. Youtubeから動画情報を取得する

クローリングするために、ライブラリを使うことになりますが、
今回のアプリでは自由度の高いCapybaraを使っていきます。

先に作成したソースコードをご覧ください。

一つずつ見ていきます。

(1) 最初に

 ①まず、YouTubeのサイトを開いて取得できる動画情報を調べます。

  • 動画のURL
  • 動画のタイトル
  • 動画の説明
  • 動画のカテゴリ
  • 動画の公開日
  • 動画の再生回数
  • 動画のGood数
  • 動画のBad数
  • 動画のコメント
  • 動画のサムネイル画像

 ②Capybaraを使えるようにするには
  インクルードが必要です。
  include Capybara::DSL
  を継承元のmovie-site.rbに書いておきます。

 ③クローリングの処理はrunメソッドの中に
  書いていきます。

(3) Step1 サイトを開く

f:id:tumiki_jp:20150819092217p:plain  Capybara::RackTest::Browser#visitを使います。
 引数にURLを指定するとそのページを開いてくれます。

(4) Step2 検索ワードをセットする

 サイトを開いた後、検索ワードを入力します。
 まず、検索ワードをセットするテキストボックスを探します。
 これには
 Capybara::Node::Element#find
 を使います。
 そして続けてsetメソッドを呼ぶと引数の値をセットしてくれます。
 今回は「子育て 1歳」というキーワードで検索します。
 ※「子育て」だけだと関係ない動画がヒットしまくりだったため苦肉の策で年齢を追加しました。

(5) Step3 検索ボタンをクリックする

 キーワードまでセットできたので、
 次は検索ボタンをクリックします。
 クリックするには、findでボタンを探した後にclickメソッドを呼ぶだけです。
 これで、検索結果が表示されたページが表示されるようになります。

(6) Step4 検索結果を絞り込む f:id:tumiki_jp:20150819092254p:plain

 その日に公開された動画だけ表示します。
 これも簡単で、フィルタボタンをクリックして
 アップロード日の列から「今日」をクリックするだけです。
 ※アップロード日が「今日」で絞り込むと一件もヒットしない可能性があるので、現在はコメントアウトしています

(7) Step5 検索結果をソートする

 アップロード日の新しい順でソートします。
 フィルタボタンをクリックして
 並べ替えの列から「アップロード日」をクリックします。

(8) Step6 URLを取得する

f:id:tumiki_jp:20150819092327p:plain  検索結果ページには動画が20件表示されるので
 それぞれのURLを取得します。
 まずは、各動画が<li>タグで囲われているので、
 <li>エレメントを取得します。
 なお、複数のエレメントを取得するときはallメソッドを使います。
 ※findメソッド複数のエレメントが見つかるとエラーになります。  次に、取得したエレメントそれぞれに対し再度XPathでURLを取得します。

(9) Step7 動画情報を取得する

 Step6で取得したURLの配列をループさせて、動画情報を取得していきます。
 a. 最初に
  visitメソッドを使ってurlのページを開きます。
 b. url
  事前に取得したurlをセットします。
 c. title
  子育て動画の中には「猫」や「犬」の子育て動画も含まれています。
  こういった動画を除外するためにタイトルに
  「犬」「猫」が含まれている場合は飛ばします。
 d. description
  動画の中にはdescription(説明)のない動画があって、
  XPathが見つからない場合があります。(エラーになる)
  なので、descriptionを取得するXPathがあるかどうかを最初にチェックして、
  なかったら「説明なし」としてセットします。
  チェックするにはhas_selector?メソッドを使います。
 e. tags
  動画のタグ情報(カテゴリ)があれば役に立つと思って用意したのですが、
  現在は何もセットしていません。
 f. release_date
  公開日はfindメソッドで取得します。
 g. view_count
  視聴回数はfindメソッドで取得します。
 h. like_count
  Good数は動画によって表示されておらず、XPathが見つからない時があるので、
  事前チェックを行います。has_selector?メソッドを使います。
 i. dislike_count
  Bad数も同じように動画によって表示されておらず、XPathが見つからない時があるので、
  事前チェックを行います。has_selector?メソッドを使います。
 j. comment
  コメントは現在は何もセットしていません。あとで使う予定です。
 k. thumbnail
  サムネイルは現在は何もセットしていません。あとで使う予定です。  

以上でYouTubeクローラーがとりあえず完成です。

実行するとこんな感じで取得します。
f:id:tumiki_jp:20150819203239p:plain  
 
 
今回は結構苦労しました。
今までは本の通りに書いて動いてただけだったので、
自分で一から考えてクローラーを作ろうとすると
いろんな問題に直面して時間がかかってしまいました。
(特にCapybaraの使い方、XPathの書き方が分かっていませんでした。) でも、すごく勉強になりました。
次回は、cookpadのサイトのクローラーを作っていきます。

ここまでこれたのはクロ本のおかげです。ありがたや〜。

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

Rubyによるクローラー開発技法 巡回・解析機能の実装と21の運用例

子育て情報アプリを作れるように vol3 ~開発する前に~

今回は、実際にアプリを作るために必要な前準備をしていきます。
 
今回の目標

  1. gemを管理する環境を作る

1. gemを管理する環境を作る

(1) まず初めに、bundlerをインストールします。

$ gem install bundler

 
(2) 次に、前回クローンしてきたリポジトリのフォルダ(parenting)に移動して
 Gemfileを追加します。

$ cd parenting
$ bundle init
Writing new Gemfile to /Users/[username]/parenting/Gemfile

bundle initを使うとGemfileを作成してくれます。
 
(3) Gemfileを開いて使用するgemを書きます。

# Parenting information Crawler Gemfile
source "https://rubygems.org"

# nokogiri
gem "nokogiri" 

Gemfileを開くと、
あらかじめsource "https://rubygems.org"という記述があるので、
その下に書いていけばいいです。
 
今回はとりあえずnokogiriのgemを使うようにしました。
gemの記述方法はgem "nokogiri"以外にもあって、

gem "nokogiri", "1.5.6"
gem "nokogiri", ">= 1.5.6"
といったバージョンを指定する方法もあります。
詳しくはこちら
gemfile - リファレンス - - Railsドキュメント

できたら保存します。

(4) Gemfileを使ってgemをインストールする

$ bundle install

を実行するとGemfileに書かれた内容に従ってgemがインストールされます。
また、実行後にGemfile.lockというファイルが作られます。
インストールしたgemの情報が書かれてるようですが、詳しい事は分からないのでネットで調べて下さい笑

(5) リポジトリに含める。
作成した(された)ファイルを
リポジトリに反映させます。

$ git add .
$ git commit -m 'gemfileの追加'
$ git push origin master

これでGitHubに更新されています。

以上で設定は終わりです。
今回の設定によりGemの管理ができるようになりました。
このGemfileを見ればこのプロジェクトに必要なgemが一目でわかりますし、
もし新たなgemが必要になったら、
今回と同様にGemfileに追記してbundle installすれば良いだけなので。
とても便利です。

Gemfileについては色んなサイトで詳しく説明されてます。
http://touch.allabout.co.jp/gm/gc/439606/2/
http://www.rubylife.jp/rails/ini/index2.html

今回はこの辺にしておきます。