ふとHTTPステータスコードの一覧と日本語の説明のデータが欲しくなったので
ElixirのFlokiモジュールを使って WikipediaのHTTPステータスコードからデータを取得してみました。
ほしいもの
以下のような構造を持ったリストを取得したいと思います
[
%{
status_code: 509,
summary: "509 Bandwidth Limit Exceeded",
description: "帯域幅制限超過。そのサーバに設定されている帯域幅(転送量)を使い切った場合に返される。"
},
%{
status_code: 508,
summary: "508 Loop Detected",
description: "ループを検出。WebDAVの拡張ステータスコード。"
},
...
]
コード
scraping_wiki_httpstatus.exs
defmodule Parser do
def parse(nodes), do: parse(nodes, [], %{})
def parse([], result, _), do: result
def parse([head | tail], result, tmp) do
{tag, _, children_nodes} = head
text = Floki.text(children_nodes)
{result, tmp} = case tag do
"dt" ->
status_code = text |> String.replace(~r/ .*/, "") |> String.to_integer
{[tmp | result], %{summary: text, status_code: status_code}}
"dd" ->
description =
case tmp[:description] do
nil -> text
desc -> "#{desc}\n#{text}"
end
{result, tmp |> Map.put(:description, description)}
end
parse(tail, result, tmp)
end
end
%{body: html} = HTTPoison.get! "https://ja.wikipedia.org/wiki/HTTP%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%B3%E3%83%BC%E3%83%89"
result = html
|> Floki.find("dt, dd")
|> Parser.parse
IO.puts inspect result
取得対象のHtmlは以下のようになっているので
<dt>200 OK</dt>
<dd><b>OK</b>。リクエストは成功し、レスポンスとともに要求に応じた情報が返される。</dd>
<dd><a href="/wiki/%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6" title="ブラウザ">ブラウザ</a>でページが正しく表示された場合は、ほとんどがこのステータスコードを返している。</dd>
<dt>201 Created</dt>
<dd><b>作成</b>。リクエストは完了し、新たに作成されたリソースのURIが返される。</dd>
<dd>例: PUTメソッドでリソースを作成するリクエストを行ったとき、そのリクエストが完了した場合に返される。</dd>
Floki.find/2
を用いて dt
dd
タグの一覧を取得しています
body
|> Foki.find("dt, dd")
#{"dt", [], ["201 Created"]},
# {"dd", [],
# [{"b", [], ["作成"]},
# "。リクエストは完了し、新たに作成されたリソースのURIが返される。"]},
# {"dd", [],
# ["例: PUTメソッドでリソースを作成するリクエストを行ったとき、そのリクエストが完了した場合に返される。"]},
# {"dt", [], ["202 Accepted"]},
# {"dd", [],
# [{"b", [], ["受理"]},
# "。リクエストは受理されたが、処理は完了していない。"]},
# {"dd", [],
# ["例: PUTメソッドでリソースを作成するリクエストを行ったとき、サーバがリクエストを受理したものの、リソースの作成が完了していない場合に返される。",
# {"a",
# [{"href", "/wiki/%E3%83%90%E3%83%83%E3%83%81%E5%87%A6%E7%90%86"},
# {"title", "バッチ処理"}], ["バッチ処理"]}, "向け。"]},
このとき dt
とdd
は親子関係になっていません。
なので再帰の中で値を tmp
に入れておき dt
タグが来たら
tmp
の中身をresult
に追加しています。
{result, tmp} = case tag do
"dt" ->
status_code = text |> String.replace(~r/ .*/, "") |> String.to_integer
{[tmp | result], %{summary: text, status_code: status_code}}
"dd" ->
description =
case tmp[:description] do
nil -> text
desc -> "{desc}\n#{text}"
end
{result, tmp |> Map.put(:description, description)}
end
ソースコードは gist にあげています
実行可能な形式であげていますので以下のように試せます
% git clone https://gist.github.com/tamanugi/6ccececefde54226f5f0b261037dedac
% cd 6ccececefde54226f5f0b261037dedac
% mix deps.get
% mix run scraping_wiki_httpstatus.exs
[%{description: "帯域幅制限超過。そのサーバに設定されている帯域幅(転送量)を使 い切った場合に返される。", status_code: 509, summary: "509 Bandwidth Limit Exceeded"}, %{description: "ループを検出。WebDAVの拡張ステータスコード。", status_code: 508, summary: "508 Loop Detected"}, ...
以下の記事を参考にさせていただきました