スクレイピング編に引き続き,クロール編を書いていきます.
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タグとか)取得する仕組みに拡張性をもたせる
改善編に続く…(時期未定)