前面两篇讲解了解析Html的思想个核心算法,感谢各位提出了意见。先专门列出一文作为解释:
1、ViewStates提到,“<TD 阿道夫骄傲了沙发</TD>”怎么办?这个是可以解析出来的,其中解析后节点名为“td”,而“ 阿道夫骄傲了沙发</TD”解析成了属性。
如图:

上面的图中,包含了上面提到的例子。
回复ml_dark 兄
象这种.. <div><div></div><div><div><div></div></div></div></div> 你看看你的解析结果....

解析结果如上图。
转 老田的专栏
解析Html成标签树结构以后,我们不但可以很容易取得想要的元素,同时也很容易将Html转换成对应的XML文件。但是由于代码是在公司写的,所以没有粘贴出来的可能性,所以我只能给出大概的代码流程,具体细节描述,相信各位都很容易写出来,并且写的比我好,关键的是算法实现思想。算法的关键如下:
- public void Parse()
- {
- char ch = GetCurrentChar(); //取第一个字符
- while (!Eof())
- {
- if (ch == '<')
- {
- ch = MoveNext(); //取下一个字符
- if ((ch >= 'A') && (ch <= 'Z') || (ch == '!'))
- {
- iBeginPos = Index; //记录开始位置
- //表示可能是一个标签
- HtmlTag tag = GetTag(); //解析此Tag
- if (tag != null)
- {
- //首先判断是否有文本
- if (m_CurrentText.Lenght > 0)
- {
- //将文本作为一个普通Tag入栈
- Stack.Push(new HtmlTextTag(m_CurrentText));
- }
- tag.BeginPos = iBeginPos; //记录此Tag的开始位置
- Stack.Push(tag); //把Tag入栈
- }
- }
- ch = GetCurrentChar();
- if (ch == '/')
- {
- //可能是结束标签
- tagName = GetTagName();
- //从上到下查看Stack,如果Tag中存在
- if (FindInStack(tagName))
- {
- //在栈中找到名为tagName的元素,则把找到的元素出栈
- PopTag(tagName);
- }
- }
- }
- else
- {
- //对于<AAA>xxx</AAA>之间的文本xxx,这里将作为TextTag来处理
- m_CurrentText.Append(GetCurrentChar());
- }
- //继续处理下一个字符
- ch = MoveNext();
- }
- //解析完成以后,如果栈不空,那么把元素出栈,并把最后一次出栈的元素作为根
- if (Stack.Count > 0)
- {
- HtmlTag tag = null;
- while (Stack.Count > 0)
- {
- tag = Stack.Pop();
- PopTag(tag);
- }
- //最后一个元素作为根元素
- if (tag != null)
- {
- m_listRoot.Add(tag);
- }
- }
- }
- private void PopTag(HtmlTag tag)
- {
- int iLevel = Stack.Count;
- //找到了元素,把iLevel到m_IMaxLevel中所有的元素按照全部作为tag的子元素
- for (int i = iLevel + 1; i < m_iMaxLevel; i++)
- {
- for (j = 0; j < HashTable[i].Count; j++)
- {
- tag.Children.Add(HashTable[i][j]);
- }
- }
- //表示栈已经为空,那么最后一次出栈的tag将作为根
- if (Stack.Count == 0)
- {
- m_listRoot.Add(tag);
- }
- }
- private void PopTag(string tagName)
- {
- /*
- * 元素出栈的时候,首先需要把当前已经存在了的HtmlTextTag入栈
- * 比如:<A>文本段1<B>文本段2</B>文本段3</A>
- * 在Parse中,当解析出<B>入栈前,需要先把"文本段1"入栈
- * 在这里,解析出了</B>结束标志
- * 那么首先需要把"文本段2"入栈。
- * 解析出</A>则需要把"文本段3'入栈。
- * 这样才能够保证"文本段1"和"文本段3"成为<A>的子节点,而"文本段2"作为<B>的子节点
- */
- if (m_CurrentText.Lenght > 0)
- {
- //将文本作为一个普通Tag入栈
- Stack.Push(new HtmlTextTag(m_CurrentText));
- }
- HtmlTag tag = Stack.Pop(); //元素出栈
- int iLevel = Stack.Count; //记录栈元素数
- while (tag.Name != tagName)
- {
- //将tag放入第iLevel层的List中
- HashTable[iLevel].Add(tag);
- tag = Stack.Pop();
- iLevel = Stack.Count;
- }
- //元素出栈后续处理
- PopTag(tag);
- }
- private HtmlTag GetTag()
- {
- if ("如果发现是 < !--开头的元素") //则表示是注释
- {
- SkipComment();
- }
- HtmlTag tag = new HtmlTag();
- tag.Name = GetTagName();
- //这里的Attribute我将其作为HashTable类型,Hash[属性名]=属性值
- tag.Attribute = GetTagAttribute();
- return tag;
- }
解析结束以后,通过访问m_listRoot就可以遍历出所有的节点了。上面仅仅是给出了大概的方法,不过我相信要将上面的方法转换成可运行代码,各位都是有这个能力的。。。
转 老田的专栏
提示:
根据这几天的访问量跟踪,我发现很多访者都没有很好地关注到真正进行算法描述的章节《解析Html生成标签树(一)》,特此进行提示。
搜索引擎一个最重要的步骤就是,获得网页的正文内容,我相信每个人看见一篇网页的时候,从视觉上都很容易知道哪个部分是正文内容,哪个部分是版权信息,哪个部分是广告信息。但是要让计算机明白这些,估计要若干年(到时候还不知道人类是否已经变异成4只眼睛,5个耳朵。。。)之后。
在网络上大量存在一些关于网页正文抽取的理论性文章,最著名的应该数介绍北大天网的《搜索引擎原理技术与系统》一书中所介绍的网页净化技术了,中心思想大概就是,首先将Html源文件解析成一棵以Html标签为节点的树(为了后面的描述方便,我们称其为“Html树”),然后再根据一定的启发式规则进行判断,当然各位可以具体去找这书来看看。所以很多的都是参考这种方式来实现。遗憾的是我没有找到源代码,并且我也不擅长Linux编程,所以即使拿到代码,估计也会头痛很久(看不懂啊)。怎么办呢?Baidu以后Google,Google以后Baidu。我都不知道为了这个事情,向这两家贡献了多少流量啊,估计他们应该给我发奖金。但是我都没有找到自己想要的代码。。。据说有一个名叫HtmlParser的家伙可以做这东西。但是我下载了一个以后(后来证明应该不是正宗的,在一个老外写的Spider中有这么几个类http://www.codeproject.com/上下载的)发现不是我想要的,虽然可以很好的解析出里面的超级链接之类的东西。所以最终确定不能用,最多只能作为学习例子进行研究。
估计HtmlParser应该是可以实现,但是我人懒,没有找到使用这东西的可执行文件,最主要的是我根本就还没有下载到HtmlParser工具包。我最喜欢的是能够先看效果,然后研究,因为我没有太多的时间来研究老外写的代码,说实在话,我的代码有点自己的风格,但是老外的代码有时候也是很难读懂的,这点“要向老外学习”。
我也找到这么篇文章:http://blog.csdn.net/lanphaday/archive/2007/08/13/1741185.aspx,说这种方法可行,可是这个不是C#写的,也找不到可执行文件,最重要的是,最终我都没有读懂这文章。
某天之后我终于醒悟,决定自己写一个能够解析Html文档的东西。自己写的,想怎么改就怎么改,想怎么用就怎么用。再废话几句,为什么要把Html先解析成Html树呢,因为在实际的搜索引擎应用中,人们可能会需要利用到每个Html的标签的一些属性(比如粗体、斜体、颜色等等)来判断一篇网页的重要程度,便于对网页进行进一步的挖掘。
我相信各位也是比较喜欢首先看效果的,我也首先给各位看看效果:
Html文件如下:
- <html>
- <head>
- <title>解析Html文件</title>
- <meta content="!@#$%^&*(">
- </head>
- <body>
- <table>
- <tr>
- <td>
- <table>
- <tr>
- <td></td>
- </tr>
- </table>
- </td>
- <td></td>
- </tr>
- <tr>
- <td></td>
- <td></td>
- </tr>
- </table>
- </body>
- </html>
解析以后图片如下:

结果到是理想的。下一篇将详细介绍生成算法
在:http://blog.csdn.net/RonoTian/archive/2008/06/06/2517568.aspx中我已经详细介绍了方法,并且配上了核心思路和核心代码。