Подсчет тегов и текстовых символов в документе HTML в Ruby - PullRequest
2 голосов
/ 08 августа 2011

Мне нужно проанализировать HTML-документ, чтобы подсчитать количество символов для тегов (включая атрибуты) и текста в Ruby. Из соображений производительности я не хочу использовать DOM-парсер. Я посмотрел на парсеры Noxogiri SAX и Reader, а также на SaxMachine, но ни один из них, похоже, не дает мне способа отследить позицию парсера во входном HTML.

Кто-нибудь знает способ доступа к этой информации в Ruby? Заранее спасибо

1 Ответ

5 голосов
/ 08 августа 2011

Входная строка

html = <<-HTML
<html>

<head>
  <title>Title</title>
</head>

<body>
  Hello world!
</body>

</html>
HTML

Тупой раствор

Грубое решение, оно подсчитывает каждый алфавитный символ (т. Е. </html> рассчитывает на 4 символа).

tag_count = 0
text_count = 0

in_tag = false

html.each_char do |char|
  case char
  when '<'
    in_tag = true
  when '>'
    in_tag = false
  when /\w/
    in_tag ? tag_count += 1 : text_count += 1
  end
end

puts "Text char count: #{text_count}"
puts "Tag char count: #{tag_count}"

раствор Nokogiri SAX

Этот можно легко перевести на другой язык (например, Java).

require 'nokogiri'

class HtmlCounter < Nokogiri::XML::SAX::Document

  attr_accessor :tag_count, :text_count, :comment_count

  def initialize(filtered_tags = [])
    @filtered_tags = filtered_tags
  end

  def start_document
    @tag_count = Hash.new(0)
    @text_count = Hash.new(0)
    @comment_count = 0
    @current_tags = []
  end

  def start_element(name, attrs)
    # Keep track of the nesting
    @current_tags.push(name)

    if should_count? 
      # Count the end element as well
      count_tag(name.length * 2)
      count_tag(attrs.flatten.map(&:length).inject(0) {|sum, length| sum + length})
    end
  end

  def end_element(name)
    @current_tags.pop
  end

  def comment(string)
    count_comment(string.length) if should_count?
  end

  def characters(string)
    count_text(string.strip.length) if should_count?
  end

  def should_count?
    # Are we in a filtered tag ?
    (@current_tags & @filtered_tags).empty?
  end

  def count_text(count)
    @text_count[@current_tags.last] += count
  end

  def count_tag(count)
    @tag_count[@current_tags.last] += count
  end

  def count_comment(count)
    @comment_count[@current_tags.last] += count
  end
end

# Don't count things in title tags
counter = HtmlCounter.new(["title"])
parser = Nokogiri::HTML::SAX::Parser.new(counter)
parser.parse(html)

puts "Text char count: #{counter.text_count}"
puts "Tag char count: #{counter.tag_count}"

вывод:

Text char count: {"body"=>12}
Tag char count: {"html"=>8, "head"=>8, "body"=>8}

Надеюсь, это поможет.

...