【问题标题】:Elegant way to replace the tag on a sequence of bs4 soup tags and wrap them all in another tag优雅的方式来替换一系列 bs4 汤标签上的标签并将它们全部包装在另一个标签中
【发布时间】:2023-04-07 08:14:01
【问题描述】:

我有一个像这样的 html 字符串:

test = """<body>
    <h1>A header</h1>
    <p> Just a normal paragraph <b>before</b> </p>
    <p>• test element</p>
    <p>• test element2</p>
    <p> Following <i>stuff</i></p>

    </body>
    """

此用户已明确包含 u'\u2022' 项目符号字符,而不是使用列表。我想得到以下转换后的html

<body>
    <h1>A header</h1>
    <p> Just a normal paragraph <b>before</b> </p>
    <ul>
    <li>test element</li>
    <li>test element2</li>
    </ul>
    <p> Following <i>stuff</i></p>

</body>

解决这个问题的最优雅的方法是什么?然后我可以从标签字符串上的简单 .find 中识别出这些项目符号项。我可以删除子弹并将它们包裹在&lt;li&gt; 标签中。但我不知道如何遍历汤,然后将所有子弹包装成一个 &lt;ul&gt; 标记。如果我可以像普通列表一样遍历汤并创建一个包含新元素的修改列表,我可以执行以下伪代码:

new_soup = []
for tag in soup:
   if has_bullet(tag):
       #  start storing tags
       bullets.append(tag)
   else:
       if bullets: # if we have some bullets to dump
           new_soup.append(ul_tag_start)
           new_soup.extend(modify_text(bullets))
           new_soup.append(ul_tag_end)
       new_soup.append(tag)
       # clear bullets list
       bullets = []

但是不知道要一个个元素一个个地写新的soup,不知道有没有更好的办法使用bs4的各种insert、insert_before、insert_after等方法。

【问题讨论】:

    标签: python html beautifulsoup


    【解决方案1】:

    你可以使用递归:

    import bs4, re
    from bs4 import BeautifulSoup as soup
    test = """<h1>A header</h1><p> Just a normal paragraph <b>before</b> </p><p>• test element</p><p>• test element2<b>something else</b></p><p> Following <i>stuff</i></p>"""
    def form_ul(d):
       return soup('<ul>{}</ul>'.format('\n'.join(f'<li>{i}</li>' for i in d)), 'html.parser').ul
    
    def to_ul(d):
       c,l = [],[]
       for i in d.contents:
          if isinstance(i, bs4.NavigableString):
             c.append(i)
          else:
             if str(i.get_text(strip=True)).startswith(u'\u2022'):
                l.append('\n'.join(j.replace(u'\u2022 ', '') if isinstance(j, bs4.NavigableString) else str(j) for j in i.contents))
             else:
                if l:
                   c.append(form_ul(l))
                   l = []
                to_ul(i)
                c.append(i)
       if l:
          c.append(form_ul(l))
       d.contents = [j for j in c if not re.findall('^\n+$', str(j))]
    
    html = soup(test, 'html.parser')
    to_ul(html)
    print(soup.prettify(html))
    

    输出:

    <h1>
     A header
    </h1>
    <p>
     Just a normal paragraph
     <b>
      before
     </b>
    </p>
    <ul>
     <li>
      test element
     </li>
     <li>
      test element2
      <b>
       something else
      </b>
     </li>
    </ul><p>
     Following
     <i>
      stuff
     </i>
    </p>
    

    【讨论】:

    • 非常感谢。这适用于上面的示例,但不适用于我的实际用例,其中第一个链接元素可能类似于• test &lt;b&gt;element&lt;/b&gt; 而不仅仅是• test element。换句话说,如果存在,我需要在该行中保留其他标签。执行此操作的最正确方法是l.append(str(i)) 而不是l.append(t)?编辑:刚刚意识到这并不完全有效,因为它保留了&lt;p&gt; 标签。
    • 这有效:`l.append(str(i).replace('

      ','').replace('

      ', ''))```,但似乎很hacky。但是,尝试解开包装对我不起作用。更好的想法?
    • @gammapoint 请查看我最近的编辑,因为我添加了一个解决方案,该解决方案将保留 的任何元素的所有子 HTML
    • @gammapoint 请查看我最近的编辑,它现在适用于您最新的输入样本。在检测到子弹后,soup.destruct 似乎也在删除某些正在进行的 HTML 元素。
    • @gammapoint 我很抱歉,事实证明额外的“空格”是由于换行符 (\n) 的存在,我更新了我的解决方案以过滤掉,现在我得到了正确的我的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2015-06-24
    • 2015-09-19
    • 2011-10-28
    • 2013-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多