簡易HTMLパーサ
RubyにHTMLパーサは数有れど、どれが標準なのか良く分からない。調べるより作ったほうが楽そうなので、ちょっと作ってみました。
# htmlparser.rb class Tag attr_reader :token, :type, :name, :attr, :text def initialize token @token = token if token.match /^<!DOCTYPE(.+)>$/m @type = "D" @text = $1 elsif token.match /^<!--(.+)-->$/m @type = "C" @text = $1 elsif token.match /^<\/([^\s]+) *.*>$/m @type = "E" @name = $1.downcase elsif token.match /^<([^\s]+) *(.*)>$/m @type = "S" @name = $1.downcase @attr = {} @order = [] $2.split.each do |attr| if attr.match /([^=]+)="?([^'"]+)"?/ key = $1 value = $2 else key = attr value = nil end @attr[key.downcase] = value @order << key end else @type = "T" @text = token end end def to_s @token end end class Parser def initialize src @src = src @pos = 0 end def next if @src[@pos, 4] == "<!--" target = "-->" adjust = 2 elsif @src[@pos, 1] == "<" target = ">" adjust = 0 else target = "<" adjust = -1 end from = @pos to = @src.index target, from unless to @pos = @src.length return end to += adjust @pos = to + 1 Tag.new @src[from..to] end end
これは、PerlのHTML::TokeParserを表面的に模倣しています。使い方は次のような感じです。
require 'net/http' require 'htmlparser.rb' url = "" # URL path = "" # PATH Net::HTTP.version_1_2 Net::HTTP.start(url, 80) do |http| p = Parser.new http.get(path).body while tag = p.next p tag end end
簡単なHTMLなら大体使えると思いますが、今思いつく範囲では、以下の点を改善したほうが良いでしょう。
- scriptタグに対応する(head内でコメントになっていない場合など)
- HTMLソースをストリームのまま解析できるようにする