0

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

(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

该代码无法检测到 2 个带有 class='tovar2' 的 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>        
4

2 回答 2

4

你把事情复杂了一点,你只需要False在事情被证明不是的时候才回来True

两个元素在标签匹配、长度匹配且每个配对子元素相同时相等。

Python 可以使用函数测试序列中的所有元素是否True真的很容易,并且通过使用我们可以很好地配对元素子元素。如果任何子对相等,将提前终止:all()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
于 2012-10-28T10:56:19.643 回答
2

您现有代码失败的原因是在有多个孩子的情况下, i 设置错误;您将其分配为零,然后从不增加它,因此您将 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,这不会进行任何不必要的额外计算。

于 2012-10-28T11:02:23.730 回答