我正在寻找一种方法来读取具有任意嵌套度的HTML元素的文本内容(即没有HTML代码)。
如果没有嵌套,那就很容易了,但由于HTML不是常规语言,others with the same problem have been told to use (X)HTML parsers.
有可能用美味的汤做到这一点吗?类似的东西:
page = soup.find('*').getText() # obviously this won't give xpath info
我可以想象使用生成器将不同的标记名称提供给find
函数,但我不知道标记名称是什么。我还需要返回类似于带有文本的元素的xpath引用,以便我知道最终从find
函数返回的内容的来源。
因此,对于以下HTML:
<div>
text of div 1
<span>
text of span 1
<span>
text of span 2
</span>
</span>
</div>
我需要一个函数来返回类似的东西:
('text of div 1', '/div'), ('text of span 1', '/div/span'), ('text of span 2', '/div/span/span')
答案 0 :(得分:2)
这个怎么样:
result_set = []
for tag in soup.find_all():
parent_list = []
content_of_tag = tag.find(text=True)
parent_list.append(tag.name)
while tag.parent is not None:
tag = tag.parent
parent_list.append(tag.name)
result_set.append((content_of_tag, parent_list))
第一个find_all()
将在所有级别上找到所有类型的所有标记。迭代这些tag.find(text=True)
会找到每个标记中的第一个文本。循环前parent_list.append(tag.name)
将当前标记名称添加到父列表中。然后while循环查找所有标记父项,并将它们的名称添加到父列表中。
答案 1 :(得分:2)
我编写了一个递归函数,它将返回字典中所有文本的XPATH,格式如下:
{'xpath1': {'text': 'text1'}, 'xpath2': {'text': 'text2'}, ...}
代码:
from bs4 import BeautifulSoup, NavigableString
def get_xpaths_dict(soup, xpaths={}, curr_path=''):
curr_path += '/{}'.format(soup.name)
for item in soup.contents:
if isinstance(item, NavigableString):
if item.strip():
try:
xpaths[curr_path]['count'] += 1
count = xpaths[curr_path]['count']
curr_path += '[{}]'.format(count)
xpaths[curr_path] = {'text': item.strip()}
except KeyError:
xpaths[curr_path] = {'text': item.strip(), 'count': 1}
else:
xpaths = get_xpaths_dict(item, xpaths, curr_path)
return xpaths
html = '''<div>
text of div 1
<span>
text of span 1.1
<span>
text of span 2.1
</span>
<span>
text of span 2.2
<span>
text of span 3
</span>
</span>
</span>
</div>'''
soup = BeautifulSoup(html, 'html.parser')
xpaths = get_xpaths_dict(soup.div)
print(xpaths)
输出:
{'/div': {'text': 'text of div 1', 'count': 1}, '/div/span': {'text': 'text of span 1.1', 'count': 1}, '/div/span/span': {'text': 'text of span 2.1', 'count': 2}, '/div/span/span[2]': {'text': 'text of span 2.2'}, '/div/span/span[2]/span': {'text': 'text of span 3', 'count': 1}}
我知道这不是您期望输出的格式。但是,您可以将其转换为您想要的任何格式。例如,要将其转换为预期输出,只需执行以下操作:
expected_output = [(v['text'], k) for k, v in xpaths.items()]
print(expected_output)
输出:
[('text of div 1', '/div'), ('text of span 1.1', '/div/span'), ('text of span 2.1', '/div/span/span'), ('text of span 2.2', '/div/span/span[2]'), ('text of span 3', '/div/span/span[2]/span')]
一些解释:
字典中的额外键count
用于存储当前标记中具有相同名称的标记数。使用这种格式(字典)可以优化代码。您只需访问一次标记一次。
<强>加成:强>
因为,该函数返回一个以XPATH为键的字典,您可以使用XPATH获取任何文本。例如:
xpaths = get_xpaths_dict(soup.div)
print(xpaths['/div/span/span[2]/span']['text'])
# text of span 3