【问题标题】:Wrap multiple tags with BeautifulSoup用 BeautifulSoup 包装多个标签
【发布时间】:2015-08-28 15:07:51
【问题描述】:

我正在编写一个允许将 html 文档转换为 reveal.js 幻灯片的 python 脚本。为此,我需要在<section> 标记内包装多个标记。

使用wrap() 方法可以轻松地将单个标签包装在另一个标签中。但是我不知道如何包装多个标签。

一个例子澄清一下,原文html:

html_doc = """
<html>

<head>
  <title>The Dormouse's story</title>
</head>

<body>

  <h1 id="first-paragraph">First paragraph</h1>
  <p>Some text...</p>
  <p>Another text...</p>
  <div>
    <a href="http://link.com">Here's a link</a>
  </div>

  <h1 id="second-paragraph">Second paragraph</h1>
  <p>Some text...</p>
  <p>Another text...</p>

  <script src="lib/.js"></script>
</body>

</html>
"""


"""

我想将&lt;h1&gt; 及其下一个标签包装在&lt;section&gt; 标签内,如下所示:

<html>
<head>
  <title>The Dormouse's story</title>
</head>
<body>

  <section>
    <h1 id="first-paragraph">First paragraph</h1>
    <p>Some text...</p>
    <p>Another text...</p>
    <div>
      <a href="http://link.com">Here's a link</a>
    </div>
  </section>

  <section>
    <h1 id="second-paragraph">Second paragraph</h1>
    <p>Some text...</p>
    <p>Another text...</p>
  </section>

  <script src="lib/.js"></script>
</body>

</html>

我是这样选择的:

from bs4 import BeautifulSoup
import itertools
soup = BeautifulSoup(html_doc)
h1s = soup.find_all('h1')
for el in h1s:
    els = [i for i in itertools.takewhile(lambda x: x.name not in [el.name, 'script'], el.next_elements)]
    els.insert(0, el)
    print(els)

输出:

[<h1 id="first-paragraph">First paragraph</h1>, 'First paragraph', '\n  ', <p>Some text...</p>, 'Some text...', '\n  ', <p>Another text...</p>, 'Another text...', '\n  ', <div><a href="http://link.com">Here's a link</a>  </div>, '\n    ', <a href="http://link.com">Here's a link</a>, "Here's a link", '\n  ', '\n\n  ']

[<h1 id="second-paragraph">Second paragraph</h1>, 'Second paragraph', '\n  ', <p>Some text...</p>, 'Some text...', '\n  ', <p>Another text...</p>, 'Another text...', '\n\n  ']

选择是正确的,但我看不到如何将每个选择包装在 &lt;section&gt; 标记内。

【问题讨论】:

  • 你能编辑你的帖子并显示预期的输出吗?
  • 请发布预期输出。
  • 我添加了显式输出。

标签: python beautifulsoup


【解决方案1】:

最后我找到了在这种情况下如何使用wrap 方法。我需要了解soup 对象中的每一个更改都是在原地完成的

from bs4 import BeautifulSoup
import itertools
soup = BeautifulSoup(html_doc)

# wrap all h1 and next siblings into sections
h1s = soup.find_all('h1')
for el in h1s:
    els = [i for i in itertools.takewhile(
              lambda x: x.name not in [el.name, 'script'],
              el.next_siblings)]
    section = soup.new_tag('section')
    el.wrap(section)
    for tag in els:
        section.append(tag)

print(soup.prettify())

这给了我想要的输出。希望对您有所帮助。

【讨论】:

  • 谢谢。我想指出一些我学到的可能并不明显的事情。 1)在其他地方附加标签(例如通过附加)将其从之前的位置删除。 2) 因为 (1) 并且因为 .next_siblings 是一个生成器,而不是一个列表,所以您需要在迭代调用 section.append(tag) 的循环之前将其转换为列表,您的复杂 els=[...] 会这样做。我不需要过滤,所以我只尝试了els=el.next_siblings。这失败了,因为兄弟姐妹的第一步打破了兄弟姐妹链。 els=list(el.next_siblings) 工作。
【解决方案2】:

我想我会考虑这个,因为它有点困难和混乱。基本上,使用 BeautifulSoup'ed 的 html_test 字符串,我将一个新的 div 添加到树中,将其锚定在然后循环它,包装/附加到所有元素中,直到它到达 ,包括未标记的字符串。 wrapTag 函数将所有元素追加到 div 中,直到到达最后一个 p。意识到while循环的主要事情是附加到div移动而不是复制next_sibling。所以如果我们使用硬列表和for循环,第i个位置就会移动。希望有帮助。卢卡斯

#!/usr/bin/env python3
#coding: utf-8
from platform import python_version
from bs4 import __version__ as bs_version, Tag, NavigableString, BeautifulSoup

try:
    html_test = '<body><div class="div" id="1">beforePa<p>line 1a</p>betweenPab<p class="x">line 1b<b>bold in 1b</b></p>betweenPbc<p>line 1c</p>betweenPcd<p>line 1d</p>betweenPde<p class="y">line 1e</p>betweenPef<p>line 1f</p>afterPf</div><div class="div" id="2"><p>line 2a</p><p>line 2b</p><p>line 2c</p></div></body>'
    html = BeautifulSoup(html_test, 'lxml')
    print(html.prettify())
    parser = 'lxml'
except:
    parser = 'html.parser'
print('python: "'+python_version()+'", bs4 version: "'+bs_version+'", bs4 parser: "'+parser+'"')

def p(tag, sstr=""):
    print(sstr+".. .")
    print(tag, " ... ", type(tag))
    print("text: ", tag.text, " ... ", type(tag.text))
    print("string: ", tag.string, " ... ", type(tag.string))
    print("contents: ", tag.contents, " ... ", type(tag.contents))
    print()
    return

def newTag(tag, attrs={}, tstr=""):
    n = html.new_tag(tag)
    if (len(attrs) > 0):
        for k, v in attrs.items():
            n[k] = v
    if (len(tstr) > 0):
        n.string = tstr
    return n

def wrapTag(newTag, fromTagInclusive, toTagExclusive):
    fromTagInclusive.wrap(newTag)
    #p(fromTagInclusive.parent, "fromTag.parent")
    n = fromTagInclusive.parent
    c = 0
    while 1:
        c += 1
        x = n.next_sibling
        if (x is None):
            break
        n.append(x)
        #print(c, x, n.next_sibling, isinstance(n.next_sibling, Tag), n.next_sibling.name if isinstance(n.next_sibling, Tag) else "~Tag", n.next_sibling.attrs if isinstance(n.next_sibling, Tag) else "~Tag")
        #if isinstance(n.next_sibling, Tag) and (n.next_sibling.name == 'p') and ('class' in n.next_sibling.attrs) and ('y' in n.next_sibling['class']):
        if (n.next_sibling == toTagExclusive):
            break
    return n, toTagExclusive

n = newTag('div', { 'class':"classx", 'id':"idx" }, 'here we are in classx idx')
p(n, "new div")
n, _ = wrapTag(n, html.find('p', {'class':"x"}), html.find('p', {'class':"y"}))
p(n, "wrapped div")
print(html.prettify())
exit()

【讨论】:

    猜你喜欢
    • 2014-05-03
    • 2023-03-08
    • 2014-01-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-04
    • 2020-05-19
    • 1970-01-01
    相关资源
    最近更新 更多