Python BeautifulSoup是否错误地检查Navigablesting元素的子成员身份?
我有一个HTML页面,它的部分树看起来像这样 (请参阅下面包含html的代码片段):Python BeautifulSoup是否错误地检查Navigablesting元素的子成员身份?,python,beautifulsoup,Python,Beautifulsoup,我有一个HTML页面,它的部分树看起来像这样 (请参阅下面包含html的代码片段): | | | | | | | | 波旁威士忌 为什么BeautifulSoup表示“左”波旁威士忌是 一个“肯塔基”(正确)和“新奥尔良”(错误)的孩子 反之亦然,正确的波旁威士忌是“肯塔基州”的孩子(不正确) 在一个页面中有不同的html元素,所有这些元素都具有相同的文本 并不少见(例如页眉、页脚)。但是现在,在
| |
| |
| |
| |
波旁威士忌
为什么BeautifulSoup表示“左”波旁威士忌是
一个“肯塔基”(正确)和“新奥尔良”(错误)的孩子
反之亦然,正确的波旁威士忌是“肯塔基州”的孩子(不正确)
在一个页面中有不同的html元素,所有这些元素都具有相同的文本
并不少见(例如页眉、页脚)。但是现在,在我为某些文本模式执行find_all()之后,当使用header.children或footer.children正确识别文本元素是否是其中一个的子元素时,我不能信任BeautifulSoup
(就好像在一家公司里,工程和营销部门都声称某个员工属于他们,仅仅因为她的名字是“Sarah”-公司中可能有多个Sarah-first_name属性只是该对象的众多属性中的一个,不应仅确定其身份。)
这样的事情可以避免吗?或者,另一种方法是什么
找出元素的正确子元素
请注意,NavigableString类的MRO以“str”开头:
<class 'str'>, <class 'bs4.element.PageElement'>, <class 'object'>
,
我猜这似乎表明问题的原因是BeautifulSoup
使用字符串比较来确定元素之间的相等性(或标识匹配)
不管这是否真的是问题所在,是否有替代方案或修复/补丁
谢谢
代码:
重新导入
从bs4导入BeautifulSoup
TEST_HTML=“”
头衔
波旁威士忌
波旁威士忌
"""
def test():
汤=美汤(测试)
#搜索“波旁”
re_pattern=re.compile('bourbon',re.IGNORECASE)
text\u matches=soup.find\u all(text=re\u模式)
#打印详细调试输出。。。
对于文本匹配中的文本匹配:
打印('id:{}-class:{}-text:{}-parent attrs:{}'\
格式(id(文本匹配),
文本匹配。类匹配。名称匹配,
text_match.string,
文本(匹配.parent.attrs))
#id:140609176408136-类:NavigableString-文本:Bourbon-父属性:{'id':'Kentucky'}
#id:140609176408376-class:NavigableString-text:Bourbon-parent attrs:{'id':'NewOrleans'}
肯塔基州匹配=文本匹配[0]
肯塔基州家长=肯塔基州家长
新奥尔良匹配=文本匹配[1]
新奥尔良家长=新奥尔良家长
#确认->一切正常。。。
打印(kentucky_parent.attrs)#{id':'kentucky'}
打印(new_orleans_parent.attrs)#{'id':'NewOrleans'}
#获取肯塔基州和新奥尔良所有孩子的名单
#(此树遍历完全正常)
ky_children=[肯塔基州儿童对儿童_parent.children]
无子女=[新奥尔良的子女与子女之比\u parent.children]
#确认->一切正常。。。
打印(ky#U children中的child的[id(child)]#[140609176408136]
打印([id(child)表示无子项中的子项]#[140609176408376]
#现在,问题来了!!!
打印(肯塔基州比赛无儿童)#正确->错误!!!!!!!
打印(肯塔基州儿童比赛)#正确
打印(新奥尔良无儿童比赛)#正确
打印(新奥尔良-肯塔基州儿童比赛)#正确->错误!!!!!!!
这是因为肯塔基匹配
和新奥尔良匹配
都是类的实例,类是常规unicode
字符串的子类
ky_children
和no_children
都包含一个基本上是字符串的列表,在您的例子中,它只是[u'Bourbon']
。[u'Bourbon']中的u'Bourbon'总是计算为True
。当在
中执行检查时,比较字符串,而不是NavigableString
类实例
换句话说,您的In
检查正在字符串列表中查找字符串
作为一种解决方法,您可以在
检查中为您的使用id()
ky_children = [id(child) for child in kentucky_parent.children]
print(id(kentucky_match) in no_children) # False
print(id(kentucky_match) in ky_children) # True
谢谢你的回答。我还怀疑基于字符串的比较是问题的原因。我可以使用id(object)作为解决方法。但我认为这应该是最后的手段。它基本上开始使用(c)python的对象引用/指针来比较对象,这将非常糟糕。树中元素的标识不应由其文本确定。类似于两个不同的员工突然向同一个经理汇报,仅仅因为他们都有相同的名字“Lisa”。我会等待另一个答案,希望有更好的选择。
import re
from bs4 import BeautifulSoup
TEST_HTML = """<!doctype html>
<head><title>A title</title></head>
<html>
<body>
<div id="Kentucky">Bourbon</div>
<div id="NewOrleans">Bourbon</div>
</body>
</html>
"""
def test():
soup = BeautifulSoup(TEST_HTML)
# search for "Bourbon"
re_pattern = re.compile('bourbon', re.IGNORECASE)
text_matches = soup.find_all(text=re_pattern)
# print verbose debug output...
for text_match in text_matches:
print('id: {} - class: {} - text: {} - parent attrs: {}'.\
format(id(text_match),
text_match.__class__.__name__,
text_match.string,
text_match.parent.attrs))
# id: 140609176408136 - class: NavigableString - text: Bourbon - parent attrs: {'id': 'Kentucky'}
# id: 140609176408376 - class: NavigableString - text: Bourbon - parent attrs: {'id': 'NewOrleans'}
kentucky_match = text_matches[0]
kentucky_parent = kentucky_match.parent
new_orleans_match = text_matches[1]
new_orleans_parent = new_orleans_match.parent
# confirm -> all ok...
print(kentucky_parent.attrs) # {'id': 'Kentucky'}
print(new_orleans_parent.attrs) # {'id': 'NewOrleans'}
# get a list of all the children for both kentucky and new orleans
# (this tree traversal is all ok)
ky_children = [child for child in kentucky_parent.children]
no_children = [child for child in new_orleans_parent.children]
# confirm -> all ok...
print([id(child) for child in ky_children]) # [140609176408136]
print([id(child) for child in no_children]) # [140609176408376]
# now, here's the problem!!!
print(kentucky_match in no_children) # True -> wrong!!!!!!!
print(kentucky_match in ky_children) # True
print(new_orleans_match in no_children) # True
print(new_orleans_match in ky_children) # True -> wrong!!!!!!!
ky_children = [id(child) for child in kentucky_parent.children]
print(id(kentucky_match) in no_children) # False
print(id(kentucky_match) in ky_children) # True