代码之家  ›  专栏  ›  技术社区  ›  Factor Mystic

如何确定HTML标记是否跨多行拆分

  •  3
  • Factor Mystic  · 技术社区  · 16 年前

    我正在编写一个涉及到抓取网页的PHP脚本。当前,脚本逐行分析页面,但如果有一个标记跨越多行,则它将中断,例如

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

    如果情况更糟,我可以通过删除所有换行符来预处理页面,然后在最近的位置重新插入它们。 > 但这看起来像是一个拼凑。

    理想情况下,我可以检测到一个跨越行的标记,只将那些标记连接到行,然后继续处理。
    那么,最好的检测方法是什么?

    6 回复  |  直到 5 年前
        1
  •  7
  •   Jörg W Mittag    16 年前

    这是我的一个宠物尿: 从未 手工分析HTML。 从未 用regexps分析HTML。 从未 使用字符串比较分析HTML。 总是 使用HTML解析器来解析HTML,这就是它们的目的。

    我已经很久没有做过任何PHP了,但是一个快速搜索出现了 this PHP5 HTML parser .

        2
  •  2
  •   Josh    16 年前

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

        3
  •  1
  •   Jon Limjap    16 年前

    嗯,这并不能回答问题,更像是一种观点,但是…

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

    有两种类型的课程:

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

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

        4
  •  1
  •   Factor Mystic    16 年前

    也许在将来的项目中,我会使用解析库,但这与手头的问题无关。这是我目前的解决方案。 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);
        }
    }
    

    这可能在某种程度上得到优化,但就我的目的而言,这已经足够了。

        5
  •  0
  •   corymathews    16 年前

    为什么不在一行中读取,并将其设置为一个字符串,然后检查该字符串是否有标记的开头和结尾,如果标记跨越多行,则将下一行添加到字符串中,并将开头大括号之前的部分移动到处理过的字符串中。然后通过整个文件进行解析。它不漂亮,但应该管用。

        6
  •  0
  •   ceejayoz    16 年前

    如果您必须坚持当前的解析方法,并且它是一个regex,则可以使用 multi-line flag “m”跨多条线。