REGEX:从HTML文档中提取可读(非编码)文本和URL
题
我正在创建一个将URL作为输入的应用程序,从网络上检索页面的HTML内容并提取 标签中不包含的一切. 。换句话说,该页面的访问者可以看到页面的文本内容。其中包括“掩盖”所有封装的一切 <script></script>
, <style></style>
和 <!-- -->
, ,由于这些部分包含未包裹在标签中的文本(但最好是单独使用)。
我已经构造了这个正则:
(?:<(?P<tag>script|style)[\s\S]*?</(?P=tag)>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>)
它正确选择了我要忽略的所有内容,并且只剩下页面的文本内容。但是,这意味着我要提取的内容不会在比赛集合中显示(我正在Visual Studio 2010中使用VB.NET)。
是否有一种方法可以“反转”这样的整个文档的匹配,以便我可以在上述正则匹配中忽略的所有文本字符串上匹配?
到目前为止,我所做的是在末尾添加另一个替代方案,该替代方法选择“任何不包含<或>的序列”,这意味着剩余的文本。我在一个捕获组中命名了最后一个位,当我迭代比赛时,我检查了“文本”组中是否存在文本。这起作用了,但是我想知道是否可以通过Regex和 只是 最终在纯文本上进行匹配。
这应该一般起作用,而不知道HTML中的任何特定标签。应该提取 全部 文本。此外,我需要保留原始的HTML,以便页面保留其所有链接和脚本 - 我只需要能够提取文本,以便我可以在其中执行搜索和替换,而不必担心“重命名”任何标签,属性,属性或脚本变量等(因此,我不能在所有的比赛中做任何“无替换”,因为即使我剩下我的需求,也要将其重新插入回到正确的位置,这很麻烦功能齐全的文档)。
我想知道是否可以使用Regex(我知道HTML Agility Pack和XPath,但不喜欢)。
有什么建议么?
更新:这是我最终得到的(基于正则的)解决方案: http://www.martinwardener.com/regex/, ,在演示Web应用程序中实现,该应用程序将显示Active Regex字符串以及测试引擎,该引擎使您可以在任何在线HTML页面上运行解析,从以及在完整的HTML文档中突出显示所有正则匹配的视图)。
解决方案 5
好,所以这就是我的做法:
使用我的原始正则(添加了纯文本的搜索模式),这恰好是标签搜索完成后剩下的任何文本):
(?:(?:<(?P<tag>script|style)[\s\S]*?</(?P=tag)>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>))|(?P<text>[^<>]*)
然后在vb.net中:
Dim regexText As New Regex("(?:(?:<(?<tag>script|style)[\s\S]*?</\k<tag>>)|(?:<!--[\s\S]*?-->)|(?:<[\s\S]*?>))|(?<text>[^<>]*)", RegexOptions.IgnoreCase)
Dim source As String = File.ReadAllText("html.txt")
Dim evaluator As New MatchEvaluator(AddressOf MatchEvalFunction)
Dim newHtml As String = regexText.Replace(source, evaluator)
文本的实际替换在这里发生:
Private Function MatchEvalFunction(ByVal match As Match) As String
Dim plainText As String = match.Groups("text").Value
If plainText IsNot Nothing AndAlso plainText <> "" Then
MatchEvalFunction = match.Value.Replace(plainText, plainText.Replace("Original word", "Replacement word"))
Else
MatchEvalFunction = match.Value
End If
End Function
瞧。 newHtml
现在包含原始的精确副本,除了页面中的每一次“原始单词”(如浏览器中显示)都用“替换单词”进行切换,并且所有HTML和脚本代码均未触及。当然,可以 /可以 /可以更精细的替代程序,但这表明了基本原则。这是12行代码,包括函数声明和HTML代码等的加载。我对看到并行解决方案非常感兴趣,在DOM等中完成(是的)(是的,我知道可以通过 肯定 某些嵌套标签怪癖的出现 - 在脚本重写中 - 但是,如果有的话,损坏仍然非常有限(请参阅上面的某些评论),总的来说,这将很好地完成这项工作)。
其他提示
我所做的是最后添加另一个替代方案,以选择“不包含的任何序列
<
或者>
”,这意味着剩余的文字。我在捕获组中命名了最后一个位,当我迭代匹配项时,我检查了“文本”组中是否存在文本。
那就是通常会做的。甚至更简单,用和空字符串替换标记模式的每场比赛,而您剩下的就是您要寻找的东西。
这是有效的,但是这里和那里似乎有一根弦,不应该被捡起。
好吧,那是因为您的表情以及一般而言是不足以解析有效的HTML,更不用说真正网络上出现的恐怖了。要查看的第一个提示,如果您真的想追逐这种徒劳的方法:属性值(以及通常的文本内容)可能包含一个unscaped >
特点。
我想再次提出HTML敏捷包的好处。
ETA:由于您似乎想要它,所以这里有一些标记的示例,看起来像是您的表达方式。
<a href=link></a> - unquoted
<a href= link></a> - unquoted, space at front matched but then required at back
<a href="~/link"></a> - very common URL char missing in group
<a href="link$!*'link"></a> - more URL chars missing in group
<a href=lïnk></a> - IRI
<a href
="link"> - newline (or tab)
<div style="background-image: url(link);"> - unquoted
<div style="background-image: url( 'link' );"> - spaced
<div style="background-image: url('link');"> - html escape
<div style="background-image: ur\l('link');"> - css escape
<div style="background-image: url('link\')link');"> - css escape
<div style="background-image: url(\
'link')"> - CSS folding
<div style="background-image: url
('link')"> - newline (or tab)
这只是完全有效的标记 惯于 匹配正确的链接,没有任何可能的无效标记,不应该匹配链接的标记,或者与您从文本分配标记的其他技术中的许多问题中的任何一个。这是冰山一角。
Regex对于检索HTML文档的文本内容并不可靠。正则无法处理嵌套标签。假设文档不包含任何嵌套标签,Regex仍然需要每个标签都正确关闭。
如果您使用的是PHP,为简单起见,我强烈建议您使用DOM(文档对象模型)来解析/提取HTML文档。 DOM库通常存在于每种编程语言中。
如果您要提取不匹配正则符合的字符串的部分,则可以简单地替换 是 与一个空字符串匹配以达到相同的效果。
请注意,这可能起作用的唯一原因是因为您有兴趣删除的标签, <script>
和 <style>
标签,不能嵌套。
但是,这并不少见 <script>
标记以包含代码以编程性附加另一个 <script>
标签,在这种情况下,您的正则是失败的。在任何标签未正确关闭的情况下,它也会失败。
您不能用正则表达式解析HTML。
用正则表达式解析HTML导致悲伤。
我知道您只是为了娱乐而做,但是那里的包裹比实际解析正确的方法,并可以可靠地进行测试。
不要重新发明轮子,而是这样做几乎可以保证使您沮丧的方式。
供您参考,
而不是与jQuery相比,可以从HTML标记中单独提取文本。为此,您可以使用以下模式。
$("<div/>").html("#elementId").text()
你可以参考这个 JSFIDDLE