HTML (テキスト) からデータを抜き出すプラグイン

HTML ページ (テキスト) から正規表現もしくは xpath で指定した部分データ
を抜き出す Praggerプラグイン


EntryFullText の動作を元に作ってみたけれど、似ていない。
まぁいっか。

  • 設定はこんな感じ (テスト用につくった音泉用設定ファイルの一部)
- module: const_list
  config:
    - http://www.onsen.ag/

- module: Filter::extract_html
  config:
    feed:
      xpath: //td[@bgcolor=#000000]
    item:
      title:
        regexp: <td width="135"[^>]?>(?:(?:<.+?>)*)\s*(.+?)\s*(?:<.+?>)
      link:
        regexp: (http://[^\s]+?\.asx)
      date:
        regexp: (http://[^\s]+?\.asx)
      category:
        regexp: <img height="11" alt="(.+?)!"
      description:
        regexp: <td align="center"><img .+?alt="([^"]+)"
  • plugin/Global/extract_html.rb
## Extract html -- gtaka555
##
## Extract html plugin.
##
## - module: Filter::extract_html
##   config:
##     feed:
##       xpath: xpath string (either 'xpath' or 'regexp')
##       regexp: regexp string (either 'xpath' or 'regexp')
##    item:
##      {hash key name}:
##        xpath: xpath string (either 'xpath' or 'regexp')
##        regexp: regexp string (either 'xpath' or 'regexp')
##    after_hook: Ruby source code
##

require 'rubygems'
require 'hpricot'

class Extract
  def Extract.match(text, opt)
    return [] unless opt.key?('feed') && opt.key?('item')

    if opt['feed'].key?('xpath')
      doc = Hpricot(text)
      record = doc.search(opt['feed']['xpath']).collect {|r| r.to_s}
    elsif opt['feed'].key?('regexp')
      record = text.scan(/#{opt['feed']['regexp']}/m)
    else
      return []
    end

    item_xpath = Hash[*opt['item'].map {|k, v| [k, v] if v.key?('xpath')}.compact.flatten]
    item_regexp = Hash[*opt['item'].map {|k, v| [k, v] if v.key?('regexp')}.compact.flatten]

    data = []

    record.each {|r|
      item = {}

      if item_regexp.size > 0
        item_regexp.each_pair {|key, val| 
          item[key] = r.match(/#{val['regexp']}/m).to_a[1]
        }
      end

      if item_xpath.size > 0
        elem = Hpricot(r)

        item_xpath.each {|key, val| 
          node = elem.at(val['xpath'])
          if node.nil?
            item[key] = ''
          else
            item[key] = node.to_s
          end
        }
      end

      if opt.key?('after_hook')
        eval(opt['after_hook'])
      end

      data << item
    }

    data
  end
end

def extract_html(config, data)
  return data  unless config.key?('feed') && config.key?('item')

  data.map {|text|
    item = Extract.match(text, config)

    item.each {|i|
      %w(title date link description).each {|a|
        next unless i.key?(a)
        i.instance_eval %Q{
          @#{a} = i['#{a}']
          def #{a}
            @#{a}
          end
        }
      }
    }

    item
  }.compact.flatten
end