導入編に引き続き,スクレイピング編を書いていきます.
スクレイピング
スクレイピングするのに必要なライブラリ追加をします.
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らしい書き方はまだまだピンときませんが,パターンマッチングとパイプすげぇで心がいっぱいです.
クロール編に続く…