Elixirでクローラー作成 スクレイピング編

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

スクレイピング

スクレイピングするのに必要なライブラリ追加をします.
Erlang,ElixirにはHexというパッケージマネージャーがあります.
必要なライブラリを探すときはHexを見ると良いです.


今回必要なライブラリは以下です.
HTTPosion・・・HTTPクライアント
Floki・・・HTMLパーサー


mix.exsに参照ライブラリ,依存ライブラリを書きます.
参照ライブラリは :ライブラリ名
依存ライブラリは {:ライブラリ名, “~> バージョン”}
と書きます.

# code mix.exs
    ・
    ・
    ・
# Configuration for the OTP application
  #
  # Type "mix help compile.app" for more information
  def application do
    # Specify extra applications you'll use from Erlang/Elixir
    [extra_applications: [:logger, :httpoison, :floki]]     # 追記
  end

  # Dependencies can be Hex packages:
  #
  #   {:my_dep, "~> 0.3.0"}
  #
  # Or git/path repositories:
  #
  #   {:my_dep, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
  #
  # Type "mix help deps" for more examples and options
  defp deps do
    [{:httpoison, "~> 0.11.0"},     # 追記
     {:floki, "~> 0.12.0"}]         # 追記
  end


以下のコマンドでライブラリを取得し,ローカルにもってきます.

# mix deps.get


指定されたURLのリンクと画像を取得して返すスクレイピングのモジュールを作成します.
関数型言語は初めてで,Elixirらしい書き方が難しいです…
get_~_from_contentは外出しして,拡張性をあげたほうがそれっぽい気がします.

# code lib/imcrawler/scraping.ex
defmodule Imcrawler.Scraper do
    require HTTPoison
    require Floki

    @doc """
        指定されたURLからHTMLを取得し、その中からリンク/画像を取得する
    """
    def scraping(url) do
        # 指定URLのHTMLを取得
        {status, body} = get_html_body(url)
        if status == :ok do
            # リンク取得
            links = get_links_from_content(body)
            # 画像取得
            imgs = get_imgs_from_content(body)

            {:ok, links, imgs}
        else
            {:error, [], []}
        end
    end

    # 指定URLのHTMLを取得
    defp get_html_body(url) do
        try do
            {_, res} = HTTPoison.get(url)
            %HTTPoison.Response{status_code: status_code, body: body} = res
            if status_code == 200 do
                {:ok, body}
            else
                {:error, ""}
            end
        rescue
            what -> IO.inspect(what)
            {:error, ""}
        end
    end

    # HTMLからリンクを取得
    defp get_links_from_content(content) do
        list = content
                |> Floki.find("a")
                |> Floki.attribute("href")
                |> Enum.filter(fn(x) -> String.contains?(x, "http://") or String.contains?(x, "https://") end)
        # IO.puts(list)
        list
    end

    # HTMLから画像を取得
    defp get_imgs_from_content(content) do
        list = content
                |> Floki.find("img")
                |> Floki.attribute("src")
                |> Enum.filter(fn(link) -> String.contains?(link, "http://") or String.contains?(link, "https://") end)
                |> Enum.map(fn(link) ->
                                {status, body} = get_html_body(link)
                                {status, link, body}
                            end)
                |> Enum.filter(fn({status, _, _}) -> status == :ok end)
                |> Enum.map(fn({_, link, body}) -> {link, body} end)
        # IO.inspect(list)
        list
    end
end


imcrawler.exのmain関数からScraping.scrapingをURL指定で呼び出すようにして実行します.

# mix run -e "Imcrawler.main"
(リンクと画像のURLが出てくる)

まとめ

Elixirらしい書き方はまだまだピンときませんが,パターンマッチングとパイプすげぇで心がいっぱいです.

クロール編に続く…

シェアする

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

フォローする