RegEx匹配XHTML自包含标签以外的打开标签


1473

我需要匹配所有这些开始标签:

<p>
<a href="foo">

但不是这些:

<br />
<hr class="foo" />

我想出了这个,想确保我做对了。我只是捕捉到a-z

<([a-z]+) *[^/]*?>

我相信它说:

  • 找到一个小于,然后
  • 查找(并捕获)az一次或多次,然后
  • 找到零个或多个空格,然后
  • 找到零次或多次贪婪的字符,除了/,然后
  • 寻找大于

我有那个权利吗?更重要的是,您怎么看?

Answers:


4417

您无法使用正则表达式解析[X] HTML。因为正则表达式无法解析HTML。正则表达式不是可用于正确解析HTML的工具。正如我之前在这里多次回答HTML和Regex问题一样,使用正则表达式将不允许您使用HTML。正则表达式是一种工具,不够复杂,无法理解HTML所采用的结构。HTML不是常规语言,因此无法通过常规表达式进行解析。正则表达式查询无法将HTML分解为有意义的部分。有很多次了,但是没有得到我。甚至Perl使用的增强的不规则正则表达式也无法完成HTML解析任务。你永远不会让我崩溃。HTML是一种足够复杂的语言,无法通过正则表达式进行解析。甚至Jon Skeet也无法使用正则表达式解析HTML。每次您尝试使用正则表达式解析HTML时,这个邪恶的孩子都会哭泣处女之血,俄罗斯黑客将您的Web应用程序伪造。用正则表达式解析HTML会使灵魂陷入生活领域。HTML和正则表达式可以像爱情,婚姻和仪式杀婴一样一起使用。<center>不能容纳为时已晚。正则表达式和HTML共同作用于同一个概念空间中,将像太多水腻子一样破坏您的思维。如果您使用正则表达式解析HTML,那么您就是在屈服于他们及其亵渎神明的方式,这使我们所有人都为不愿在基本多语言平面中表达其名字的人付出辛劳。HTML + regexp将在您观察的同时液化知觉的神经,使您的心灵在恐怖的冲击中枯萎。为时已晚,为时已晚,我们无法挽救孩子的混乱,确保正则表达式会消耗掉所有活组织(除了HTML,如先前所言,它不能这样做)亲爱的上帝帮助我们,任何人都可以使用正则表达式来解析这种祸害 HTML 使用rege x作为处理HTML的工具,将人类注定了永恒的酷刑和安全漏洞,在这个世界和腐败实体(例如SGML实体,但更腐败)的可怕领域之间建立了断层,这仅仅是一瞥。 reg的世界前解析器HTML将插件 tantly运输AP rogrammer的意识我 n要AW ORL人的不断尖叫d,他来了,可恶SL ithy正则表达式感染WIL 升吞噬你的HT ML解析器,应用和存在的Visual Basic一样,所有的时间只有更糟,他谈到他命令 ES 没有网络连接 GHT ^ h E排,喜小号邪恶的光采德stro҉ying所有的启蒙运动,HTML标记泄漏fr̶ǫm玩吧眼睛像LIQ UID p AlN,定期EXP重的歌曲裂变解析将EXTI nguish的铁道部的声音从SP TAL的人在这里我可以看到它,你可以看到它是美丽的牛逼,他˚F inal snufFing头Ø F中的谎言的男人一切都失去了A的 LL我SLOST日è小马才想起小号他命令他共同ES 式T ICH或permeat ES人 L我FAC Ë我的脸ᵒh神ň □否野应 o在 Θ停止牛逼,他的*̶͑̾̾GL ES ͎a̧͈͖r̽̾̈́͒͑eñ OT真实ZA̡͊͠͝LGΌISͮ҉̯͈͕̹̘Ť O͇̹̺Ɲ̴ȳ̳TH ËPO NYH̸̡̪̯ͨ͊̽̅̾Ȩ̶̧̨̬̩̹̭̯̾͛ͪ̈ͧ̾ͬ͘C̷̙̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ š


您是否尝试过使用XML解析器?


主持人的话

该帖子已被锁定,以防止对其内容进行不适当的编辑。该帖子看起来与预期的完全一样-内容没有问题。请不要标记它以引起我们的注意。


179
Kobi:我认为现在是时候该辞去Assistant's Do n't Parse HTML with Regex Officer的职位了。不管我们说多少次,他们都不会每天甚至每小时都停止。这是一个失败的事业,别人可以为之奋斗。因此,如有必要,请继续使用正则表达式解析HTML。这只是断码,不是生与死。
bobince

27
是否可以使用RegEx解析此答案?
克里斯·波特

2
如果您看不到此帖子,请参见以下所有内容的屏幕截图:imgur.com/gOPS2.png
Andrew Keeton 2009年

3247

虽然只有正则表达式的任意 HTML是不可能的,但有时使用它们来解析一组有限的已知 HTML 是适当的。

如果您想从一小组HTML页面中抓取数据,然后将其填充到数据库中,则正则表达式可能会正常工作。例如,我最近想获得我从议会网站上获得的澳大利亚联邦代表的姓名,政党和地区。这是一项有限的一次性工作。

正则表达式对我来说效果很好,并且安装起来非常快。


131
此外,明智地使用scan&regex,从大型文档中以相当规则的格式抓取数据将比任何通用解析器更快。而且,如果您对编码正则表达式感到满意,则编码方式比编码xpath更快。而且几乎可以肯定,您要抓取的内容不会那么脆弱。真是的
Michael Johnston

255
@MichaelJohnston“少脆弱”吗?几乎可以肯定。正则表达式比XML解析器可以忽略的文本格式细节更重要。在&foo;编码和CDATA部分之间切换?使用HTML缩小程序删除浏览器无法呈现的文档中的所有空格?XML解析器将不在乎,编写良好的XPath语句也将不在乎。另一方面,基于正则表达式的“解析器” ...
查尔斯·达菲

41
@CharlesDuffy可以做一次工作,对于空格,我们使用\ s +
量子

68
@xiaomao的确,如果必须了解所有技巧和变通办法才能获得80%的解决方案,而该解决方案在其余时间都“为您工作”而失败,那么我无法阻止您。同时,我使用解析器处理问题,解析器对100%语法有效的XML起作用。
查尔斯·达菲

374
我曾经不得不从大约1万个页面中提取一些数据,所有数据都使用相同的HTML模板。他们到处都是HTML错误,这些错误会导致解析器阻塞,并且所有样式都是内联的或类似的样式<font>:没有可帮助导航DOM的类或ID。经过一整天的“正确”方法的努力后,我终于切换到正则表达式解决方案,并使其在一个小时内正常工作。
Paul A Jungwirth,2012年

2037

我认为这里的缺陷是HTML是Chomsky Type 2语法(无上下文语法),而RegEx是Chomsky Type 3语法(常规语法)。由于类型2语法从根本上比类型3语法复杂(请参阅Chomsky层次结构),因此从数学上讲,无法使用RegEx解析XML。

但是许多人会尝试,有些甚至会声称成功-但直到其他人发现错误并将您完全搞砸为止。


225
OP要求解析非常有限的XHTML子集:开始标记。使(X)HTML成为CFG的原因是它有潜力在其他元素的开始和结束标记之间包含元素(如语法规则A -> s A e)。(X)HTML 开始标记没有此属性:开始标记不能包含其他开始标记。OP尝试解析的子集不是CFG。
LarsH'3

101
在CS理论中,正则语言无上下文语言的严格子集,但是主流编程语言中的正则表达式实现更为强大。正如noulakaz.net/weblog/2007/03/18/…所描述的那样,所谓的“正则表达式”可以检查一元数中的素数,这当然是CS理论中的正则表达式无法实现的。
亚当·米哈尔辛

11
@eyelidlessness:相同的“仅当”适用于所有CFG,不是吗?即,如果(X)HTML输入格式不正确,那么即使是成熟的XML解析器也无法可靠地工作。也许如果您给出的示例是您所指的“在现实世界中的用户代理中实现的(X)HTML语法错误”,那么我会理解您所获得的好处。
LarsH 2012年

82
@AdamMihalcin完全正确。大多数现存的正则表达式引擎比Chomsky Type 3语法更强大(例如,非贪婪匹配,后向引用)。一些正则表达式引擎(例如Perl)已经完成了Turing。的确,即使是那些解析HTML的工具也很差,但是这种经常被引用的观点并不是原因。
dubiousjim

26
这是这里最“完整和简短”的答案。它引导人们学习形式语法和语言的基础知识,并希望有一些数学知识,以便他们不会将时间浪费在诸如在多项式时间内解决NP任务等绝望的事情上
mishmashru 2013年

1332

不要听这些家伙。如果将任务分解成较小的部分,则可以使用regex 完全解析上下文无关的语法。您可以使用按顺序执行每个脚本的脚本来生成正确的模式:

  1. 解决停止问题。
  2. 摆一个圆圈。
  3. 计算O(log n)或以下的旅行商问题。如果不止如此,您将用完RAM,引擎将挂起。
  4. 该模式将非常大,因此请确保您有一个无损压缩随机数据的算法。
  5. 几乎在那里-将整个事情除以零。十分简单。

我本人还没有完成最后一部分,但是我知道我已经接近了。CthulhuRlyehWgahnaglFhtagnException由于某种原因,它总是抛出s,因此我将其移植到VB 6并使用On Error Resume Next。一旦调查了刚刚在墙上打开的这扇奇怪的门,我将更新代码。嗯

PS Pierre de Fermat也想出了办法,但是他所写的利润不足以编写代码。


80
零除比您提到的其他问题容易得多。如果您使用间隔,而不是普通的浮点算术(每个人都应该是,但没有人是),则可以用[包含[零]的间隔]高兴地除以某个数。结果就是一个包含正负无穷大的间隔。
rjmunro 2012年

147
Fermat的小边距问题已通过现代文本编辑软件中的软边距解决。
kd4ttc

50
Fermat的小边距问题已由Randall Munroe通过将fontsize设置为零来解决:xkcd.com/1381
heltonbiker 2014年

29
FYI:Fermat的问题已经实际上解决了在1995年,只花了数学家358年这样做。
jmiserez

10
通过使用冷聚变产生的布朗棘轮,我得以绕过了粘滞除零步骤。
蒂姆·莱纳

1072

免责声明:如果有选择,请使用解析器。那个...

这是我使用(!)来匹配HTML标签的正则表达式:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

可能并不完美,但是我通过许多 HTML 来运行此代码。请注意,它甚至会捕获<a name="badgenerator"">显示在Web上的奇怪内容。

我想使它与自包含标签不匹配,您要么想使用Kobi的否定式外观:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>

或仅合并是否合并。

致下降投票者:这是来自实际产品的有效代码。我怀疑任何阅读此页面的人都会给人一种印象,即在HTML上使用正则表达式在社会上是可以接受的。

警告:我应该注意到,在存在CDATA块,注释以及脚本和样式元素的情况下,此正则表达式仍会分解。好消息是,您可以摆脱使用正则表达式的那些东西。


94
我会选择一种对理智的事情有用的东西,而不是为不普遍完美而哭泣:-)
prajeesh kumar 2012年

55
有人在HTML内使用CDATA吗?
Danubian Sailor

16
因此,您实际上并不能仅使用regexp解决解析问题,而是可以使用解析器作为解析器的一部分。PS:有效的产品并不意味着良好的代码。没有违法行为,但这就是工业编程的工作方式并获得其收入
mishmashru 2013年

32
您的正则表达式无法在最短的有效HTML上启动<!doctype html><title><</title>。简单的'<!doctype html><title><</title>'.match(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g)回报["<!doctype html>", "<title>", "<</title>"]而应该["<title>", "</title>"]

2
如果我们只是尝试匹配而不匹配给定的示例,/<.([[^r>][^>]*)?>/g有效:-) // javascript:'<p> <a href = “ foo”> <br /> <hr class =“ foo” />'.match(/<.([^r>][^>]*)?>/g)–
imma

506

有人会告诉你地球是圆形的(或者,如果他们想使用奇怪的单词,也许地球是扁球体)。他们在撒谎。

有人会告诉你正则表达式不应该是递归的。他们限制了你。他们需要征服您,并且通过让您保持愚昧来做到这一点。

您可以生活在现实中,也可以服用红色药丸。

像元帅勋爵(他是元帅.NET类的亲戚吗?)一样,我已经看到了基于Underverse Stack的Regex-Verse,并且返回了无法想象的能力知识。是的,我认为有一个或两个老人在保护他们,但是他们正在电视上看足球,所以这并不困难。

我认为XML情况很简单。RegEx(使用.NET语法)在base64中进行了压缩和编码,以使您更容易理解,这应该是这样的:

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
+r/vD34mUADO1P4/AQAA//8=

设置的选项是RegexOptions.ExplicitCapture。您正在寻找的捕获组是ELEMENTNAME。如果捕获组ERROR不为空,则存在解析错误,并且正则表达式停止。

如果您在将其转换为可读的正则表达式时遇到问题,这应该会有所帮助:

static string FromBase64(string str)
{
    byte[] byteArray = Convert.FromBase64String(str);

    using (var msIn = new MemoryStream(byteArray))
    using (var msOut = new MemoryStream()) {
        using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
            ds.CopyTo(msOut);
        }

        return Encoding.UTF8.GetString(msOut.ToArray());
    }
}

如果您不确定,不,我不是在开玩笑(但也许我在撒谎)。它将起作用。我已经建立了大量的单元测试来对其进行测试,甚至我已经使用了(部分)一致性测试。它是一个标记器,而不是成熟的解析器,因此它将仅将XML拆分为其组件标记。它不会解析/集成DTD。

哦...如果您想要正则表达式的源代码,请使用一些辅助方法:

正则表达式以标记xml完全正则表达式


68
天哪,这是巨大的。我最大的问题是为什么?您意识到所有现代语言都有XML解析器,对吗?您可以像3行一样完成所有操作,并确保它可以正常工作。此外,您还认识到纯正则表达式无法证明不能做某些事情吗?除非您创建了混合正则表达式/命令式代码解析器,否则它看起来并不像您所拥有的那样。您还可以压缩随机数据吗?
贾斯汀·摩根

112
@贾斯汀,我不需要理由。可以做到的(这不是非法的/不道德的),所以我做到了。除了我们承认的那些(拿破仑·希尔),思维上没有任何限制。现代语言可以解析XML吗?真?我认为那是非法的!:-)
xanatos 2011年

76
先生,我相信了。我将使用该代码作为我的永动机的内核的一部分-您相信专利局的那些傻瓜会继续拒绝我的申请吗?好吧,我给他们看。我会给大家看!
贾斯汀·摩根

31
@Justin因此,根据定义,Xml解析器没有错误,而Regex不是吗?因为如果Xml Parser定义上不是没有错误的,那么可能会有一个使它崩溃的xml,我们返回到步骤0。可以这样说:Xml Parser和此Regex都试图解析所有“合法的” XML。他们可以解析一些“非法” XML。错误可能会使它们同时崩溃。C#XmlReader肯定比该Regex经过更多测试。
xanatos 2011年

31
不,没有什么是没有错误的:1)所有程序都包含至少一个错误。2)所有程序至少包含一行不必要的源代码。3)通过#1和#2并使用逻辑归纳法,可以很容易地证明任何程序都可以简化为带有错误的一行代码。(来自Learning Perl)
Scott Weaver 2012年

299

在shell中,您可以使用sed解析HTML

  1. 图灵赛德
  2. 编写HTML解析器(作业)
  3. ???
  4. 利润!

相关(为什么不应该使用正则表达式匹配):


3
恐怕你没有开玩笑,@ kenorb。请再次阅读问题和接受的答案。这与一般的HTML解析工具无关,也不与HTML解析外壳工具有关,而与通过正则表达式分析HTML有关。
Palec 2015年

1
不,@ Abdul。从数学意义上讲,这是完全不可能的。
Palec

3
是的,这个答案很好地总结了@Abdul。但是请注意,正则表达式实现并不是真正意义上的正则表达式-它们具有使它们更强大(通常为图灵完备)的结构(等效于Type 0语法)。这种说法违背了这一事实,但是从某种意义上说,正则表达式从来都不意味着能够胜任这一工作,但这种说法仍然是正确的。
Palec

2
顺便说一句,我所指的笑话是kenorb(激进的)编辑之前的答案的内容,特别是修订版4,@ Abdul。
Palec

3
有趣的是,OP从未要求使用正则表达式解析html。他要求使用正则表达式匹配文本(恰好是HTML)。这是完全合理的。
Paralife

274

我同意解析XML 特别是HTML的正确工具是解析器,而不是正则表达式引擎。但是,就像其他人指出的那样,有时使用正则表达式会更快,更容易,并且如果您知道数据格式,则可以完成工作。

Microsoft实际上在.NET Framework中有一节有关正则表达式最佳实践,并专门讨论了考虑输入源

正则表达式确实有局限性,但是您是否考虑了以下内容?

.NET框架在正则表达式方面是唯一的,因为它支持平衡组定义

因此,我相信您可以使用正则表达式解析XML。但是请注意,它必须是有效的XML浏览器对HTML的理解非常宽容,并且HTML中允许使用错误的XML语法)。这是可能的,因为“平衡组定义”将允许正则表达式引擎充当PDA。

引用上面引用的第1条:

.NET正则表达式引擎

如上所述,不能通过正则表达式描述适当平衡的构造。但是,.NET正则表达式引擎提供了一些允许识别平衡结构的结构。

  • (?<group>) -使用名称组将捕获的结果压入捕获堆栈。
  • (?<-group>) -从捕获堆栈中弹出名称组最高的捕获。
  • (?(group)yes|no) -如果存在名称为group的组,则匹配yes,否则不匹配。

这些构造允许.NET正则表达式通过本质上允许堆栈操作的简单版本(即push,pop和empty)来模拟受限PDA。简单的操作分别相当于递增,递减和与零比较。这使.NET正则表达式引擎可以识别上下文无关语言的子集,尤其是那些只需要简单计数器的语言。反过来,这允许非传统的.NET正则表达式识别各个适当平衡的构造。

考虑以下正则表达式:

(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
   <!-- .*? -->                  |
   <[^>]*/>                      |
   (?<opentag><(?!/)[^>]*[^/]>)  |
   (?<-opentag></[^>]*[^/]>)     |
   [^<>]*
)*
(?(opentag)(?!))

使用标志:

  • 单线
  • IgnorePatternWhitespace(如果折叠正则表达式并删除所有空白,则不需要)
  • IgnoreCase(不必要)

正则表达式解释(内联)

(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?>                                        # atomic group / don't backtrack (faster)
   <!-- .*? -->                 |          # match xml / html comment
   <[^>]*/>                     |          # self closing tag
   (?<opentag><(?!/)[^>]*[^/]>) |          # push opening xml tag
   (?<-opentag></[^>]*[^/]>)    |          # pop closing xml tag
   [^<>]*                                  # something between tags
)*                                         # match as many xml tags as possible
(?(opentag)(?!))                           # ensure no 'opentag' groups are on stack

您可以在A Better .NET正则表达式测试器中尝试此操作。

我使用了以下示例资源:

<html>
<body>
<div>
   <br />
   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>
</div>
</body>
</html>

找到匹配项:

   <ul id="matchMe" type="square">
      <li>stuff...</li>
      <li>more stuff</li>
      <li>
          <div>
               <span>still more</span>
               <ul>
                    <li>Another &gt;ul&lt;, oh my!</li>
                    <li>...</li>
               </ul>
          </div>
      </li>
   </ul>

虽然实际上是这样出来的:

<ul id="matchMe" type="square">           <li>stuff...</li>           <li>more stuff</li>           <li>               <div>                    <span>still more</span>                    <ul>                         <li>Another &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

最后,我真的很喜欢Jeff Atwood的文章: 解析HTML Cthulhu Way。有趣的是,它引用了这个问题的答案,目前该问题的投票已超过4k。


18
System.Text不属于C#。它是.NET的一部分。
约翰·桑德斯

8
在您的正则表达式((?=<ul\s*id="matchMe"\s*type="square"\s*>) # match start with <ul id="matchMe"...)的第一行中,“ <ul”和“ id”之间应为\s+,而不是\s*,除非您希望它与<ulid = ...相匹配;)
C0deH4cker 2012年

@ C0deH4cker您是正确的,该表达式应具有\s+而不是\s*
山姆

4
这并不是说我真正了解它,但我觉得你的正则表达式失败<img src="images/pic.jpg" />
Scheintod

3
@Scheintod谢谢您的评论。我更新了代码。上一个表达式因自封闭标签而失败,该自封闭标签的/内部某处对<img src="images/pic.jpg" />html 失败。
2013年

258

我建议使用QueryPath在PHP中解析XML和HTML。它的语法基本上与jQuery相同,只是在服务器端。


8
@Kyle-jQuery不解析XML,而是使用客户端的内置解析器(如果有的话)。因此,您不需要jQuery就能做到,而只需两行普通的旧JavaScript即可。如果没有内置的解析器,jQuery将无济于事。
RobG

1
@RobG实际上jQuery使用DOM,而不是内置的解析器。
Qix-蒙尼卡犯了错误,

11
@Qix-您最好告诉文档的作者:“ jQuery.parseXML使用浏览器的本机解析功能…… ”。来源:jQuery.parseXML()
RobG 2014年

6
来自模因问题(meta.stackexchange.com/questions/19478/the-many-memes-of-meta/…),我喜欢答案之一是“ Use jQuery”
Jorn

221

虽然您无法使用正则表达式解析HTML的答案是正确的,但它们不适用于此处。OP只想用正则表达式解析一个HTML标记,而这可以通过正则表达式来完成。

建议的正则表达式是错误的,但是:

<([a-z]+) *[^/]*?>

如果您向正则表达式添加内容,则可以通过回溯将其强制匹配诸如之类的愚蠢内容<a >>[^/]这太宽松了。另请注意,这<space>*[^/]*是多余的,因为[^/]*还可匹配空格。

我的建议是

<([a-z]+)[^>]*(?<!/)>

(?<! ... )(在Perl正则表达式中)负向后移在哪里。它读为“一个<,然后是一个单词,然后是不是>的任何内容,最后一个可能不是/,后跟>”。

请注意,这允许类似<a/ >(与原始正则表达式一样)的东西,因此,如果您想要更严格的限制,则需要构建一个正则表达式以匹配用空格分隔的属性对。


29
+1表示问题不在于解析完整(X)HTML,而是与匹配(X)HTML开放标记有关。
LarsH 2012年

10
大多数答案似乎都忽略了其他事情,那就是HTML解析器可以很好地在其实现中使用正则表达式来处理HTML的某些部分,如果大多数解析器不这样做,我会感到惊讶。
Thayne 2015年

@Thayne就是这样。解析单个标签时,正则表达式是完成任务的正确工具。必须滚动到页面的中途才能找到合理的答案,这是非常荒谬的。接受的答案是错误的,因为它混合了词法分析和语法分析。
kasperd 2015年

2
当属性值包含“>”或“ /”字符时,此处给出的答案将失败。
Martin L

这在包含注释或CData部分的HTML上将无法正常工作。如果带引号的属性包含>字符,它将也无法正常工作。我同意OP什么建议可以用正则表达式来完成,但是这里介绍的是一个远简单化。
雅克·B'J

183

尝试:

<([^\s]+)(\s[^>]*?)?(?<!/)>

它与您的相似,但是最后一个>不能在斜杠之后,并且也可以接受h1


107
<a href="foo" title="5> 3“>糟糕</a>
Gareth

21
确实如此,我确实考虑过,但我认为该>符号已正确转义为&gt;。
Kobi

65
>在属性值中有效。实际上,在“规范XML”序列化中,您不能使用&gt;。(除了强调>属性值根本不是一件不寻常的事情,这并不完全相关。)
bobince

5
@Kobi:在正则表达式中,感叹号(放在末尾的那个)是什么意思?
Marco Demaio 2011年

6
@bobince:你确定吗?我不明白了,所以这个有效的HTML也是:<div title="this tag is a <div></div>">hello</div>
Marco Demaio 2011年

179

中国古代的战略家,一般哲学家孙子说:

据说,如果您认识自己的敌人并认识自己,那么您就可以赢得一百场战斗,而不会遭受任何损失。如果您只了解自己而不是对手,那么您可能会赢或输。如果您既不认识自己,也不认识敌人,那么您将永远危害自己。

在这种情况下,您的敌人是HTML,而您本人或正则表达式。您甚至可能是使用不规则正则表达式的Perl。懂HTML。认识你自己。

我编写了一个描述HTML本质的句。

HTML has
complexity exceeding
regular language.

我还撰写了一个hai句,描述了Perl中正则表达式的性质。

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>

153
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');

$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';

$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
    $nodeName = strtolower($el->nodeName);
    if ( !in_array( $nodeName, $selfClosing ) ) {
        var_dump( $nodeName );
    }
}

输出:

string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"

基本上,只需定义自动关闭的元素节点名称,将整个html字符串加载到DOM库中,获取所有元素,遍历并过滤掉非自动关闭的元素并对其进行操作。

我确定您现在已经知道不应为此使用正则表达式。


1
如果要处理真正的XHTML,则将getElementsByTagName附加到NS并指定名称空间。
meder omuraliev 09年

148

我不知道您对此有确切的需求,但是如果您还使用.NET,则不能使用HTML Agility Pack吗?

摘抄:

这是一个.NET代码库,可让您解析“网络外” HTML文件。该解析器对“真实世界”格式的HTML十分宽容。


137

您要第一个>不带/。看这里了解如何做到这一点的详细信息。这被称为否定性回溯。

但是,<bar/></foo>该示例的简单实现最终将在此示例文档中匹配

<foo><bar/></foo>

您能否提供更多有关您要解决的问题的信息?您是否以编程方式遍历标签?


1
是的,我肯定是。确定当前打开的所有标签,然后将其与单独数组中的已关闭标签进行比较。RegEx伤了我的大脑。
Jeff

122

W3C以伪正则表达式形式解释了解析:
W3C Link

遵循了var链接QNameS以及Attribute获得更清晰的画面。
基于此,您可以创建一个很好的正则表达式来处理诸如剥离标签之类的事情。


5
这不是伪正则表达式形式,而是EBNF形式,如此处指定:XML规范,附录6
Rob G

106

如果您需要使用PHP:

PHP DOM 功能,除非它是格式正确的XML将无法正常工作。无论它们对全人类的使用有多好。

simplehtmldom很好,但是我发现它有点问题,并且它占用了大量内存[会在大页面上崩溃。]

我从未使用过querypath,因此无法评论其有用性。

另一个可以尝试的是我的DOMParser,它对资源非常少,并且我已经很开心地使用了一段时间。简单易学且功能强大。

对于Python和Java,发布了类似的链接。

对于不满意的人-我仅在XML解析器证明无法承受实际使用时编写类。宗教否决只会阻止发布有用的答案-请使问题保持​​在问题的角度内。


95

解决方法如下:

<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';

// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
    <br/>and check out.<hr />
    <h2>title</h2>
    <a name ="paragraph" rel= "I\'m an anchor"></a>
    Fine, <span title=\'highlight the "punch"\'>thanks<span>.
    <div class = "clear"></div>
    <br>';

// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);

// print the result:
print_r($matches[0]);
?>

为了进行深入测试,我输入了如下字符串自动关闭标签:

  1. <hr />
  2. <br/>
  3. <br>

我还输入了以下标签:

  1. 一个属性
  2. 多个属性
  3. 值绑定到单引号双引号的属性
  4. 当分隔符为双引号时,属性包含单引号,反之亦然
  5. “ unpretty”属性在“ =”符号之前,之后,前后都有空格。

如果您发现上面的概念证明不起作用,我可以分析代码以提高我的技能。

<EDIT> 我忘记了用户的问题是避免解析自动关闭标签。在这种情况下,模式变得更简单了,变成了这样:

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

用户@ridgerunner注意到该模式不允许未加引号的属性没有值的属性。在这种情况下,微调为我们带来了以下模式:

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</ EDIT>

了解模式

如果有人有兴趣了解有关该模式的更多信息,请提供以下内容:

  1. 第一个子表达式(\ w +)与标记名称匹配
  2. 第二个子表达式包含属性的模式。由以下人员组成:
    1. 一个或多个空格\ s +
    2. 属性的名称(\ w +)
    3. 零个或多个空格\ s *(是否可能,此处留空)
    4. “ =”符号
    5. 同样,零个或多个空格
    6. 属性值的定界符,单引号或双引号('|“)。在模式中,单引号被转义,因为它与PHP字符串定界符重合。此子表达式用括号捕获,因此可以引用再次解析属性的关闭,这就是为什么它非常重要的原因。
    7. 属性的值,几乎与所有内容匹配:(。*?); 在这种特定的语法中,使用贪婪的匹配(星号后的问号),RegExp引擎启用了类似于“ look-ahead”的运算符,该运算符可以匹配此子表达式之后的内容
    8. 有趣的是:\ 4部分是向后引用运算符,它引用模式中之前定义的子表达式,在这种情况下,我指的是第四个子表达式,这是找到的第一个属性定界符
    9. 零个或多个空格\ s *
    10. 属性子表达式在此结束,并以星号指定零个或多个可能出现的次数。
  3. 然后,由于标签可能以“>”符号前的空格结尾,因此零个或多个空格与\ s *子模式匹配。
  4. 要匹配的标签可以以简单的“>”符号或可能的XHTML闭包结尾,该闭包使用其前的斜杠((||>))。当然,由于斜杠与正则表达式定界符一致,因此已被转义。

小提示:为了更好地分析此代码,有必要查看生成的源代码,因为我没有提供任何HTML特殊字符转义。


12
与属性没有值(即)的有效标签不匹配<option selected>。也不匹配带有未引用属性值(即)的有效标签<p id=10>
ridgerunner 2011年

1
@ridgerunner:非常感谢您的评论。在这种情况下,模式必须更改一点:$ pattern ='/ <(\ w +)(\ s +(\ w +)(\ s * \ = \ s *(\'|“ |)(。*?)\\ 5 \ s *)?)* \ s *> /';我对它进行了测试,并且可以在未引用属性或无值属性的情况下使用
Emanuele Del Grande

标签名称前的空格如何:< a href="http://wtf.org" >我很确定它是合法的,但是您不匹配它。
弗洛里斯

7
抱歉,标记名前的空格是非法的。除了“非常确定”之外,您为什么不提供一些反对的证据?这是我的,w3.org/TR/xml11/#sec-starttags指的是XML 1.1,您可以在HTML 4、5和XHTML上找到相同的名称,因为如果进行测试,W3C验证也会警告您。像周围的许多其他blah-blah-poets一样,我仍然没有收到任何明智的论据,除了我的答案有几百个负号之外,还不能证明我的代码根据问题中指定的合同规则在哪里失败。我只欢迎他们。
2013年

@ridgerunner当然,您的评论很聪明,很受欢迎。
2013年

91

每当需要快速从HTML文档中提取内容时,我都会使用Tidy将其转换为XML,然后使用XPath或XSLT来获取所需的内容。您的情况是这样的:

//p/a[@href='foo']

89

我以前使用过一个名为HTMLParser的开源工具。它旨在以各种方式解析HTML,并且可以很好地达到目的。它可以将HTML解析为不同的treenode,并且您可以轻松地使用其API从节点中获取属性。检查一下,看看是否可以帮到您。


84

我喜欢用正则表达式解析HTML。我不会尝试解析故意损坏的白痴HTML。这段代码是我的主要解析器(Perl版):

$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print

它称为htmlsplit,将HTML分成几行,每行上有一个标记或文本块。然后可以使用其他文本工具和脚本(例如grepsed,Perl等)进一步处理这些行。我什至不开玩笑:)享受。

如果您希望处理巨大的网页,那么很简单就可以将我的Slurp-Perthing-first-Perl脚本重新调整为不错的流式处理。但这不是真的必要。

我敢打赌我会为此而感到失望。

HTML分割


出乎我的意料,这引起了一些反对,所以我将建议一些更好的正则表达式:

/(<.*?>|[^<]+)\s*/g    # get tags and text
/(\w+)="(.*?)"/g       # get attibutes

它们对于XML / XHTML很有用。

只需稍作改动,它就可以处理杂乱的HTML ...或先转换HTML-> XHTML。


编写正则表达式的最佳方法是使用Lex / Yacc样式,而不是不透明的单行代码或带有注释的多行怪物。我还没有在这里做;这些人几乎不需要它。


35
“我不会尝试解析故意损坏的白痴HTML。” 您的代码如何知道差异?
凯文·潘科

嗯,HTML是否损坏并不重要。它仍然会将HTML分为标签和文本。唯一可能造成问题的是,如果人们在文本或属性中包含未转义的<或>字符。实际上,我的HTML拆分器效果很好。我不需要一个充满启发式的庞然大物。简单的解决方案并不适合每个人...!
山姆·沃特金斯

我添加了一些更简单的正则表达式来提取XML / XHTML的标签,文本和属性。
山姆·沃特金斯

(获取属性错误1)/(\w+)="(.*?)"/假定双引号。它将丢失单引号中的值。在html版本4和更早的版本中,如果它是一个简单的单词,则允许不带引号的值。
David Andersson

(获取属性错误2)/(\w+)="(.*?)"/可能会错误地匹配看起来像某个属性中某个属性的文本,例如<img title="Nope down='up' for aussies" src="..." />。如果在全球范围内应用,它将与普通文本或html注释中的此类内容匹配。
David Andersson

74

这是一个基于PHP的解析器,它使用一些不合适的正则表达式解析HTML。作为该项目的作者,我可以告诉您可以用正则表达式解析HTML,但是效率不高。如果您需要服务器端解决方案(就像我为wp-Typography WordPress插件所做的那样),则此方法有效。


1
htmlawed是另一个PHP项目,它分析HTML进行过滤,转换等。如果您能弄清楚的话,有一些不错的代码!
user594694 2011年

不,您无法使用正则表达式解析HTML。但是对于某些子集,它可能会起作用。
mirabilos

71

有两种使用BB代码替换HTML一些漂亮的正则表达式在这里。对于所有反对者,请注意,他并没有完全解析HTML,只是为了对其进行清理。他可能有能力杀死他简单的“解析器”无法理解的标签。

例如:

$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;

if (!$query->param("ascii")) {
    $html =~ s/\s\s+/\n/gi;
    $html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}

$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;

$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;

$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;

$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;

$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;

15
不要这样 请。
maletor'sep

68

关于用于解析(x)HTML的RegExp方法的问题,对于所有谈到某些限制的人的答案是:您没有受过足够的训练来统治这种强大武器的力量,因为NOBODY在这里谈到了递归

一位与RegExp无关的同事通知了我这个讨论,这肯定不是网络上关于这个古老而又热门话题的第一个讨论。

阅读了一些帖子之后,我要做的第一件事是在该线程中寻找“?R”字符串。第二个是搜索“递归”。
不,圣牛,没有找到匹配的东西。
由于没有人提到解析器所基于的主要机制,所以我很快意识到没人明白这一点。

如果(x)HTML解析器需要递归,则没有递归的RegExp解析器不足以实现此目的。这是一个简单的构造。

RegExp黑色技巧很难掌握,因此也许在尝试和测试我们的个人解决方案时,有更多的可能性被我们遗漏了,以便用一只手捕获整个网络……嗯,我敢肯定:)

这是魔术图案:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

去尝试一下。
它以PHP字符串编写,因此“ s”修饰符使类包含换行符。
这是我在一月份写的PHP手册示例注释参考

(请注意,在该注释中,我错误地使用了“ m”修饰符;尽管未使用^或$定位符,但它已被RegExp引擎丢弃,但应将其删除)。

现在,我们可以从更明智的角度谈谈这种方法的局限性:

  1. 根据RegExp引擎的具体实现,递归解析的嵌套模式数量可能有所限制,但这取决于所使用的语言
  2. 尽管损坏的(x)HTML不会导致严重错误,但尚未清理

无论如何,这只是一个RegExp模式,但它揭示了开发许多强大实现的可能性。
我编写了这种模式来增强我在框架中构建的模板引擎的递归下降解析器的性能,无论是在执行时间还是在内存使用方面,其性能都非常好(与使用相同语法的其他模板引擎无关)。


35
我将其放在“不允许大于属性的Regex”容器中。根据<input value =“ is 5> 3?”进行检查。/>
Gareth

68
如果在生产代码中放入类似内容,维护人员很可能会开枪。陪审团永远不会对他定罪。
aehiilrs

30
正则表达式不起作用,因为根据定义,它们不是递归的。在正则表达式中添加递归运算符基本上会使CFG仅具有较差的语法。为什么不使用最初设计为递归的东西,而不是将递归强行插入已经具有多余功能的东西呢?
Welbog

16
我的反对不是功能之一,而是投入的时间之一。RegEx的问题在于,当您发布Cutsey小衬板时,您似乎做得更有效(“请看一行代码!”)。当然,没有人提到他们在备忘单上花费的半小时(或3个小时),并(希望)测试输入的每种可能排列。一旦您克服了所有这些,当维护人员去弄清楚或验证代码时,他们就不能仅仅查看它并认为它是正确的。必须剖析该表达式,并从本质上再次对其进行重新测试……
Oorang 2010年

15
...知道这很好。这会发生,甚至与人谁是很好用正则表达式。老实说,我怀疑绝大多数人不会很了解。因此,您采取了最臭名昭著的维护噩梦之一,并将其​​与递归结合起来,这又是另一个维护噩梦,我认为我对我的项目真正需要的是一个不太聪明的人。我们的目标是编写可以让不良程序员在不破坏代码基础的情况下维护的代码。我知道编码最小公分母很容易。但是聘请优秀的人才很难,而且您经常...
Oorang

62

正如许多人已经指出的那样,HTML不是一种常规语言,因此很难解析。我的解决方案是使用整洁的程序将其转换为常规语言,然后使用XML解析器使用结果。为此有很多不错的选择。我的程序是使用Java和jtidy库编写的,将HTML转换为XML,然后通过Jaxen将xpath转换为结果。


61
<\s*(\w+)[^/>]*>

零件说明:

<:起始字符

\s*:标记名称前可能有空格(难看但可能)。

(\w+):标签可以包含字母和数字(h1)。好吧,\w也匹配“ _”,但是我猜并没有伤害。如果好奇,请改用([a-zA-Z0-9] +)。

[^/>]*:除了东西>/直至收盘>

>:关闭 >

无关

对于那些低估正则表达式的人来说,它们仅与正则语言一样强大:

可以将非常规的,甚至不是上下文无关的n ba n ba n^(a+)b\1b\1$

反引用FTW


@GlitchMr,那是他的观点。现代正则表达式在技术上不是正规的,也没有任何理由。
alanaktion

3
@alanaktion:“现代”正则表达式(读取:具有Perl扩展名)不能匹配O(MN)(M为正则表达式长度,N为文本长度)。反向引用是造成这种情况的原因之一。awk中的实现没有反向引用,并且可以在O(MN)时间内匹配所有内容。
Konrad Borowski13年

56

如果您只是想查找这些标签(没有解析的野心),请尝试以下正则表达式:

/<[^/]*?>/g

我在30秒内写下了它,并在这里进行了测试:http//gskinner.com/RegExr/

它与您提到的标签类型匹配,而忽略了您想忽略的标签类型。


2
我想你的意思\/>不是\\>
贾斯汀·摩根

不,这\>就是我的意思;我从未打算编辑原始帖子的正则表达式。
朗尼·贝斯特

2
仅供参考,您不需要使用尖括号。当然,无论如何逃脱它们都没有害处,但是请注意您可以避免的混乱。;)
艾伦·摩尔

当我不确定某些东西是否具有特殊性时,有时我会不必要地逃避。我已经编辑了答案;它的工作原理相同,但更为简洁。
Lonnie Best,

现在看这个,我不知道我为什么认为你的意思\/,因为那样做与要求完全相反。也许我以为您提供的是负过滤模式。
贾斯汀·摩根

54

在我看来,您正在尝试匹配末尾不带“ /”的标签。尝试这个:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>

8
这是行不通的。对于输入'<xa =“ <b>” /> <y>',匹配项是x和y,尽管x终止了。
2011年

51

的确,在编程时,处理HTML时通常最好使用专用的解析器和API而不是正则表达式,尤其是在精度至关重要的情况下(例如,如果处理可能会带来安全隐患)。但是,我不认为教条认为XML样式的标记永远不应该使用正则表达式来处理。在某些情况下,正则表达式是完成这项工作的理想工具,例如,在文本编辑器中进行一次性编辑,修复损坏的XML文件或处理看起来像但不完全是XML的文件格式时。有一些问题需要注意,但并非不可克服,甚至不一定是相关的。

<([^>"']|"[^"]*"|'[^']*')*>在我刚刚提到的情况下,一个简单的正则表达式通常就足够了。考虑到所有因素,这是一个幼稚的解决方案,但是它确实允许>属性值中使用未编码的符号。如果您要查找的是table标签,则可以将其修改为</?table\b([^>"']|"[^"]*"|'[^']*')*>

只是为了了解更“高级”的HTML正则表达式,以下代码在模拟真实浏览器行为和HTML5解析算法方面做得相当不错:

</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)

以下内容符合XML标记的相当严格的定义(尽管它不能解决XML名称中允许的完整Unicode字符集):

<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>

当然,这些并不能说明周围的环境和一些极端情况,但是即使您确实愿意,也可以处理此类事情(例如,通过在另一个正则表达式的匹配项之间进行搜索)。

在一天结束时,即使是正则表达式,也要使用最合适的工具来完成工作。


49

尽管为此目的使用正则表达式不适当且无效,但有时正则表达式可以为简单的匹配问题提供快速解决方案,并且在我看来,对于琐碎的工作使用正则表达式并不那么麻烦。

一篇关于匹配最里面的HTML元素的权威博客文章,这些元素由Steven Levithan撰写。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.