正则表达式匹配P标签的继承

时间:2012-08-28 04:50:20

标签: php javascript regex pattern-matching web-scraping

这是一个我一直在努力的有趣小事。我找到了很多解决方案,但没有一个真的是正确的匹配。目标是“仅当连续3个或更多时匹配p标签”

所以我觉得这应该是正确的,但事实并非如此。

<p.*>(.*)<\/p>(?=\s?<p){3,}

基本上我的话说:

  • 将p标记与标记内的任何内容匹配
  • 匹配任何内容,直到您看到结束P标记
  • 仅匹配前面的(2行以上)iff后跟
    • 一个空白字符(可能),然后是&lt; P
    • 如果发生3次或更多次

问题是这在Javascript中运行良好,但在PHP中运行不正常。 PHP说

Compilation failed: nothing to repeat at offset 28

我已经尝试了不同轮次的parens给它“没有什么可重复”,但这会导致错误的正则表达式。

是的,这是针对网络抓取的,但不是我做研究而不做恶事。

可能有什么想法? 谢谢!

3 个答案:

答案 0 :(得分:1)

状态机XML解析器(SAX解析器)似乎最适合我。这是一个例子:

class StateHelper {

    function __construct($filename) {
        $this->p_count = 0;
        $this->p_elements = array();
        $this->in_p = FALSE;
        $this->minimum_in_succession = 2;
        $this->successive_element_data = array();
        $parser = xml_parser_create();
        xml_set_element_handler($parser, array($this, 'start_element'), NULL);
        xml_set_character_data_handler($parser, array($this, 'character_data'));

        $fp = fopen($filename, 'r')
            or die ("Cannot open $filename");

        while ($data = fread($fp, 4096)) {
            xml_parse($parser, $data, feof($fp)) or 
                die(sprintf('XML ERROR: %s at line %d',
                xml_error_string(xml_get_error_code($parser)),
                xml_get_current_line_number($parser)));
        }
        xml_parser_free($parser);
        $this->start_element(NULL, "end", NULL);
    }

    function start_element($parser, $element_name, $element_attrs) {
        if ($element_name == 'P') {
            $this->p_count += 1;
            $this->in_p = TRUE;
        } else {
            if ($this->p_count >= $this->minimum_in_succession) {
                $this->successive_element_data[] = $this->p_elements;
            }
            $this->p_elements = array();
            $this->p_count = 0;
            $this->in_p = FALSE;
        }
    }

    function character_data($parser, $data) {
        if ($this->in_p && strlen(trim($data))) {
            $this->p_elements[] = $data;
        }
    }
}

$parseState = new StateHelper("example.html");
print_r($parseState->successive_element_data);

<强> example.html的*

<html>
    <head>
    </head>
    <body>
        <p>Foo1</p>
        <p>Foo2</p>
        <p>Foo3</p>
        <div>
            <p>Bar1</p>
            <p>Bar2</p>
        </div>
        <ul>
            <li>
                <p>Baz1</p>
                <p>Baz2</p>
                <p>Baz3</p>
                <p>Baz4</p>
            </li>
        </ul>
    </body>
</html>

<强>输出

Array
(
    [0] => Array
        (
            [0] => Foo1
            [1] => Foo2
            [2] => Foo3
        )

    [1] => Array
        (
            [0] => Baz1
            [1] => Baz2
            [2] => Baz3
            [3] => Baz4
        )

)

答案 1 :(得分:0)

PHP很可能会给你这个错误,因为你的零宽度断言无法重复,perl和javascript都没有警告你。

如果你匹配它,你可以匹配任意次数,因为它实际上并没有消耗任何东西。

根据您的意图,您可以使用正则表达式。但是,如果你需要以任何方式真正了解你的HTML,那么最好使用HTML解析库。

你需要做什么?

答案 2 :(得分:0)

为什么不使用XPath?那么表达式就是:

//p[name(following-sibling::*[1]) = 'p' and name(following-sibling::*[2]) = 'p']

查询会在文档中的任何位置找到所有p,其中紧随其后有两个p

示例(demo):

$html = <<< HTML
<div>
    <p>lore</p>
    <p>ipsum</p>
    <p>dolor</p>
    <br/>
    <p>sit</p>
    <p>amet</p> 
</div>
HTML;

我们只想找到此代码段中的第一个元素。那么代码就是:

$query = "//p[
    name(following-sibling::*[1]) = 'p' and 
    name(following-sibling::*[2]) = 'p'
]";

print_r(xpath_match_all($query, $html));

<强>输出:

Array(
    [0] => Array(
        [0] => <p>lore</p>
    )
    [1] => Array(
        [0] => lore
    )
)

结果数组包含该查询的outerHTML和innerHTML。

当然,您不必使用xpath_match_all功能。这只是一个便利工具。有关替代方案,请参阅How do you parse and process HTML/XML in PHP?