网站首页php
PHP正则递归子组匹配完整html标签内容
发布时间:2019-10-04 13:04:36编辑:slayer.hover阅读(3814)
在采集网页时,经常需要按标签取内容,有时候取个小东西,似乎没必要引入phpQuery或者simple_html_dom这么重量级消耗内存的选手,
我们一条正则就完整的匹配到某html标签下的所有内容,如下:
#(<(?<appTag>\w+)\s+[^>]*?{$keywords}[^>]*?>
((\s*<(?<recTag>\w+)[^>]*/?>(?:(?>.*?(?=<\k<recTag>|</\k<recTag>>))|(?3))*</\k<recTag>>?\s*)
|\s*<[^>]*?/>\s*)*</\k<appTag>>)|(<(?<aqqTag>\w+)\s+[^>]*?{$keywords}[^>]*?>.*?</\k<aqqTag>>)
|(<[\w]+[^>]*?{$keywords}[^>]*?/>)#msix因为PHP的正则不支持平衡组,所以只能通过递归来实现了,正则表达式不要有换行。
看起来很复杂,其实这条正则是由三部分组成的:
第一块子递归匹配所有完整标签的html内容。
第二块匹配内部没有闭合标签<a...></a>的单块内容。
第三块匹配以<.../>形式表现的html标签。
其中第三个子组作为递归子组(?3)匹配到所有<recTag></recTag>内部标签内容,此语句传入keywords参数,按标签的属性值来查找并返回。
不甚理解的话,就拿下面的示例代码跑一遍就明白了 : )
<?php
$html = <<<EOF
<div class="text_d right" id="navHide">
<img src="linker0" />
<a rel="nofollow" href="javascript:void(0);">导航菜单 <i id="icon" class="sprite arrowRight"></i></a>
<div id="navBox" class="nwebNav">
<div class="ndealList clear_fix">
<div class="navTitle left">
<h4 class="round">
<a rel="nofollow" href="/softs/" target="_blank">软件下载</a></h4>
</div>
<dl class="right">
<dd class="links">
<a rel="nofollow" href="/softs/android.html" target="_blank">android</a>
<a rel="nofollow" href="/softs/mac.html" target="_blank">MAC</a>
<a rel="nofollow" href="/qudong/" target="_blank">驱动下载</a>
<a rel="nofollow" href="/fonts/" target="_blank">字体下载</a>
<a rel="nofollow" href="/dll/" target="_blank">DLL</a>
<img src="linker1" />
</dd>
</dl>
</div>
</div>
</div>
<dl class="right">
<dd class="morelinks">
<a rel="nofollow" href="/softs/android.html" target="_blank">android</a>
<a rel="nofollow" href="/softs/mac.html" target="_blank">MAC</a>
<a rel="nofollow" href="/qudong/" target="_blank">驱动下载</a>
<a rel="nofollow" href="/fonts/" target="_blank">字体下载</a>
<a rel="nofollow" href="/dll/" target="_blank">DLL</a>
<img src="linker2" />
</dd>
</dl>
EOF;
#以标签的属性值{keywords}来解析html,返回包含{keywords}属性标签的完整内容,第三个参数为找到的第几项(默认返回所有)
function parse(string $html, string $keywords, int $order=-1)
{
$pattern = "#(<(?<appTag>\w+)\s+[^>]*?{$keywords}[^>]*?>
((\s*<(?<recTag>\w+)[^>]*/?>(?:(?>.*?(?=<\k<recTag>|</\k<recTag>>))|(?3))*</\k<recTag>>?\s*)
|\s*<[^>]*?/>\s*)*</\k<appTag>>)
|(<(?<aqqTag>\w+)\s+[^>]*?{$keywords}[^>]*?>.*?</\k<aqqTag>>)
(<[\w]+[^>]*?{$keywords}[^>]*?/>)#msix";
preg_match_all($pattern, $html, $elements);
if($order==-1){
return $elements[0];
}else{
return $elements[0][$order];
}
}
$elements = parse($html, "links");
print_r($elements);返回值如下,清晰明了 : )
Array ( [0] => <dd class="links"> <a rel="nofollow" href="/softs/android.html" target="_blank">android</a> <a rel="nofollow" href="/softs/mac.html" target="_blank">MAC</a> <a rel="nofollow" href="/qudong/" target="_blank">驱动下载</a> <a rel="nofollow" href="/fonts/" target="_blank">字体下载</a> <a rel="nofollow" href="/dll/" target="_blank">DLL</a> <img src="linker1" /> </dd> [1] => <dd class="morelinks"> <a rel="nofollow" href="/softs/android.html" target="_blank">android</a> <a rel="nofollow" href="/softs/mac.html" target="_blank">MAC</a> <a rel="nofollow" href="/qudong/" target="_blank">驱动下载</a> <a rel="nofollow" href="/fonts/" target="_blank">字体下载</a> <a rel="nofollow" href="/dll/" target="_blank">DLL</a> <img src="linker2" /> </dd> )
结论:很费劲!要大规模采集网页,还是直接引入phpQuery,queryList或者simple_html_dom吧...
评论