Python BeautifulSoup 提取元素之间的文本
- 2025-03-10 08:52:00
- admin 原创
- 64
问题描述:
我尝试从以下 HTML 中提取“这是我的文本”:
<html>
<body>
<table>
<td class="MYCLASS">
<!-- a comment -->
<a hef="xy">Text</a>
<p>something</p>
THIS IS MY TEXT
<p>something else</p>
</br>
</td>
</table>
</body>
</html>
我尝试过这种方法:
soup = BeautifulSoup(html)
for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
print hit.text
但是我得到了所有嵌套标签之间的所有文本以及注释。
有人能帮助我从中去掉“这是我的文本”吗?
解决方案 1:
详细了解如何在解析树中BeautifulSoup
导航。解析树已获得tags
和NavigableStrings
(因为这是文本)。示例
from BeautifulSoup import BeautifulSoup
doc = ['<html><head><title>Page title</title></head>',
'<body><p id="firstpara" align="center">This is paragraph <b>one</b>.',
'<p id="secondpara" align="blah">This is paragraph <b>two</b>.',
'</html>']
soup = BeautifulSoup(''.join(doc))
print soup.prettify()
# <html>
# <head>
# <title>
# Page title
# </title>
# </head>
# <body>
# <p id="firstpara" align="center">
# This is paragraph
# <b>
# one
# </b>
# .
# </p>
# <p id="secondpara" align="blah">
# This is paragraph
# <b>
# two
# </b>
# .
# </p>
# </body>
# </html>
要向下移动解析树,您需要contents
和string
。
内容是页面元素中包含的 Tag 和 NavigableString 对象的有序列表
如果标签只有一个子节点,并且该子节点是字符串,则该子节点可用作 tag.string 以及 tag.contents[0]
对于上述情况,也就是说你可以得到
soup.b.string
# u'one'
soup.b.contents[0]
# u'one'
对于多个子节点,你可以有例如
pTag = soup.p
pTag.contents
# [u'This is paragraph ', <b>one</b>, u'.']
因此您可以在这里玩耍contents
并获取您想要的索引的内容。
您还可以迭代标签,这是一个快捷方式。例如,
for i in soup.body:
print i
# <p id="firstpara" align="center">This is paragraph <b>one</b>.</p>
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>
解决方案 2:
改用.children
:
from bs4 import NavigableString, Comment
print ''.join(unicode(child) for child in hit.children
if isinstance(child, NavigableString) and not isinstance(child, Comment))
是的,这有点像舞蹈。
输出:
>>> for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
... print ''.join(unicode(child) for child in hit.children
... if isinstance(child, NavigableString) and not isinstance(child, Comment))
...
THIS IS MY TEXT
解决方案 3:
您可以使用.contents
:
>>> for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
... print hit.contents[6].strip()
...
THIS IS MY TEXT
解决方案 4:
使用您自己的汤对象:
soup.p.next_sibling.strip()
你直接用 * 抓取 <p>
soup.p
(这取决于它是解析树中的第一个 <p>)然后在返回的
next_sibling
标签对象上使用soup.p
,因为所需文本与 <p> 嵌套在解析树的同一级别.strip()
只是一个 Python str 方法,用于删除前导和尾随空格
*否则只需使用您选择的过滤器即可找到元素
在解释器中它看起来像这样:
In [4]: soup.p
Out[4]: <p>something</p>
In [5]: type(soup.p)
Out[5]: bs4.element.Tag
In [6]: soup.p.next_sibling
Out[6]: u'
THIS IS MY TEXT
'
In [7]: type(soup.p.next_sibling)
Out[7]: bs4.element.NavigableString
In [8]: soup.p.next_sibling.strip()
Out[8]: u'THIS IS MY TEXT'
In [9]: type(soup.p.next_sibling.strip())
Out[9]: unicode
解决方案 5:
简短回答:soup.findAll('p')[0].next
真正的答案:您需要一个不变的参考点,从该参考点您可以到达您的目标。
您在对 Haidro 的回答的评论中提到,您想要的文本并不总是在同一个位置。找到它相对于某个元素位于同一位置的意义。然后弄清楚如何让 BeautifulSoup 按照该不变路径浏览解析树。
例如,在原始帖子中您提供的 HTML 中,目标字符串紧跟在第一个段落元素之后,并且该段落不为空。由于findAll('p')
将找到段落元素,soup.find('p')[0]
因此将是第一个段落元素。
在这种情况下您可以使用soup.find('p')
但soup.findAll('p')[n]
更通用因为也许您的实际场景需要第 5 段或类似的内容。
fieldnext
属性将是树中的下一个解析元素,包括子元素。因此soup.findAll('p')[0].next
包含段落的文本,并将soup.findAll('p')[0].next.next
在提供的 HTML 中返回您的目标。
解决方案 6:
soup = BeautifulSoup(html)
for hit in soup.findAll(attrs={'class' : 'MYCLASS'}):
hit = hit.text.strip()
print hit
这将打印:这是我的文本尝试一下..
解决方案 7:
BeautifulSoup 文档提供了使用 extract 方法从文档中删除对象的示例。以下示例的目的是从文档中删除所有注释:
删除元素
一旦获得了对元素的引用,就可以使用 extract 方法将其从树中删除。此代码将从
文档中删除所有注释:
from BeautifulSoup import BeautifulSoup, Comment
soup = BeautifulSoup("""1<!--The loneliest number-->
<a>2<!--Can be as bad as one--><b>3""")
comments = soup.findAll(text=lambda text:isinstance(text, Comment))
[comment.extract() for comment in comments]
print soup
# 1
# <a>2<b>3</b></a>
扫码咨询,免费领取项目管理大礼包!