网站首页php

PHP正则递归子组匹配完整html标签内容

发布时间:2019-10-04 13:04:36编辑:slayer.hover阅读(3973)

        在采集网页时,经常需要按标签取内容,有时候取个小东西,似乎没必要引入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吧...

评论