Juliaで楽しくWebスクレイピング!

  • 13
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめに

科学計算向けスクリプト言語「Julia(JuliaLang)」の利点の一つは、
データ前処理と統計処理、そしてその結果のレポートを、Pythonと同じように一貫して行える事にあります。
そして、Juliaは多くのケースでPythonよりも早いと言われています。
しかし、Juliaの最新バージョンは0.4であり、
ライブラリが出揃っている状態ではなく、又、ライブラリの情報も知れ渡っているものではありません。
今回は、JuliaでWebスクレイピング(Webページからデータを抽出する事)に使えそうなライブラリを調べてみました。
(Beautiful SoupをPyCall.jlで使う方法も考えましたが、今回は紹介しません)

01.Webページをダウンロードする。 [Requests.jl, HttpCommon.jl]

Requests.jlの関数「get(URI, クエリ文字列)」でWebページをダウンロードします。
その際、HttpCommon.jlの関数「encodeURI(エンコード前の文字列)」でWebページのURIをエンコードしておきます。
getの返り値のメンバー変数「data」からソースコードを読み取ります。

Webページをダウンロードする.jl
Pkg.add("Requests")
Pkg.add("HttpCommon")

using Requests
using HttpCommon

request = get("http://www.nicovideo.jp/search/" * encodeURI("東方project"), query = {:hft => "0", :itc => "1", :ct => "2", :cip => "1"})
code = request.data

02.Webページのソースコードを解釈する。[Gumbo.jl]

Gumbo.jlのparsehtml(HTMLの文字列)でソースコードを解釈します。
Gumbo.jlはHTML構文解析ライブラリGumbo(Google製)のJuliaラッパーです。

ソースコードを解釈する.jl
Pkg.add("Gumbo")

using Gumbo

doc = parsehtml(code)

03.指定要素を取得する。

Gumbo.jlで要素を取得する方法は、
ジャグ配列(配列の配列)として指定する方法とイテレーターの中で指定する方法の2つがあります。
CSSセレクターやXPathのような高度な指定方法はないようです。

ジャグ配列として指定する.jl
elem = doc.root[2][7][1]
elem
  # => HTMLElement{:div}:
  #    <div class="inner"> ……
イテレーターの中で指定する.jl
for elem in preorder(doc.root)
  if isa(elem, HTMLElement)
    #タグの場合
    #(特になし)
  elseif isa(elem, HTMLText)
    #テキストの場合
    println(elem.text)
      # => キーワードで動画検索
      #    niconico ……
  end
end

※イテレーター自体の指定方法はpostorder(前から)・preorder(後ろから)・breadthfirst(幅優先)の3つがあります。

04.指定要素を判別し、値を取得する。

要素にはHTMLElement(タグ)とHTMLText(テキスト)の2種類の型があるようです。
それぞれの型で値を取得する方法は違っています。

HTMLElementから値を取得する.jl
#タグ名を取得
tag(elem)
  # => ;div

#属性一覧を取得
elem.attributes
attrs(elem)
  # => Dict{String,String} with 1 entry:
  #      "class" => "inner"

#子要素一覧を取得
elem.children
children(elem)
  # => 2-element Array{HTMLNode,1}:
  #     HTMLElement{:div}
  #     HTMLElement{:div}

#親要素一覧を取得
elem.parent
  # => HTMLElement{:section}:
  #    <section class="tagCaption"> ……

#属性を取得
getattr(elem, "class")
  # => "inner"

#属性を設定
setattr!(elem, "id", "divInner")
  # => "divInner"
HTMLTextから値を取得する.jl
txt = doc.root[2][7][1][1][1][1][1][1]

#テキストを取得
txt.text
  # => "Example text"

終わりに

PythonのBeautiful Soupのような高度なHTMLパーサーが欲しい所です。