网站首页php
PHP正则递归子组匹配完整html标签内容
发布时间:2019-10-04 13:04:36编辑:slayer.hover阅读(3204)
在采集网页时,经常需要按标签取内容,有时候取个小东西,似乎没必要引入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吧...
评论