【问题标题】:Funny behavior with html paragraph tags带有 html 段落标签的有趣行为
【发布时间】:2012-08-18 18:36:42
【问题描述】:
$regex = '#<p.+</p>#s';

我的目标是返回出现在第一个段落标记和最后一个段落标记之间的大字符串。这是包括所有内容,甚至是其他段落。

我上面的正则表达式适用于除段落标签之外的所有内容。我测试了它用'html'替换'p'并返回成功,替换为'script'并返回成功......为什么这对于这些情况会返回true而不是对于段落?

我仍在努力,并且相对确信没有奇怪的转义序列导致正则表达式停止......我认为这是因为我可以提取第一个和最后一个“html”标签之间的所有内容。 “html”标签之间的文本还包含我未能提取的所有“p”标签。如果存在某种转义或错误,我认为在提取“html”标签时也会引发相同的错误。我试过 preg_quote() 没有成功。

也许我需要将专用于正则表达式处理的内存设置得更高,以便它可以处理整个文档?

更新:在大多数情况下,前导“p”将(在大多数情况下)不是同一段落标签的结束“/p”标签。

更新:返回的结果类似于:

<p>this is the first tag</p>this is a bunch of text from the document, could be all manner of tags <p>this is the last paragraph tag</p>

更新:代码示例

$htmlArticle = <<< 'ENDOFHTML'

Insert data from pastebin here
http://pastebin.com/4A3FYGc8

ENDOFHTML;

$pattern = '#<html.+/html>#s'; // Works fine, returns all characters between first <html and last /html
$pattern = '#<script.+/script>#s'; // Works fine, same as above
$pattern = '#<p.+/p>#s'; // Returns nothing, nothing at all. :'(

preg_match($pattern, $htmlArticle, $matches);

var_dump($matches);

?>

解决方案: ini_set('pcre.backtrack_limit', '1000000');

我已经用尽了我的回溯限制。这是 php.ini 文件中的设置,可以使用 ini_set() 在代码中设置。奇怪的是,我用 ini_set() 设置了值以匹配我的 php.ini 文件中的值......所以它应该从一开始就工作。 --- 感谢您尽快发布解决方案。

【问题讨论】:

  • 我不想将 html 解析为有意义的值。我只想提取第一次出现的“

    ”之间的任何文本。例如,我将对返回的文本进行哈希处理,并使用它来加密某些内容。返回的文本到底是什么,我不关心,只要我得到它。

  • 除非您提供实际的详细信息,否则您不会收到有意义的答案。比如实际的匹配结果是什么。
  • 在大多数情况下,前导“

    ”将(在大多数情况下)不是同一段落标签的结束“

    ”标签。
  • 也许你需要再解释一下。结果正是您的正则表达式应该返回的结果,所有内容都在文档末尾的第一个 &lt;p&gt;&lt;/p&gt; 之间,或者不是?

标签: php html regex string


【解决方案1】:

这很好奇。它没有返回错误,并且使用较短的文档似乎会返回匹配项。我不明白为什么会发生这种情况。我已经在大量文档上使用了正则表达式,没有遇到任何问题。

请注意,这会产生匹配:#&lt;p\b.+&lt;\#s

也许尝试使用backtrack limit,因为有很多&lt;/p&gt; 匹配项。但是,如果限制太低,我希望 preg_match 返回 False,而不是 0!

作为一种解决方法,请尝试以下方法:

function extractBetweenPs($data) {
$startoffset = null;
$endoffset = null;
if (preg_match('/<p\b/', $data, $matches, PREG_OFFSET_CAPTURE)) {
    $startoffset = $matches[0][1];
    $needle = '</p>';
    $endoffset = strrpos($data, $needle);
    if ($endoffset !== FALSE) {
        $endoffset += strlen($needle);
    } else {
        // this will return everything from '<p' to the end of the doc
        // if there is no '</p>'
        // maybe not what you want?
        $endoffset = strlen($data);
    }
    return substr($data, $startoffset, $endoffset-$startoffset);
}
return '';
}

也就是说,这是一个非常奇怪的要求——将结构化文档的任意部分视为一个 blob。也许您可以退后一步,说出您更广泛的目标是什么,我们可以建议另一种方法?

【讨论】:

  • 哇!谢谢你。我的下一个重要步骤(失败后)是写一些能做到这一点的东西。我会使用你的代码,可能会做一些修改。
  • 至于我的目的:我正在开发一种从各种互联网站点抓取相关内容的流程。我计划将这些存档以捕捉“您生日时的互联网”,并向用户展示“他们生日时网络空间发生了什么”。这是一个有趣的小想法,让我遇到了这个正则表达式问题。
  • 看起来 preg_match 会返回一个真实的答案,即使回溯限制已用尽。那是我的问题,现在解决了。奇怪的是,我在代码中使用 set_ini() 将回溯限制设置为与 php.ini 文件中相同的值......在代码中重新设置它使其工作。
  • 你抓取的段的问题是它无论如何都不是有效的 html 文档。例如:&lt;div&gt;&lt;p&gt;para1&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;para2&lt;/p&gt;&lt;/div&gt; 匹配 &lt;p&gt;para1&lt;/p&gt;&lt;/div&gt;&lt;div&gt;&lt;p&gt;para2&lt;/p&gt;。也许改为使用 dom 来捕获第一个和最后一个 P 元素之间的所有节点。这至少是有效的,不会切断元素。
【解决方案2】:

Regex 不是一个可以用来正确解析 HTML 的工具。

您只需要DOMDocument

$dom = new DOMDocument();
$dom->loadHTML($your_html);
$node = $dom->getElementsByTagName('p')->item(0);
$dom2 = new DOMDocument();
$node = $dom2->importNode($node, true);
$dom2->appendChild($node);
echo $dom2->saveHTML();

【讨论】:

  • 感谢您的回复,但我不想单独解析标签。我希望在第一次出现“

    ”之间获取全局,即使文档中有多个段落标签。我过去曾使用过 DOMDocument,但它不适用于我打算做的事情。

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 2016-05-17
  • 2011-02-13
  • 1970-01-01
  • 1970-01-01
  • 2018-08-19
  • 1970-01-01
相关资源
最近更新 更多