BeautifulSoup的stripped_strings中换行符太多... 如何获取更接近原始HTML的纯文本格式?

2 投票
1 回答
2714 浏览
提问于 2025-04-30 10:47

使用Python 2.7和bs4,考虑以下这段HTML代码:

    <div class="pd-t10">
        <ul class="bullet-list c-body pd-b20">
            <li class="mr-t20">
            <strong>
              <em>Innerhalb Deutschlands</em>
            </strong>: 0800 100 6711</li>
            <li class="mr-t20">International: Siehe die Liste der 
            <a href="/de/support/contacts/us_support.html" fallback="Auf Englisch" target="new">geb&#252;hrenfreien Telefonnummern weltweit</a> von VMware.</li>
            <li class="mr-t20">
            <strong>Wichtiger Hinweis:</strong> Bitte halten Sie Ihre 
            <a href="/de/support/customer-number-faq.html" fallback="Auf Englisch">Kundennummer</a> bereit, wenn Sie den Support anrufen. Wenn Sie &#252;ber ein VMware-Konto verf&#252;gen, finden Sie Ihre Kundennummer auf der 
            <a href="/account/secure/customerRegistration.do?action=existingCustomer">Profilseite</a>.</li>
        </ul>
    </div>

用.stripped_strings可以提取出文本,但打印出来的纯文本格式和浏览器显示的样子不太一样:

for x in foo.stripped_strings:
    print x

结果是:

Innerhalb Deutschlands
: 0800 100 6711
International: Siehe die Liste der
gebührenfreien Telefonnummern weltweit
von VMware.
Wichtiger Hinweis:
Bitte halten Sie Ihre
Kundennummer
bereit, wenn Sie den Support anrufen. Wenn Sie über ein VMware-Konto verfügen, finden Sie Ihre Kundennummer auf der
Profilseite
.

但我想看到的是:

Innerhalb Deutschlands: 0800 100 6711
International: Siehe die Liste der gebührenfreien Telefonnummern weltweit von VMware.
Wichtiger Hinweis: Bitte halten Sie Ihre Kundennummer bereit, wenn Sie den Support anrufen. Wenn Sie über ein VMware-Konto verfügen, finden Sie Ihre Kundennummer auf der Profilseite.

有没有什么聪明的方法我没想到?使用get_text和find_all(text=True)得到的结果也差不多……

再多说一点……让我困惑的是:这些soup对象似乎即使在标签被移除后,仍然保留了一些记忆……

如果我们对每个不是<p><li><br>的标签使用.unwrap(),foo会变成:

<div class="pd-t10">

<li class="mr-t20">

Innerhalb Deutschlands
: 0800 100 6711</li>
<li class="mr-t20">International: Siehe die Liste der 
        gebührenfreien Telefonnummern weltweit von VMware.</li>
<li class="mr-t20">
Wichtiger Hinweis: Bitte halten Sie Ihre 
            Kundennummer bereit, wenn Sie den Support anrufen. Wenn Sie über ein VMware-Konto verfügen, finden Sie Ihre Kundennummer auf der 
            Profilseite.</li>

</div>

但这仍然没有达到你预期的效果:

[txt for txt in foo.stripped_strings]
[u'Innerhalb Deutschlands', u': 0800 100 6711', u'International: Siehe die Liste der', u'geb\xfchrenfreien Telefonnummern weltweit', u'von VMware.', u'Wichtiger Hinweis:', u'Bitte halten Sie Ihre', u'Kundennummer', u'bereit, wenn Sie den Support anrufen. Wenn Sie \xfcber ein VMware-Konto verf\xfcgen, finden Sie Ihre Kundennummer auf der', u'Profilseite', u'.']

不过,如果我们把去掉标签的代码写入一个临时文件,然后把它作为一个新的soup对象打开,我们就能更接近想要的结果:

[txt for txt in newSoup.stripped_strings]
[u'Innerhalb Deutschlands\n: 0800 100 6711', u'International: Siehe die Liste der \n            geb\u7aefhrenfreien Telefonnummern weltweit von VMware.', u'Wichtiger Hinweis: Bitte halten Sie Ihre \n            Kundennummer bereit, wenn Sie den Support anrufen. Wenn Sie \u7aefber ein VMware-Konto verf\u7aefgen, finden Sie Ihre Kundennummer auf der \n            Profilseite.']

现在只需要把每个列表项中的换行符替换成空格,并去掉多余的空白(' '.join(listitem.split()))。这样就解决了这个一般性的问题,但这并没有解释为什么直接输出字符串并创建一个新的soup对象的行为和现有的soup对象不一样。

暂无标签

1 个回答

1

我从来不抓取整个文本,而是总是有针对性地抓取。

不过既然你想要这样做,那我会使用下面这种简单的工具来找到正确的解决方案。

记住,我假设数据总是以这种格式出现 a:b,就像 Innerhalb Deutschlands: 0800 100 6711 这样。

所以我会这样处理你的 html

soup=BeautifulSoup(html)
text=soup.text.replace("\n:",":").replace("\n "," ")
text=text.split("\n")
text=filter(None, text) # This is to remove the empty elements
text=[" ".join(c.split()) for c in text] # To remove the extra whitespaces
for i in text:
    print i

Out[46]:
Innerhalb Deutschlands: 0800 100 6711
International: Siehe die Liste der gebührenfreien Telefonnummern weltweit von VMware.
Wichtiger Hinweis: Bitte halten Sie Ihre Kundennummer bereit, wenn Sie den Support anrufen. Wenn Sie über ein VMware-Konto verfügen, finden Sie Ihre Kundennummer auf der Profilseite.

希望这能帮到你 :-)

撰写回答