C# - 解析网页的最佳方法是什么?

19
我已将整个网页的HTML保存到一个字符串中,现在我想获取链接的“href”值,最好能够稍后将它们保存到不同的字符串中。最好的方法是什么?
我尝试将该字符串保存为.xml文档,并使用XPathDocument导航器对其进行解析,但(惊讶吧)它无法很好地导航一个不真正是XML文档的文件。 正则表达式是实现我想要的功能的最佳方法吗?
8个回答

45

我可以推荐使用HTML Agility Pack。我在需要解析HTML的几个情况下使用过它,效果非常好。一旦你将HTML加载到其中,就可以使用XPath表达式查询文档并获取锚点标签(以及其中的任何其他内容)。

HtmlDocument yourDoc = // load your HTML;
int someCount = yourDoc.DocumentNode.SelectNodes("your_xpath").Count;

哇,太棒了... :) 我本来要写一个HTML解析器...但现在我不需要再做了...非常感谢Jeff。 - Dr TJ

10

正则表达式是一种方法,但可能会有问题。

大多数HTML页面不能使用标准的html技术进行解析,因为正如您发现的那样,大多数页面无法通过验证。

您可以花时间尝试集成HTML Tidy或类似工具,但构建所需的正则表达式会更快。

更新

在本次更新时,我收到了15个赞和9个踩。 我认为可能人们没有仔细阅读问题或此答案下的评论。 OP想要做的就是获取href值。就这些。从这个角度来看,一个简单的正则表达式就足够了。如果作者想要解析其他内容,那么我绝不会推荐正则表达式,正如我在开始时所说,这会带来问题。


好答案 - 正则表达式是你的朋友! - Jarrod Dixon
23
回答不好,请勿这样做。 - SLaks
8
嗯,使用正则表达式解析HTML。可能会出什么问题呢?哦对了:https://dev59.com/X3I-5IYBdhLWcg3wq6do - Ash
4
尝试用正则表达式解析所有标签是个不好的想法。然而,正则表达式可以抓取字符串中所有 href="whatever" 值,这也正是 OP 想要做的。 - NotMe
当然,边缘情况可能会发生——例如模式存在于文本节点中——但正则表达式可以是足够好的解决方案,并且非常容易和快速实现。 - Gerardo Lima

5

2
您可能需要类似Majestic解析器的东西:http://www.majestic12.co.uk/projects/html_parser.php。还有其他一些可以处理不稳定HTML的选项,值得一看的是Html Agility Pack,正如其他人提到的那样。我认为正则表达式并不是HTML的理想解决方案,因为HTML不是上下文无关的。它们可能会产生足够精确但不够准确的结果;即使确定地识别URI也是一个混乱的问题。

2

如果可能的话,最好不要重新发明轮子。一些很好的工具存在,可以将HTML转换为格式良好的XML,或者作为XmlReader:

以下是三个好工具:

  1. TagSoup是一个开源程序,是由John Cowan开发的基于Java和SAX的工具。它是一个符合SAX标准的Java解析器,不像解析良好格式或有效XML,而是解析在实际应用中发现的HTML:糟糕、恶劣和残酷,但往往远非简短。TagSoup专为那些必须使用某种理性应用设计来处理这些内容的人而设计。通过提供SAX接口,它允许甚至将标准的XML工具应用于最糟糕的HTML。TagSoup还包括一个命令行处理器,可以读取HTML文件并生成干净的HTML或类似XHTML的规范XML。
    Taggle是TagSoup的商业C++端口。

  2. SgmlReader是由微软的Chris Lovett开发的工具。
    SgmlReader是一个XmlReader API,适用于任何SGML文档(包括对HTML的内置支持)。还提供了一个命令行实用程序,可输出格式良好的XML结果。
    下载包括独立可执行文件和完整源代码的zip文件:SgmlReader.zip

  3. 一个杰出的成就是David Carlisle编写的XSLT 2.0纯解析器

阅读它的代码对我们每个人来说都是一次很好的学习练习。
从描述中可以看出:
"d:htmlparse(string)"函数有两种形式:一种是只有一个参数的形式,相当于使用命名空间为"http://ww.w3.org/1999/xhtml"和html-mode为true()的形式进行解析;另一种是有三个参数的形式,可以指定命名空间和html-mode参数。该函数使用内置启发式方法将字符串解析为HTML和/或XML,并控制隐含元素的开启和关闭。它不具有完全的HTML DTD知识,但具有完整的空元素列表和实体定义列表。HTML实体、十进制和十六进制字符引用都被接受。请注意,即使html-mode=false(),也会识别HTML实体。如果html-mode为true(),元素名称将转换为小写,并放置在由命名空间参数指定的命名空间中(如果命名空间参数为"",则表示无命名空间,除非输入具有显式命名空间声明,在这种情况下,将遵循这些声明)。属性名称在html-mode=true()时转换为小写。
阅读更详细的描述在这里
希望这有所帮助。
祝好,
Dimitre Novatchev。

1

我同意Chris Lively的观点,因为HTML通常不是很规范,所以最好使用正则表达式来处理。

href=[\"\'](http:\/\/|\.\/|\/)?\w+(\.\w+)*(\/\w+(\.\w+)?)*(\/|\?\w*=\w*(&\w*=\w*)*)?[\"\']

这里开始,RegExLib应该能帮助你入门


谢谢时间。我正在尝试使用这个,然而,C#一直告诉我所有的反斜杠都是"无法识别的转义序列"。在其中加上一个@也没有帮助。你知道发生了什么吗? - MattSayar
哈哈,我是说“谢谢TIM”。时间不值得感谢。 - MattSayar
这个链接帮助我解决了问题 http://regexadvice.com/forums/thread/36529.aspx - MattSayar
5
由于HTML经常不规范,所以不应使用正则表达式来处理它。参考链接:https://dev59.com/X3I-5IYBdhLWcg3wq6do - Ash
我同意一般情况下的观点,解析HTML比任何人预期的都要困难得多,通常仅使用正则表达式是不够的。但是,在这种特定情况下,仅解析hrefs,正则表达式就足够了,而且比XML DOM更容易。 - Tim Jarvis

0

如果您知道或可以修复文档,使其至少成为格式良好的文件,则使用xml可能会更加顺利。如果您有良好的html(或者更确切地说是xhtml),则.Net中的xml系统应该能够处理它。不幸的是,好的html非常少见。

另一方面,正则表达式在解析html时非常糟糕。幸运的是,您不需要处理完整的html规范。您所需要关注的全部内容都是解析href=字符串以获取url。即使这也可能很棘手,因此我不会立即尝试。相反,我将首先询问一些问题,以尝试建立一些基本规则。 它们基本上都归结为“您对文档了解多少?”,但是在这里还有:

  • 你知道 "href" 文本是否总是小写吗?
  • 你知道它是否总是在 URL 周围使用双引号、单引号或什么都不用吗?
  • 它总是一个有效的 URL,还是需要考虑像 '#'、javascript 语句之类的东西?
  • 是否可能处理一个描述 HTML 特性的文档(例如:href= 也可能在文档中出现,而不属于锚标签)?
  • 你还能告诉我们关于文档的什么信息吗?

我知道href文本始终是小写的。 它始终使用双引号。 它可能不总是一个有效的URL,但我假设99%的时间都是这样的。 文档有可能在其他地方也有“href”。 这就是我所能想到的。解析函数真的比正则表达式更好吗? - MattSayar
问题在于允许在其他地方使用href=。 这会使您返回查找真正的锚标记,这意味着最好使用(非常宽松的)解析库。 您甚至可以尝试将其加载到Web浏览器控件中。 - Joel Coehoorn

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接