Elixirでクローラー作成 クロール編

スクレイピング編に引き続き,クロール編を書いていきます.

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タグとか)取得する仕組みに拡張性をもたせる

改善編に続く…(時期未定)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする