簡易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ソースをストリームのまま解析できるようにする