3

我正在编写一个涉及抓取网页的 PHP 脚本。目前,该脚本逐行分析页面,但如果有一个跨越多行的标签,它就会中断,例如

<img src="example.jpg"
alt="example">

如果情况变得更糟,我可能会通过删除所有换行符来预处理页面,然后将它们重新插入到最接近的位置>,但这似乎是一个混乱。

理想情况下,我能够检测到跨行的标签,仅将这些标签与行结合,然后继续处理。
那么检测这种情况的最佳方法是什么?

4

6 回答 6

7

这是我最讨厌的事情之一:永远不要手动解析 HTML。永远不要使用正则表达式解析 HTML。永远不要使用字符串比较来解析 HTML。始终使用 HTML 解析器来解析 HTML——这就是它们的用途。

我已经很久没有做任何 PHP 了,但是快速搜索发现了这个 PHP5 HTML parser

于 2008-08-29T02:19:03.817 回答
2

不要编写解析器,使用别人的:DOMDocument::loadHTML - 这只是一个,我认为还有很多其他的。

于 2008-08-29T02:21:59.470 回答
1

好吧,这并不能回答问题,而是更多的意见,但是...

我认为最好的抓取策略(因此,消除这个问题)不是逐行分析 HTML,这对 HTML 来说是不自然的,而是通过其自然分隔符:<> 对来分析它。

当然会有两种类型的课程:

  • 标记立即关闭的元素,例如 <br />
  • 标记需要单独结束标记的元素,例如 <p> 文本</p>

您可以立即看到在段落(p)标记的情况下使用此策略的优势:解析多行段落将更容易,而不必跟踪结束标记的位置。

于 2008-08-29T02:16:53.290 回答
1

也许对于未来的项目,我会使用解析库,但这与手头的问题无关。这是我目前的解决方案。rstrpos是strpos,但从相反的方向。示例使用:

for($i=0; $i<count($lines); $i++)
{
    $line = handle_mulitline_tags(&$i, $line, $lines);
}

这是那个实现:

function rstrpos($string, $charToFind, $relativePos)
{
    $searchPos = $relativePos;
    $searchChar = '';

    while (($searchChar != $charToFind)&&($searchPos>-1))
    {
        $newPos = $searchPos-1;
        $searchChar = substr($string,$newPos,strlen($charToFind));
        $searchPos = $newPos;
    }

    if (!empty($searchChar))
    {
        return $searchPos;
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

function handle_multiline_tags(&$i, $line, $lines)
{
    //if a tag is opened but not closed before a line break,

    $open = rstrpos($line, '<', strlen($line));
    $close = rstrpos($line, '>', strlen($line));
    if(($open > $close)&&($open > -1)&&($close > -1))
    {
        $i++;
        return trim($line).trim(handle_multiline_tags(&$i, $lines[$i], $lines));
    }
    else
    {
        return trim($line);
    }
}

这可能会以某种方式进行优化,但就我的目的而言,这已经足够了。

于 2008-08-29T16:20:57.163 回答
0

为什么不读一行,并将其设置为字符串,然后检查字符串中的标签开头和结尾,如果标签跨越多于一行,则将下一行添加到字符串中并将部分移到左大括号之前到您处理的字符串。然后只需解析整个文件即可。它不漂亮,但它应该工作。

于 2008-08-29T02:42:35.570 回答
0

如果您必须坚持当前的解析方法,并且它是一个正则表达式,您可以使用多行标志“m”跨越多行。

于 2008-08-29T16:18:16.423 回答