スクレイピング編に引き続き,クロール編を書いていきます.
1.指定URLでプロセスを作成
2.指定URLをスクレイピングしてURLと画像を取得
3.取得したURLと画像はMongoDBに保存
4.取得したURLで1に戻る
大きく上の流れで実行されます.
ポイントとしては
- 同じURLへのアクセスは間隔を指定する(指定しないとダメ)
- DBはMongoDB(ライブラリにはMongoを使用しています)
- プロセスを無限に生成する(Elixirのプロセスは軽いらしいがどこまでいけるのやら…)
クロール
メインプロセスを落とさないように,サブプロセスからメインプロセスにはメッセージを送りません.
(メインプロセスは永久に待ち状態になる)
defmodule Imcrawler do @moduledoc """ Documentation for Imcrawler. """ alias Imcrawler.Scraper alias Imcrawler.DBManager def work(mainer, url) do if DBManager.check_crawl(url) do # スクレイピング {status, links, imgs} = Scraper.scraping(url) if status == :error do exit(:ok) end # URL更新 DBManager.save_link(url) # クロールワーカー生成 links |> Enum.each(fn(link) -> pid = spawn(Imcrawler, :crawl, []) send pid, {:crawl, mainer, link} end) # 画像保存 if status == :ok do DBManager.save_imgs(imgs) end # メインプロセスに応答を返すとメインプロセスが落ちる # send mainer, {:crawl, url "...Crawl OK"} IO.puts(url "...Crawl OK") else # メインプロセスに応答を返すとメインプロセスが落ちる # send mainer, {:crawl, url "...Crawl NG"} IO.puts(url "...Crawl NG") end exit(:ok) end def crawl do receive do {:crawl, mainer, url} -> work(mainer, url) end end def main(url) do pid = spawn(Imcrawler, :crawl, []) send pid, {:crawl, self(), url} receive do {:crawl, message} -> IO.puts(message) end end end
本来ならキューのようなURLを管理するものを用意して,
URL取得→キューにプッシュ→メインプロセスでキューからURLを取り出してプロセス生成→スクレピング
という感じでやりたいのですが…
プロセス管理のPoolboyと絡めて,GenServerを利用して試したのですが,プロセスのタイムアウトの関係でうまくいきませんでした.
Poolboyを絡めたのが悪かったのかどうなのか…
まとめ
改善点がいくつかあるので,直していきたいと思います.
* 深さを指定できるようにする
* キューでURLを管理する
* スクレイピングで要素(aタグとか)取得する仕組みに拡張性をもたせる
改善編に続く…(時期未定)