检测2个HTML片段是否具有相同的层次结构

时间:2012-10-28 10:41:13

标签: python html xml lxml

具有相同层次结构的片段示例:

(1)
<div>
  <span>It's a message</span>
</div>

(2)
<div>
  <span class='bold'>This is a new text</span>
</div>

具有不同结构的片段示例:

(1)
<div>
  <span><b>It's a message</b></span>
</div>

(2)
<div>
  <span>This is a new text</span>
</div>

因此,具有相似结构的片段对应于一个分层树(相同的标签名称,相同的层次结构)。

如何使用lxml检测2个元素(html片段)是否具有相同的结构?

我有一个功能对于一些更困难的情况(比例子)不能正常工作:

def _is_equal( el1, el2 ):      
    # input: 2 elements with possible equal structure and tag names
    # e.g. root = lxml.html.fromstring( buf )
    # el1 = root[ 0 ]
    # el2 = root[ 1 ]
    # move from top to bottom, compare elements
    result = False  

    if el1.tag == el2.tag:
        # has no children
        if len( el1 ) == len( el2 ):
            if len( el1 ) == 0:             
                return True
            else:
                # iterate one of them, for example el1
                i = 0
                for child1 in el1:
                    child2 = el2[ i ]
                    is_equal2 = _is_equal( child1, child2 )
                    if not is_equal2:
                        return False
                return True                     
        else:
            return False
    else:
        return False

代码无法检测到class ='tovar2'的2个div具有相同的结构:

<body>


    <div class="tovar2">
        <h2 class="new">
            <a href="http://modnyedeti-krsk.ru/magazin/product/333193003">
                Куртка  д/д
            </a>
        </h2>
        <ul class="art">
            <li>
                Артикул: <span>1759</span>
            </li>
        </ul>
        <div>
            <div class="wrap" style="width:180px;"> 
                <div class="new">
                    <img src="shop_files/new-t.png" alt="">
                </div>     
                <a class="highslide" href="http://modnyedeti-krsk.ru/d/459730/d/820.jpg" onclick="return hs.expand(this)"> 
                    <img src="shop_files/fr_5.gif" style="background:url(/d/459730/d/548470803_5.jpg) 50% 50% no-repeat scroll;" alt="Куртка  д/д" height="160" width="180"> 
                </a>     
            </div>
        </div>

        <form action="" onsubmit="return addProductForm(17094601,333193003,3150.00,this,false);">
            <ul class="bott ">
                <li class="price">Цена:<br>
                    <span>
                        <b>
                            3 150
                        </b> руб.
                    </span>
                </li>
                <li class="amount">Кол-во:<br><input class="number" onclick="this.select()" value="1" name="product_amount" type="text">
                </li>
                <li class="buy"><input value="" type="submit">
                </li>
            </ul>
        </form>
    </div>


    <div class="tovar2">
        <h2 class="new">
            <a href="http://modnyedeti-krsk.ru/magazin/product/333124803">Куртка  д/д</a>
        </h2>
        <ul class="art">
            <li>
                Артикул: <span>1759</span>
            </li>
        </ul>
        <div>
            <div class="wrap" style="width:180px;"> 
                <div class="new">
                    <img src="shop_files/new-t.png" alt="">
                </div>     
                <a class="highslide" href="http://modnyedeti-krsk.ru/d/459730/d/820.jpg" onclick="return hs.expand(this)"> 
                    <img src="shop_files/fr_5.gif" style="background:url(/d/459730/d/548470803_5.jpg) 50% 50% no-repeat scroll;" alt="Куртка  д/д" height="160" width="180"> 
                </a>      
            </div>
        </div>      

        <form action="" onsubmit="return addProductForm(17094601,333124803,3150.00,this,false);">
            <ul class="bott ">
                <li class="price">Цена:<br>
                    <span>
                        <b>3 150</b> руб.
                    </span>
                </li>
                <li class="amount">Кол-во:<br><input class="number" onclick="this.select()" value="1" name="product_amount" type="text">
                </li>
                <li class="buy">
                    <input value="" type="submit">
                </li>
            </ul>
        </form>
    </div>

    </body>        

2 个答案:

答案 0 :(得分:4)

你使事情变得复杂一点,当事情被证明不是False时,你只需要在最后返回True

当两个元素的标记匹配,它们的长度匹配,并且每个配对的子元素相同时,它们是相等的。

Python使用all() function测试序列中的所有元素是否True非常简单,并且使用zip()我们可以很好地配对子元素。如果任何子对相等,all()将提前终止:

def _is_equal( el1, el2 ):      
    if el1.tag == el2.tag and len(el1) == len(el2):
        return all(_is_equal(c1, c2) for c1, c2 in zip(el1, el2))

    return False

答案 1 :(得分:2)

您现有代码失败的原因是,如果有多个子代,我的设置错误;你将它分配给零,然后永远不会增加它,所以你将el1的每个元素与el2的第一个元素进行比较,而不是与它在同一位置的el2元素进行比较。

要修复现有代码,请执行以下操作:

def _is_equal( el1, el2 ):      
    # input: 2 elements with possible equal structure and tag names
    # e.g. root = lxml.html.fromstring( buf )
    # el1 = root[ 0 ]
    # el2 = root[ 1 ]
    # move from top to bottom, compare elements
    result = False  

    if el1.tag == el2.tag:
        # has no children
        if len( el1 ) == len( el2 ):
            if len( el1 ) == 0:             
                return True
            else:
                # iterate one of them, for example el1
                for i, child1 in enumerate(el1):
                    child2 = el2[ i ]
                    is_equal2 = _is_equal( child1, child2 )
                    if not is_equal2:
                        return False
                return True                     
        else:
            return False
    else:
        return False

但是,您现有的代码可以更加简洁。你正在测试三个条件: 1)标记匹配 2)相同数量的孩子 3)所有条件适用于每对孩子

这些都是Python中的单行表达式。所以你可以做到以下几点:

def _is_equal(el1, el2):
    return (el1.tag == el2.tag and
            len(el1) == len(el2) and
            all(_is_equal(c1, c2) for c1, c2 in zip(el1, el2)))

请注意,由于and短路而all只要它迭代的任何一个元素都返回False,就不会做任何不必要的额外计算。< / p>