这是一个我一直在努力的有趣小事。我找到了很多解决方案,但没有一个真的是正确的匹配。目标是“仅当连续3个或更多时匹配p标签”
所以我觉得这应该是正确的,但事实并非如此。
<p.*>(.*)<\/p>(?=\s?<p){3,}
基本上我的话说:
问题是这在Javascript中运行良好,但在PHP中运行不正常。 PHP说
Compilation failed: nothing to repeat at offset 28
我已经尝试了不同轮次的parens给它“没有什么可重复”,但这会导致错误的正则表达式。
是的,这是针对网络抓取的,但不是我做研究而不做恶事。
可能有什么想法? 谢谢!
答案 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?