Skip to content

Instantly share code, notes, and snippets.

@lululau
Created March 26, 2015 02:56
Show Gist options
  • Select an option

  • Save lululau/4ed7c56300ae26382129 to your computer and use it in GitHub Desktop.

Select an option

Save lululau/4ed7c56300ae26382129 to your computer and use it in GitHub Desktop.
A ruby class to extract h1/h2/h3/... headings from HTML
#!/usr/bin/env ruby
class HTMLHeadingParser
class TreeNode
attr_accessor :content, :children, :parent, :level
def initialize(content, parent=nil)
@content = content
@children = []
self.parent = parent
end
def parent=(p)
@parent = p
@level = (parent.level + 1) rescue 0
end
def to_s(indent=4)
" " * (indent * @level) + content + "\n" + @children.map { |c| c.to_s(indent) } * ""
end
def ancestor(deepth=-1)
ancst = self
(deepth+1).times { ancst = ancst.parent }
ancst
end
end
class HTMLHeading
attr_accessor :content, :text, :level
def initialize(content, level)
@content = content
@level = level.to_i
parse_content(content)
end
def content=(content)
@content = content
parse_content(content)
end
def parse_content(content)
regex = %r{>?([^><]*)(</)?}
@text = content.scan(regex).map(&:first).join
end
def to_str
text
end
end
def extract_headings(html)
regex = %r{<h([1-6])[^>]*>(.*?)</h\1>}
html.scan(regex).map do |(level, content)|
HTMLHeading.new(content, level)
end
end
def parse(html)
root = TreeNode.new("")
previous = root
headings = extract_headings(html)
headings.each do |heading|
deepth = previous.level - heading.level
next if deepth < -1
parent = previous.ancestor(deepth)
this_node = TreeNode.new(heading, parent)
previous.ancestor(deepth).children << this_node
previous = this_node
end
root
end
end
if __FILE__ == $0
html = File.read(ARGV[0]).gsub("\n", " ")
puts HTMLHeadingParser.new.parse(html)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment