Python BeautifulSoup是否错误地检查Navigablesting元素的子成员身份?

Python BeautifulSoup是否错误地检查Navigablesting元素的子成员身份?,python,beautifulsoup,Python,Beautifulsoup,我有一个HTML页面,它的部分树看起来像这样 (请参阅下面包含html的代码片段): | | | | | | | | 波旁威士忌 为什么BeautifulSoup表示“左”波旁威士忌是 一个“肯塔基”(正确)和“新奥尔良”(错误)的孩子 反之亦然,正确的波旁威士忌是“肯塔基州”的孩子(不正确) 在一个页面中有不同的html元素,所有这些元素都具有相同的文本 并不少见(例如页眉、页脚)。但是现在,在

我有一个HTML页面,它的部分树看起来像这样 (请参阅下面包含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