2009/02/14

Recent entries from same category

  1. WebService::SimpleをLuaで...
  2. ただ単に、「Lua」で「るわ」を検索したくなった。それだけ
  3. LuaでTwitterるわ!

Luaも動的にメンバが追加出来る言語ですので、データ構造を動的に作り上げる事が出来ます。今日はなんとなく、「LuaでWebScraper作ったら、どんなんになるんだろう...」と思いつきで...
用意するのは
  • LuaからXPathを操作出来るltxml
  • LuaからSocketを操作出来るLuaSocket
あくまでサンプルですので、CSSセレクタも使えなければ最新のWebScraperの様に相対/絶対URL展開や、フィルタ等はサポートしていません。
またresultも動作させていない為、結果が全て戻ります。
さらにltxmlが内部で使っているTinyXML/TinyXPathの仕様からか、XPathの途中に「//」をめり込ます事が出来ませんでした。
まずLua版WebScraperのソース。

luascraper.lua
local http = require("socket.http")
local xml = require("xml")

-- return process structure
function process(t)
  return {name="process",process={xpath=t[1], name=t[2], scraper=t[3]}}
end
-- not supported
function result(p)
  return p
end
-- return scraper structure
function scraper(self)
  self.name = "scraper"
  -- scrape method
  function self.scrape(url, ctx)
    -- create http session and parse HTML
    if ctx == nil then
      local chunk = {}
      local b, c = http.request {
        method = "GET",
        url = url,
        sink = ltn12.sink.table(chunk)
      }
      if not c == 200 then
        return nil
      end
      local html = table.concat(chunk)
      ctx = {op = self, doc = xml.parse(html)}
    end
    -- scraping...
    self.res = {}
    if not ctx.doc then
      return nil
    end
    for k,v in pairs(self) do
      if (type(v) == "table" and v.name == "process") then
        if (type(v.process.scraper) == "table") then
          for k1,v1 in pairs(ctx.doc:select(v.process.xpath)) do
            local newctx = {top=ctx.top, doc=v1}
            self.res[#(self.res)+1] = v.process.scraper.scrape(url, newctx)
          end
        else
          local node = ctx.doc:select(v.process.xpath)
          if node then
            local attr = v.process.scraper
            if attr == "TEXT" then
              self.res[v.process.name] = node[1]:text()
            elseif string.sub(attr, 1, 1) == "@" then
              attr = string.sub(attr, 2)
              self.res[v.process.name] = node[1]:attribute(attr)
            end
          end
        end
      end
    end
    return self.res
  end
  return self
end
案外ボリューム無く書けました。
このモジュールを使って私のtwitter/with_friendsの発言リストをスクレイピングするLuaスクリプトがコレ

twitter_scraper.lua
require "luascraper"

local s = scraper {
  process {'//tr[@class="hentry"]',
    'status', scraper {
      process {'//td[@class="content"]/strong/a', 'nick', 'TEXT'},
      process {'//td[contains(@class,"author")]/a/img', 'name', '@alt'},
      process {'//td[contains(@class,"author")]/a/img', 'image', '@src'},
      process {'//span[contains(@class,"entry-title")]', 'description', 'TEXT'},
      process {'//a[@rel="bookmark"]', 'url', '@href'},
    }},
  result = 'friends';
}

function dump(r)
  print('-')
  for k,v in pairs(r) do
    if type(v) == "table" then
      dump(v)
    else
      print(k,v)
    end
  end
end
r = s.scrape('http://twitter.com/mattn_jp/with_friends/')
dump(r)
そして結果がコレ
image   http://s3.amazonaws.com/twitter_production/profile_images/24072332/negipo_normal.png
name    Yoshiteru Negishi
url http://twitter.com/negipo/statuses/371889282
description fooo.nameのおかげで自分が入り忘れてるウェブサービスがわかるようになった
nick    negipo
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34076402/crestock-221345-1024x768_normal.jpg
name    ハラヘ(´・ω・`)
url http://twitter.com/mobcov/statuses/371888702
description 今見るとシローアマダはかっこ悪いけどアイナサハリンは(ry
nick    mobcov
-
image   http://s3.amazonaws.com/twitter_production/profile_images/33598712/wt_normal.png
name     暴君
url http://twitter.com/VoQn/statuses/371888452
description 授業終わった。やっとご飯食べられる
nick    VoQn
-
image   http://s3.amazonaws.com/twitter_production/profile_images/24072332/negipo_normal.png
name    Yoshiteru Negishi
url http://twitter.com/negipo/statuses/371887582
description はてなperlグループに入った
nick    negipo
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34078402/cat_888_normal.gif
name    (c)yabkoji
url http://twitter.com/yabkoji/statuses/371887022
description 激スイマー襲来中。
nick    yabkoji
-
image   http://s3.amazonaws.com/twitter_production/profile_images/22560082/nobita_normal.jpg
name    kimidora
url http://twitter.com/kimidora/statuses/371886072
description ねむいねむいねむいねむいねむいねむいねむいねむいねむいねむい。。。。。。。。。。ぐぅ *Tw*
nick    kimidora
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34253552/twfoot_normal.gif
name    highness
url http://twitter.com/smokeymonkey/statuses/371885732
description うぅ、ムラサメで部長とカブった。
nick    smokeymonkey
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34253552/twfoot_normal.gif
name    highness
url http://twitter.com/smokeymonkey/statuses/371885022
description はてなグループって排他制御あるのかしら。
nick    smokeymonkey
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34076402/crestock-221345-1024x768_normal.jpg
name    ハラヘ(´・ω・`)
url http://twitter.com/mobcov/statuses/371884382
description TextMateアップデートキターーーーー!!
nick    mobcov
-
image   http://s3.amazonaws.com/twitter_production/profile_images/32257122/basara_normal.jpg
name    japo
url http://twitter.com/japo/statuses/371884052
description ハマーン様万歳だろJK
nick    japo
-
image   http://s3.amazonaws.com/twitter_production/profile_images/19383642/ore_normal.jpg
name    Sugano Yoshihisa(E)
url http://twitter.com/koshian/statuses/371883862
description 「あのPerlモジュールなんてったっけ?」とぐぐったら、自分のはてなブックマークがひっかかって見事に発見
nick    koshian
-
image   http://s3.amazonaws.com/twitter_production/profile_images/22022432/plagger_logo_purple_normal.png
name    plagger.org
url http://twitter.com/plagger/statuses/371883772
description Safariの『Webクリップ』を試してみる。 - sta la sta
nick    plagger
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34253552/twfoot_normal.gif
name    highness
url http://twitter.com/smokeymonkey/statuses/371882682
description うわ、 GFF でペーネロペー出てるのか。超欲しい。
nick    smokeymonkey
-
image   http://s3.amazonaws.com/twitter_production/profile_images/33078022/icon_normal.gif
name    Miki@7500
url http://twitter.com/7500/statuses/371881092
description 午後の部にとりかかりまっしゅ!夕方までしゃいなら?ノシ
nick    7500
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34253552/twfoot_normal.gif
name    highness
url http://twitter.com/smokeymonkey/statuses/371879692
description 初代からν→ F91 まで + アレックスと 08 小隊とデンドロビウムは把握してる。その後はさっぱりだな。
nick    smokeymonkey
-
image   http://s3.amazonaws.com/twitter_production/profile_images/15059862/al3x_normal.jpg
name    Alex Payne
url http://twitter.com/al3x/statuses/371879502
description Oh, how I wanted to be rid of MacPorts. Curse you, Image/Rmagick.
nick    al3x
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34297902/kamon_normal.png
name    wrongbee
url http://twitter.com/wrongbee/statuses/371878872
description ペーネロペーの画像検索結果
nick    wrongbee
-
image   http://s3.amazonaws.com/twitter_production/profile_images/33078022/icon_normal.gif
name    Miki@7500
url http://twitter.com/7500/statuses/371878362
description @
nick    7500
-
image   http://s3.amazonaws.com/twitter_production/profile_images/15927972/otsune_hanaji_purple_normal.jpg
name    ??uns?o ??n??s??
url http://twitter.com/otsune/statuses/371877452
description Pukkaのすばらしい所はdel.icio.usのアカウント切り替えが一瞬でプルダウンメニューで選べる所だ。bookmarkletなんかでソーシャルブックマークしてたらログアウト・ログインの時間分だけ人生を無駄にする lang:ja
nick    otsune
-
image   http://s3.amazonaws.com/twitter_production/profile_images/34253552/twfoot_normal.gif
name    highness
url http://twitter.com/smokeymonkey/statuses/371876992
description 閃光のハサウェイはガンダムサイドストーリーでは一番好きだ。
nick    smokeymonkey
-
-
Luaでもやれない事はない!

追記
result処理、相対/絶対URL展開処理を加えて、CodeReposに入れておきました。
Posted at by | Edit