【问题标题】:How to exclude table rows that contain specific strings between start and end tag from matching?如何从匹配中排除包含开始和结束标记之间特定字符串的表行?
【发布时间】:2011-03-02 22:53:24
【问题描述】:

上下文 该案例是使用 QuotaXML SDK 1.6 对 Web 内容进行屏幕抓取,最终在仪表板和 iPhone 上显示数据。 这个 QuotaXML 工具提供了仅用于提取表数据的正则表达式。 QuotaXML 确实使用三步方法解析 html 表。 1.首先识别表,例如使用“(?si)<table.*?>(.*?)</table>” 2. 在这个解析的表中,它识别行的第二个,比如“(?si)<tr.*?>(.*?)</tr>” 3. 在此行范围内的第三个,将单个单元格标识为“(?si)<tr.*?>(.*?)</tr>

问题 源 html 包含一些不相关的数据行,例如使用 colspan 跨越整个表格宽度的行或图像。 或者表格包含与所需数据线路无关的数据单元格,例如呼叫详细记录,其中还包含未从您的计划中的分钟数中减去的免费电话的呼叫,在本例中为 0800 和 00800 号码。 换句话说,(.*?) 可能不匹配 'colspan="' 和 '>0800' 和 '>00800'。

在代码中:

exclude:<tr><td colspan="2"></td></tr>
include:<tr><td><strong>Date</strong></td><td><strong>Time</strong></td></tr>
exclude:<tr><td>05-01-2011</td><td>08004913</td></tr>
include:<tr><td>05-01-2011</td><td>0123456789</td></tr>

功课完成 即使尝试我的第一个(开始简单)尝试只排除 colspan 都失败了:

  1. (?si)&lt;tr.*?&gt;(?!colspan)(.*?)&lt;/tr&gt;
  2. (?si)&lt;tr.*?&gt;(.*?)(?!colspan)&lt;/tr&gt;
  3. (?si)&lt;tr.*?&gt;.*?[^colspan].*?&lt;/tr&gt;
  4. (?si)&lt;tr(\s[^&gt;]*)?&gt;.*?(?!colspan).*?&lt;/tr&gt;
  5. (?si)&lt;tr(\s[^&gt;]*)?&gt;.*?(!colspan).*?&lt;/tr&gt;
  6. (?si)&lt;tr(\s[^&gt;]*)?&gt;(.*?)(?!colspan)&lt;/tr&gt;
  7. (?si)&lt;tr.*?&gt;^(?!.*?colspan=").*?&lt;/tr&gt; How to negate specific word in regex? 似乎相关,尽管这些建议根本不匹配。
  8. (?si)&lt;tr.*?&gt;(.(?&lt;!colspan))*?&lt;/tr&gt;
  9. (?si)&lt;tr.*?&gt;(?!.*colspan).*&lt;/tr&gt; 也不要使用http://www.regular-expressions.info/lookaround.html 提供正面和负面的环顾四周。

我应该如何正确编写这个正则表达式?

【问题讨论】:

  • 只是不要用正则表达式解析 HTML:stackoverflow.com/questions/1732348/…
  • 我知道我不应该使用正则表达式解析 HTML。正如问题中所解释的,该工具除了使用正则表达式之外没有提供其他选项。

标签: html regex


【解决方案1】:

您遇到的第一个问题是您的原始表达式非常脆弱,因为“。*?>”旨在匹配最早的“>”-但实际上将匹配以下“ >" 如果表达式的其余部分失败并回溯。

请改用“[^>]*>”之类的结构。

第二个问题是您误解了负前瞻的含义:它不是检查给定模式是否出现在其位置之前 - 它是在其位置之前查看模式是否出现在 AT那个位置。

通过这两个更改,您的第一次尝试非常接近解决您的测试用例:

$ pcretest
PCRE version 7.8 2008-09-05

  re> /(?si)<tr[^>]*>(?!.*(colspan|>0?0800))(.*?)<\/tr>/
data> <tr><td colspan="2"></td></tr>
No match
data> <tr><td><strong>Date</strong></td><td><strong>Time</strong></td></tr>
 0: <tr><td><strong>Date</strong></td><td><strong>Time</strong></td></tr>
 1: <unset>
 2: <td><strong>Date</strong></td><td><strong>Time</strong></td>
data> <tr><td>05-01-2011</td><td>08004913</td></tr>
No match
data> <tr><td>05-01-2011</td><td>0123456789</td></tr>
 0: <tr><td>05-01-2011</td><td>0123456789</td></tr>
 1: <unset>
 2: <td>05-01-2011</td><td>0123456789</td>

请注意,这仍然无法解决整个问题,因为字符串后面出现“colspan”或 800 数字会阻止匹配。您需要更多的测试用例,例如:

$ pcretest
PCRE version 7.8 2008-09-05

  re> /(?si)<tr[^>]*>(?!.*(colspan|>0?0800))(.*?)<\/tr>/
data> <tr><td>05-01-2011</td><td>0123456789</td></tr><tr><td colspan="2"></td></tr>
No match

所以你需要确保负前瞻永远不会跨越到下一个:

$ pcretest
PCRE version 7.8 2008-09-05

  re> /(?si)<tr[^>]*>(?!((?!<\/tr).)*(colspan|>0?0800))(.*?)<\/tr>/
data> <tr><td colspan="2"></td></tr>
No match
data> <tr><td><strong>Date</strong></td><td><strong>Time</strong></td></tr>
 0: <tr><td><strong>Date</strong></td><td><strong>Time</strong></td></tr>
 1: <unset>
 2: <unset>
 3: <td><strong>Date</strong></td><td><strong>Time</strong></td>
data> <tr><td>05-01-2011</td><td>08004913</td></tr>
No match
data> <tr><td>05-01-2011</td><td>0123456789</td></tr>
 0: <tr><td>05-01-2011</td><td>0123456789</td></tr>
 1: <unset>
 2: <unset>
 3: <td>05-01-2011</td><td>0123456789</td>
data> <tr><td>05-01-2011</td><td>0123456789</td></tr><tr><td colspan="2"></td></tr>
 0: <tr><td>05-01-2011</td><td>0123456789</td></tr>
 1: <unset>
 2: <unset>
 3: <td>05-01-2011</td><td>0123456789</td>

此时人们可能想知道 RegExps 是否是解决这个特定问题的正确工具 :-)

【讨论】:

  • 完美,感谢您的快速和恰当的回答,甚至包括一步一步的解释。
  • 在您的帮助下,我还可以构建逆正则表达式,选择每个包含特定字符串的 ... 块。在此示例中,“短信”的匹配是通过(?si)&lt;tr[^&gt;]*&gt;(?:((?!&lt;\/tr).)*&gt;Sms&lt;\/td&gt;)(.*?)&lt;/tr&gt; 完成的
【解决方案2】:

我不知道这是否是它失败的原因,但个别单元格是 td 而不是 tr。这应该工作

(?si)<td(?!colspan)>(.*)</td>

【讨论】:

  • 这不是它失败的原因。目的是选择开始 &lt;tr&gt; 和结束 &lt;/tr&gt; 之间的所有文本,只要该 innerhtml 不包含特定的字符串模式。确切的位置(为了简化)并不重要。
  • 'Pro Backup',我得记住这个名字
  • 读完后我以为你想要的是单个单元格而不是行中的信息。如果它在一行中,那没关系,如果它在一个单元格中,它就可以了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-03-23
  • 1970-01-01
  • 1970-01-01
  • 2014-09-15
  • 2018-09-23
  • 1970-01-01
  • 2010-11-04
相关资源
最近更新 更多