CasperJSでWEBサイトをスクレイピングしてみる


前回の投稿(CasperJSでWEBサイトの画面キャプチャを取得してみた)の続きです。環境構築の方法(Mac)については、前回の投稿をご覧ください。

はじめに

  • とある事情で、Qiitaの Organizationページ(自社の) をWEBスクレイピングしました。
    • とある事情というのは、自社のブログ に、自社メンバーのQiita新着投稿をブログパーツ的に表示したかった。
    • Qiitaへゴリゴリ負荷をかけるものではないです^^;
  • CasperJS は CoffeeScript をそのまま解釈してくれるので、今回は CoffeeScript で書いてみます。
    • ちなみに CasperJS は仮想ブラウザ?的に動いているので、対象ページが JavaScript 等で動的に描写されるページでも、スクレイピングできるはず..です。

今回の成果物

簡単なWEBスクレイピングの例

  • まず手始めに、対象ページの a タグの href="hoge" を全て取得してみます。ソースはこんな感じ。

    scraping.coffee
    casper = require("casper").create()
    
    target_url = "http://qiita.com/organizations/yumemi"
    
    # 対象のWEBページを開く
    casper.start target_url, ->
    
      urls = []
    
      urls = @evaluate ->
        items = document.querySelectorAll("a")
        Array::map.call items, (e) ->
          e.getAttribute('href')
    
      # 出力
      @echo(urls.join("\n"))
    
    casper.run ->
      @exit()
    
  • では上記のソースを実行してみます。コマンドラインで、

    $ casperjs scraping.coffee
    
  • すると次のように、href="hoge" の中身が全て取得できたことが確認できます。

querySelector の使い方のポイント

上記の例のように、単純に a タグのものを全て取得としてしまうと、余分なものも取得されてしまうので、実際は対象ページのHTMLの構造を見て、絞り込む必要があります。

具体的にいうと、divタグのid名class名HTMLタグ名HTMLタグの属性(検索に正規表現の利用可) で絞ります。

例えばこの例だと、

elements = []

elements = @evaluate ->
  items = document.querySelectorAll(".activities article .body a[href*=\"/items/\"]")
  Array::map.call items, (e) ->
    e.getAttribute('href');
  • 対象ページのHTMLの activities class 配下の、
  • article タグ 配下の、
  • body class 配下の、
  • a タグで、href 属性の value(つまりURL)で /items/ を含むものの、
  • href 属性の value(つまりURL)を取得する

となります。

href 属性のところで *= は部分一致を意味します。もし先頭文字列の一致の場合は ^= 、後方文字列の一致の場合は $= とします。

e.getAttribute('href'); は属性の取得を意味します。もしHTMLの中身を取得する場合は e.innerHTML とします。

ただ実際には、うまく絞りきれない場合が多いので.. その場合は、いったん配列にスクレイピングの結果を格納しておいて、普通に CoffeeScript で配列操作を行います。例えば、

  • 特定のURLを含むものを、別の配列へ移し替える

    • この例の場合は URLが /piyo から始まるもの
    elements_2 = []
    
    i = 0
    while i < elements.length
      if elements[i].match(/^\/piyo/)
        elements_2.push(elements[i])
      i++
    
  • 文字列を加工する

    • この例の場合は、余分な文字列の削除
    i = 0
    while i < element.length
      entry_days[i] = entry_days[i].replace(/[\n\r]/g,"")
      entry_days[i] = entry_days[i].replace(/\ /g,"")
      entry_days[i] = entry_days[i].replace(/<strong>.+?<\/strong>/g,"")
      i++
    

おわりに

スクレイピングする際は、対象ページのHTMLソースを見て、睨めっこ&試しながらソースを書いていく流れになります^^; 結構、骨が折れる作業です..