【问题标题】:beautiful soup extract tags delete text美丽的汤提取物标签删除文本
【发布时间】:2017-05-29 04:55:12
【问题描述】:

我正在尝试使用 Beautifuloup 提取 html 标签并删除文本。以这个 html 为例:

html_page = """
<html>
<body>
<table>
<tr class=tb1><td>Lorem Ipsum dolor Sit amet</td></tr>
<tr class=tb1><td>Consectetuer adipiscing elit</td></tr>
<tr><td>Aliquam Tincidunt mauris eu Risus</td></tr>
<tr><td>Vestibulum Auctor Dapibus neque</td></tr>
</table>
</body>
</html>
"""

想要的结果是:

<html>
<body>
<table>
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
<tr><td></td></tr>
</table>
</body>
</html>

这是我目前所得到的:

def get_tags(soup):
    copy_soup = soup
    for tag in copy_soup.findAll(True):
        tag.attrs = {} # removes attributes of a tag
        tag.string = ''

    return copy_soup

print get_tags(soup)

使用 tag.attrs = {} 可以删除所有标签属性。但是当我尝试使用 tag.string 或 tag.clear() 时,我只剩下&lt;html&gt;&lt;/html&gt;。我知道在第一次迭代中使用tag.string 或tag.clear() 可能会删除html 标记中的所有内容。

我不确定如何解决这个问题。也许首先递归地删除孩子的文本?还是我缺少一种更简单的方法?

【问题讨论】:

    标签: python html web-scraping beautifulsoup


    【解决方案1】:

    您不能简单地将 .string 重置为空字符串,因为如果一个元素有一个带有文本的子元素,例如您的示例中的 tr 元素,您会无意中从树中删除 td 元素。

    您不能使用.clear(),因为它还会递归删除所有子节点。

    我不记得在没有BeautifulSoup 中的数据的情况下获取 HTML 树结构的内置方法 - 我会使用以下方法:

    for elm in soup.find_all():
        if not elm.find(recursive=False):  # if not children
            elm.string = ''
        elm.attrs = {}
    

    这里我们只在没有孩子的情况下重置.string。

    演示:

    >>> from bs4 import BeautifulSoup
    >>> 
    >>> html_page = """
    ... <html>
    ... <body>
    ... <table>
    ... <tr class=tb1><td>Lorem Ipsum dolor Sit amet</td></tr>
    ... <tr class=tb1><td>Consectetuer adipiscing elit</td></tr>
    ... <tr><td>Aliquam Tincidunt mauris eu Risus</td></tr>
    ... <tr><td>Vestibulum Auctor Dapibus neque</td></tr>
    ... </table>
    ... </body>
    ... </html>
    ... """
    >>> 
    >>> soup = BeautifulSoup(html_page, "html.parser")
    >>> for elm in soup.find_all():
    ...     if not elm.find(recursive=False):
    ...         elm.string = ''
    ...     elm.attrs = {}
    ... 
    >>> print(soup.prettify())
    <html>
     <body>
      <table>
       <tr>
        <td>
        </td>
       </tr>
       <tr>
        <td>
        </td>
       </tr>
       <tr>
        <td>
        </td>
       </tr>
       <tr>
        <td>
        </td>
       </tr>
      </table>
     </body>
    </html>
    

    【讨论】:

    • 啊,谢谢你的解释,特别是准确地描述了我原来的方法有缺陷的原因。
    • @hannahbanana2.0 很高兴为您提供帮助,我正在尝试看看是否有更漂亮的方法来解决您的问题..查看lxml 和lxml.objectify..
    • @hannahbanana2.0 顺便说一句,这里是 quite related topic 的另一种方法 - 可能比我们在这里做的更简单。
    • 哦,拍摄。在创建自己的问题之前,我在这里搜索类似问题时没有看到该帖子。感谢分享。
    • @hannahbanana2.0 好吧,我不认为 lxml.objectify 在这里会有所帮助。至少它看起来一点也不比 BeautifulSoup 简单。可能是我错过了一些简单的东西。
    【解决方案2】:

    实际上,我可以通过递归更新标签 children's 来删除文本。您还可以在递归中更新它们的属性。

    from bs4 import BeautifulSoup
    from bs4.element import NavigableString
    
    def delete_displayed_text(element):
        """
        delete displayed text from beautiful soup tag element object recursively
        :param element: beautiful soup tag element object
        :return: beautiful soup tag element object
        """
        new_children = []
        for child in element.contents:
            if not isinstance(child, NavigableString):
                new_children.append(delete_displayed_text(child))
        element.contents = new_children
        return element
    
    if __name__ =='__main__':
        html_code_sample = '<div class="hello">I am not supposed to be displayed<a>me neither</a></div>'
        soup = BeautifulSoup(html_code_sample, 'html.parser')
        soup = delete_displayed_text(soup)
        cleaned_soup = BeautifulSoup(str(soup), 'html.parser')
        print(cleaned_soup.getText())
    

    【讨论】:

    • 天才,获奖答案,先生!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-15
    • 1970-01-01
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多