# Best way to parse/update HTML file?

**URL:** <https://rubytalk.org/t/best-way-to-parse-update-html-file/19173>\
**Category:** ruby-talk\
**Created:** [25 June 2005 01:15 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173 "2005-06-25T01:15:39Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bucco](https://avatars.discourse-cdn.com/v4/letter/b/e0b2c6/32.png) [@Bucco](https://rubytalk.org/u/Bucco)\
**Post date:** [25 June 2005 01:15 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/1 "2005-06-25T01:15:39Z")

</div>

Sorry for the newbie question. I am trying to find the best metod for  
parsing a HTML file and changinf one tag/item. Unfortunately, REXML  
chokes on the file because of the incomplete tags. Completing the tag  
is not an option either. What is the best way to find a specific tag  
in an html file, change it's text and attribute settings?

Thanks:)

SA

---

<div class="post-metadata">

**Author:** ![daz](https://avatars.discourse-cdn.com/v4/letter/d/87869e/32.png) [@daz](https://rubytalk.org/u/daz)\
**Post date:** [26 June 2005 09:40 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/2 "2005-06-26T09:40:39Z")

</div>

Bucco wrote:

> Sorry for the newbie question.

This has been answered once or twice before by this group 😉

> I am trying to find the best metod for parsing a HTML file  
> and changinf one tag/item. Unfortunately, REXML chokes on  
> the file because of the incomplete tags. Completing the tag  
> is not an option either. What is the best way to find a specific  
> tag in an html file, change it's text and attribute settings?
> 
> Thanks:)
> 
> SA

The best way tends to involve using a package although  
you /could/ work your way through it using regular expressions.

If you're likely to be doing this kind of thing in the  
future, you'll be glad you spent a bit of time installing;  
then it's always available.

As I recall, a different package is often  
recommended but I don't know which is best.

This is what some of us use:  
[http://ruby-htmltools.rubyforge.org/](http://ruby-htmltools.rubyforge.org/) (Ned Konz +)

Examples are included but here's another ...

> **···**
>
> #-----------------------------------------------------------------  
> EXAMPLE = \<\<EOX  
> \<html lang="en"\>  
> &nbsp;&nbsp;\<head\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\<title\>Page title\</title\>  
> &nbsp;&nbsp;\</head\>  
> &nbsp;&nbsp;\<body\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\<div id="Header"\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<h1\>\<a href="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/&quot;&gt;&lt;em&gt;q&lt;/em&gt;URL%5C.net&lt;/a&gt;&lt;/h1&gt);  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<p\>For When You Want a Quick URL\</p\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\</div\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\<hr\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\<div id="Content"\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<form action="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/&quot); method="post"\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<fieldset\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<legend\>Enter a \<abbr title="Uniform Resource Locator"\>URL\</abbr\> to make into a xxxx:\</legend\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<input id="InputURL" type="text" size="40" maxlength="65535" name="url" value=""\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<input type="submit" name="action" value="Create xxxx"\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</fieldset\>  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\</form\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\</div\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\<hr\>  
> &nbsp;&nbsp;&nbsp;&nbsp;\<a href="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/pages/contact&quot;&gt;Contact&lt;/a&gt); -  
> &nbsp;&nbsp;&nbsp;&nbsp;\<a href="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/downloads/&quot;&gt;Downloads&lt;/a&gt); -  
> &nbsp;&nbsp;&nbsp;&nbsp;\<a href="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/&quot;&gt;Create&lt;/a&gt); -  
> &nbsp;&nbsp;&nbsp;&nbsp;\<a href="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/pages/terms&quot;&gt;Terms) of Use\</a\> -  
> &nbsp;&nbsp;&nbsp;&nbsp;\<a href="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/pages/list&quot;&gt;List&lt;/a&gt); -  
> &nbsp;&nbsp;&nbsp;&nbsp;\<a href="[xxxx.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;xxxx Resources and Information.](http://xxxx.net/pages/prefs&quot;&gt;Preferences&lt;/a&gt);  
> &nbsp;&nbsp;\</body\>  
> \</html\>  
> EOX
> 
> require 'html/tree' # [http://ruby-htmltools.rubyforge.org/](http://ruby-htmltools.rubyforge.org/)
> 
> verbose = true
> 
> exa = HTMLTree::Parser.new(verbose, !false)  
> #exa.parse\_file\_named('xxxx\_net.html')  
> exa.feed(EXAMPLE) # replaces '.parse\_file\_named'
> 
> item\_a = exa.html.select {|ea| ea.tag == 'a'}  
> item\_a.each {|ea| p [:ahref, ea['href']]}  
> puts '+'\*100
> 
> exa.html.each do |ea|  
> &nbsp;&nbsp;p [ea.tag, ea['href']]  
> &nbsp;&nbsp;ea.each do |item|  
> &nbsp;&nbsp;&nbsp;&nbsp;if item.data?  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;p [:data, item.to\_s]  
> &nbsp;&nbsp;&nbsp;&nbsp;elsif item.tag == 'a'  
> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;item['href'].sub!(/xxxx/, 'mysite')  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;puts '='\*100  
> &nbsp;&nbsp;ea.dump  
> end
> 
> ### exa.html.dump
> 
> #-----------------------------------------------------------------
> 
> Output from the script above is too long to post here,  
> so I've uploaded it to:  
> [http://www.d10.karoo.net/ruby/example\_html\_parse.txt](http://www.d10.karoo.net/ruby/example_html_parse.txt)
> 
> Hope this is of some use,
> 
> daz  
> --  
> JARH (Nihon-style) [qurl.net&nbsp;-&nbsp;This website is for sale!&nbsp;-&nbsp;qurl Resources and Information.](http://qurl.net/h3)

---

<div class="post-metadata">

**Author:** ![mathew](https://avatars.discourse-cdn.com/v4/letter/m/5daacb/32.png) [@mathew](https://rubytalk.org/u/mathew)\
**Post date:** [27 June 2005 16:45 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/3 "2005-06-27T16:45:39Z")

</div>

Bucco wrote:

> Sorry for the newbie question. I am trying to find the best metod for  
> parsing a HTML file and changinf one tag/item. Unfortunately, REXML  
> chokes on the file because of the incomplete tags. Completing the tag  
> is not an option either. What is the best way to find a specific tag  
> in an html file, change it's text and attribute settings?

For invalid "tag soup" HTML, your best bet is probably to use html/htmltokenizer.

\<URL:[http://rubyforge.org/projects/htmltokenizer/&gt](http://rubyforge.org/projects/htmltokenizer/&gt);

It'll search for specified 'tags', returning the text skipped over, which you can put into a buffer. Then you can get the attributes of the 'tag', and modify them, and put the result in the buffer. Finally, you can slurp in the rest of the pseudo-HTML.

mathew

---

<div class="post-metadata">

**Author:** ![why\_the\_lucky\_stiff1](https://avatars.discourse-cdn.com/v4/letter/w/898d66/32.png) [@why\_the\_lucky\_stiff1](https://rubytalk.org/u/why_the_lucky_stiff1)\
**Post date:** [28 June 2005 20:27 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/4 "2005-06-28T20:27:47Z")

</div>

Bucco wrote:

> Sorry for the newbie question. I am trying to find the best metod for  
> parsing a HTML file and changinf one tag/item. Unfortunately, REXML  
> chokes on the file because of the incomplete tags. Completing the tag  
> is not an option either. What is the best way to find a specific tag  
> in an html file, change it's text and attribute settings?

Hi. I know I'm a bit late to the discussion, so 'sokay if you have an answer already.

A really fantastic HTML parser library is HTree by Tanaka Akira.

&nbsp;&nbsp;\<[http://cvs.m17n.org/~akr/htree/&gt](http://cvs.m17n.org/~akr/htree/&gt);

It's completely forgiving of bad HTML and you can import the document into REXML through the HTree parser.

&nbsp;&nbsp;require 'htree'  
&nbsp;&nbsp;HTree.parse( "\<b\>Bad markup" ).to\_rexml

The only downside is that you'll need to install the iconv library, which can be a bit of a pain to track down on Windows. Other than that, it's a great package.

\_why

---

<div class="post-metadata">

**Author:** ![Brad\_Wilson](https://avatars.discourse-cdn.com/v4/letter/b/71c47a/32.png) [@Brad\_Wilson](https://rubytalk.org/u/Brad_Wilson)\
**Post date:** [27 June 2005 19:35 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/5 "2005-06-27T19:35:13Z")

</div>

If you're comfortable "cleaning it up", why not tidy it to XHTML then  
use the XML parser? This is the approach I took recently when I needed  
it.

> **···**
>
> On 6/27/05, mathew \<meta@pobox.com\> wrote:
> 
> > For invalid "tag soup" HTML, your best bet is probably to use  
> > html/htmltokenizer.

---

<div class="post-metadata">

**Author:** ![daz](https://avatars.discourse-cdn.com/v4/letter/d/87869e/32.png) [@daz](https://rubytalk.org/u/daz)\
**Post date:** [29 June 2005 07:00 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/6 "2005-06-29T07:00:42Z")

</div>

\_why wrote:

> [snip]
> 
> A really fantastic HTML parser library is HTree by Tanaka Akira.

I'm glad you brought that in because I tried it last year and saw  
that it was a serious "heavy horse" and perhaps a little bit  
\_more\_ than I was looking for.  
It was adding XHTML namespace prefixes to all tags, so a  
horizontal rule, for example, became:

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\<{[XHTML namespace](http://www.w3.org/1999/xhtml)}hr\>

> It's completely forgiving of bad HTML and you can import the  
> document into REXML through the HTree parser.
> 
> &nbsp;&nbsp;require 'htree'  
> &nbsp;&nbsp;HTree.parse( "\<b\>Bad markup" ).to\_rexml

It may have been in its early stages of development, but my  
assumption that HTree would be too strict is under review 🙂  
Applying your example, I get the result I was expecting  
without all that namespace stuff.

> The only downside is that you'll need to install the iconv library,  
> which can be a bit of a pain to track down on Windows.

Instead of hunting around for that, I'd made a dummy Ruby version  
(no functionality for those who don't need any):

In my old version there are two files which require 'iconv' -  
("text.rb" and "encoder.rb") which I changed to:

&nbsp;&nbsp;begin  
&nbsp;&nbsp;&nbsp;&nbsp;require 'iconv'  
&nbsp;&nbsp;rescue LoadError  
&nbsp;&nbsp;&nbsp;&nbsp;require 'htree/iconv\_dummy'  
&nbsp;&nbsp;end

Then, add this dummy file as:  
&nbsp;&nbsp;lib\ruby\site\_ruby\1.8\htree\iconv\_dummy.rb

> **···**
>
> #-------------------------------------------------------------  
> class Iconv
> 
> &nbsp;&nbsp;## For testing : Not part of the HTree package ##  
> &nbsp;&nbsp;warn "Using dummy iconv lib: #{\_\_FILE\_\_}"  
> &nbsp;&nbsp;IC\_DUMMY = true
> 
> &nbsp;&nbsp;def Iconv.open(to, from)  
> &nbsp;&nbsp;&nbsp;&nbsp;inst = Iconv.new  
> &nbsp;&nbsp;&nbsp;&nbsp;block\_given? ? yield(inst) : inst  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;def Iconv.iconv(to, from, \*strs)  
> &nbsp;&nbsp;&nbsp;&nbsp;strs.join  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;def Iconv.conv(to, from, str)  
> &nbsp;&nbsp;&nbsp;&nbsp;str  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;def Iconv.list  
> &nbsp;&nbsp;&nbsp;&nbsp;raise 'No Iconv.list'  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;def initialize(to, from)  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;def close  
> &nbsp;&nbsp;&nbsp;&nbsp;''  
> &nbsp;&nbsp;end  
> &nbsp;&nbsp;def iconv(str, strt = 0, len = -1)  
> &nbsp;&nbsp;&nbsp;&nbsp;(len and !( len \< 0 )) or len = str.size - strt  
> &nbsp;&nbsp;&nbsp;&nbsp;str[strt, len]  
> &nbsp;&nbsp;end
> 
> &nbsp;&nbsp;module Failure  
> &nbsp;&nbsp;&nbsp;&nbsp;def initialize(\*args) # 3  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;def success  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;def failed  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;&nbsp;&nbsp;def inspect  
> &nbsp;&nbsp;&nbsp;&nbsp;end  
> &nbsp;&nbsp;end
> 
> # class InvalidEncoding \< ArgumentError; end  
> # class IllegalSequence \< ArgumentError; end  
> # class InvalidCharacter \< ArgumentError; end  
> # class OutOfRange \< RuntimeError; end
> 
> &nbsp;&nbsp;def Iconv.charset\_map  
> &nbsp;&nbsp;&nbsp;&nbsp;raise 'No Iconv.charset\_map'  
> &nbsp;&nbsp;end  
> end  
> #-------------------------------------------------------------
> 
> > \_why
> 
> daz

---

<div class="post-metadata">

**Author:** ![Bill\_Guindon1](https://avatars.discourse-cdn.com/v4/letter/b/4da419/32.png) [@Bill\_Guindon1](https://rubytalk.org/u/Bill_Guindon1)\
**Post date:** [9 July 2005 13:17 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/7 "2005-07-09T13:17:27Z")

</div>

> Bucco wrote:
> 
> \>Sorry for the newbie question. I am trying to find the best metod for  
> \>parsing a HTML file and changinf one tag/item. Unfortunately, REXML  
> \>chokes on the file because of the incomplete tags. Completing the tag  
> \>is not an option either. What is the best way to find a specific tag  
> \>in an html file, change it's text and attribute settings?  
> \>  
> Hi. I know I'm a bit late to the discussion, so 'sokay if you have an  
> answer already.
> 
> A really fantastic HTML parser library is HTree by Tanaka Akira.
> 
> &nbsp;&nbsp;\<[http://cvs.m17n.org/~akr/htree/&gt](http://cvs.m17n.org/~akr/htree/&gt);
> 
> It's completely forgiving of bad HTML and you can import the document  
> into REXML through the HTree parser.
> 
> &nbsp;&nbsp;require 'htree'  
> &nbsp;&nbsp;HTree.parse( "\<b\>Bad markup" ).to\_rexml
> 
> The only downside is that you'll need to install the iconv library,  
> which can be a bit of a pain to track down on Windows. Other than that,  
> it's a great package.

There's a page on the Rails site that covers the iconv installation on Windows:  
[http://wiki.rubyonrails.com/rails/show/iconv](http://wiki.rubyonrails.com/rails/show/iconv)

Once I had the iconv.so in a library path, and iconv.dll in  
windows\system32, I ran the test-all.rb. Got an error due to a lack  
of /dev/null, but that was fixed by creating a dev directory, and  
adding an empty 'null' file to it.

Should swap that out to have it point to a temp dir, but with that  
setup, all of the htree tests passed.

> **···**
>
> On 6/28/05, why the lucky stiff \<ruby-talk@whytheluckystiff.net\> wrote:
> 
> > \_why
> 
> --  
> Bill Guindon (aka aGorilla)

---

<div class="post-metadata">

**Author:** ![Bucco](https://avatars.discourse-cdn.com/v4/letter/b/e0b2c6/32.png) [@Bucco](https://rubytalk.org/u/Bucco)\
**Post date:** [28 June 2005 00:20 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/8 "2005-06-28T00:20:39Z")

</div>

I have a couple of more questions then:

1. I tried the example for the htmltokenizer and got an error around  
assert. Where/what is the assert method?

2. What do you mean by "slurp" in the rest of the text?

3. Any better examples how to use htmltokenizer?

Thanks:)  
SA

---

<div class="post-metadata">

**Author:** ![mathew](https://avatars.discourse-cdn.com/v4/letter/m/5daacb/32.png) [@mathew](https://rubytalk.org/u/mathew)\
**Post date:** [28 June 2005 16:25 UTC](https://rubytalk.org/t/best-way-to-parse-update-html-file/19173/9 "2005-06-28T16:25:41Z")

</div>

Bucco wrote:

> 1. I tried the example for the htmltokenizer and got an error around  
> assert. Where/what is the assert method?

An error around "assert" is likely an internal error of some kind. Assertions are pieces of code placed in software to detect invalid arguments to methods, internal data structure inconsistencies, and so on.

For example, consider the Ruby URI library. It doesn't support all kinds of URI. So, it would be a good idea if it were to assert that the URI it is being passed is one of the kinds it actually knows how to parse. That way, someone innocently using the library with the wrong kind of URI will discover the problem immediately, rather than being passed back bad data, or having some bizarre error occur in the middle of the library code.

So it could be that you're passing an invalid argument to a method of htmltokenizer. It's also possible that you're triggering a bug in the library.

> 2. What do you mean by "slurp" in the rest of the text?

"slurp" meaning "pull in the entire content of the file from the current file pointer onwards, without performing any processing on it".

As in file = File.new("something.gif")  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;data = file.read # slurp!

\<URL:[http://www.retrologic.com/jargon/S/slurp.html&gt](http://www.retrologic.com/jargon/S/slurp.html&gt);

> 3. Any better examples how to use htmltokenizer?

require 'html/htmltokenizer'

#[...]

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;# Parse all the images and links out of the web page  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;tokenizer = HTMLTokenizer.new(@body)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@images = Array.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@links = Array.new  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lastlink = ''  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while tag = tokenizer.getTag('img', 'a')  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if tag.tag\_name == 'img'  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url = tag.attr\_hash['src']  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uri = @uri.merge(url)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@images.push([uri.to\_s, lastlink])  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;url = tag.attr\_hash['href']  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;uri = @uri.merge(url)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@links.push(uri.to\_s)  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lastlink = uri.to\_s  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end  
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end

That's the only time I've used it, I'm afraid. Still, it might give you some ideas.

mathew
