【问题标题】:How to get an attribute value using BeautifulSoup and Python?如何使用 BeautifulSoup 和 Python 获取属性值?
【发布时间】:2017-08-28 20:54:10
【问题描述】:

我在使用 BeautifulSoup 和 Python 获取属性值方面非常失败。以下是 XML 的结构:

...
</total>
<tag>
    <stat fail="0" pass="1">TR=111111 Sandbox=3000613</stat>
    <stat fail="0" pass="1">TR=121212 Sandbox=3000618</stat>
    ...
    <stat fail="0" pass="1">TR=999999 Sandbox=3000617</stat>
</tag>
<suite>
...

我想要得到的是 pass 值,但对于我的生活,我就是不明白该怎么做。我检查了BeautifulSoup,似乎我应该使用stat['pass'] 之类的东西,但这似乎不起作用。

这是我的代码:

with open('../results/output.xml') as raw_resuls:
results = soup(raw_resuls, 'lxml')
for stat in results.find_all('tag'):
            print stat['pass']

如果我执行results.stat['pass'],它会返回另一个标记内的值,在 XML blob 中向上。

如果我打印 stat 变量,我会得到以下信息:

<stat fail="0" pass="1">TR=787878 Sandbox=3000614</stat>
...
<stat fail="0" pass="1">TR=888888 Sandbox=3000610</stat>

这似乎没问题。

我很确定我错过了什么或做错了什么。我应该在哪里看?我是否采取了错误的方法?

任何建议或指导将不胜感激!谢谢

【问题讨论】:

    标签: python beautifulsoup


    【解决方案1】:

    请考虑这种方法:

    from bs4 import BeautifulSoup
    
    with open('test.xml') as raw_resuls:
        results = BeautifulSoup(raw_resuls, 'lxml')
    
    for element in results.find_all("tag"):
        for stat in element.find_all("stat"):
            print(stat['pass'])
    

    您的解决方案的问题是 pass 包含在 stat 中,而不是在您搜索它的 tag 中。

    此解决方案搜索所有tag,并在这些tag 中搜索stat。从这些结果中,它得到通过

    对于 XML 文件

    <tag>
        <stat fail="0" pass="1">TR=111111 Sandbox=3000613</stat>
        <stat fail="0" pass="1">TR=121212 Sandbox=3000618</stat>
        <stat fail="0" pass="1">TR=999999 Sandbox=3000617</stat>
    </tag>
    

    上面的脚本得到输出

    1
    1
    1
    

    加法

    由于某些细节似乎仍不清楚(请参阅 cmets),请考虑使用 BeautifulSoup 的完整解决方法来获得您想要的一切。如果您遇到性能问题,这种使用字典作为列表元素的解决方案可能并不完美。但是由于您似乎在使用 Python 和 Soup 时遇到了一些麻烦,所以我认为我可以通过名称而不是索引来访问所有相关信息,从而尽可能简单地创建这个示例。

    from bs4 import BeautifulSoup
    
    # Parses a string of form 'TR=abc123 Sandbox=abc123' and stores it in a dictionary with the following
    # structure: {'TR': abc123, 'Sandbox': abc123}. Returns this dictionary. 
    def parseTestID(testid):
        dict = {'TR': testid.split(" ")[0].split("=")[1], 'Sandbox': testid.split(" ")[1].split("=")[1]}
        return dict
    
    # Parses the XML content of 'rawdata' and stores pass value, TR-ID and Sandbox-ID in a dictionary of the 
    # following form: {'Pass': pasvalue, TR': TR-ID, 'Sandbox': Sandbox-ID}. This dictionary is appended to
    # a list that is returned.
    def getTestState(rawdata):
        # initialize parser
        soup = BeautifulSoup(rawdata,'lxml')
        parsedData= []
    
        # parse for tags
        for tag in soup.find_all("tag"):
            # parse tags for stat
            for stat in tag.find_all("stat"):
                # store everthing in a dictionary
                dict = {'Pass': stat['pass'], 'TR': parseTestID(stat.string)['TR'], 'Sandbox': parseTestID(stat.string)['Sandbox']}
                # append dictionary to list
                parsedData.append(dict)
    
        # return list
        return parsedData
    

    你可以使用上面的脚本做任何你想做的事情(例如打印出来)

    # open file
    with open('test.xml') as raw_resuls:
        # get list of parsed data 
        data = getTestState(raw_resuls)
    
    # print parsed data
    for element in data:
        print("TR = {0}\tSandbox = {1}\tPass = {2}".format(element['TR'],element['Sandbox'],element['Pass']))
    

    输出如下所示

    TR = 111111 Sandbox = 3000613   Pass = 1
    TR = 121212 Sandbox = 3000618   Pass = 1
    TR = 222222 Sandbox = 3000612   Pass = 1
    TR = 232323 Sandbox = 3000618   Pass = 1
    TR = 333333 Sandbox = 3000605   Pass = 1
    TR = 343434 Sandbox = ZZZZZZ    Pass = 1
    TR = 444444 Sandbox = 3000604   Pass = 1
    TR = 454545 Sandbox = 3000608   Pass = 1
    TR = 545454 Sandbox = XXXXXX    Pass = 1
    TR = 555555 Sandbox = 3000617   Pass = 1
    TR = 565656 Sandbox = 3000615   Pass = 1
    TR = 626262 Sandbox = 3000602   Pass = 1
    TR = 666666 Sandbox = 3000616   Pass = 1
    TR = 676767 Sandbox = 3000599   Pass = 1
    TR = 737373 Sandbox = 3000603   Pass = 1
    TR = 777777 Sandbox = 3000611   Pass = 1
    TR = 787878 Sandbox = 3000614   Pass = 1
    TR = 828282 Sandbox = 3000600   Pass = 1
    TR = 888888 Sandbox = 3000610   Pass = 1
    TR = 999999 Sandbox = 3000617   Pass = 1
    

    让我们总结一下使用的核心元素:

    查找 XML 标记 要查找 XML 标记,您可以使用 soup.find("tag") 返回第一个匹配的标记或 soup.find_all("tag") 查找所有匹配的标记并将它们存储在列表中。通过遍历列表可以轻松访问单个标签。

    查找嵌套标签 要查找嵌套标签,您可以再次使用find()find_all(),将其应用于第一个find_all() 的结果。

    访问标签的内容 要访问标签的内容,请将string 应用于单个标签。比如tag = &lt;tag&gt;I love Soup!&lt;/tag&gt;tag.string = "I love Soup!".

    查找属性值 要获取属性的值,您可以使用下标表示法。比如tag = &lt;tag color=red&gt;I love Soup!&lt;/tag&gt;tag['color']="red".

    为了解析 "TR=abc123 Sandbox=abc123" 形式的字符串,我使用了常见的 Python 字符串拆分。你可以在这里阅读更多信息:How can I split and parse a string in Python?

    【讨论】:

    • 我明白了,我现在明白了,完全有道理!它现在工作得很好,感谢它!如果可以问,我还有一个问题:因为我只有一个 tag 属性,是否需要 for 循环?如果没有,我怎样才能直接进入 tag 属性?谢谢!
    • 能帮到你真是太好了!您可以通过投票并接受它作为正确答案stackoverflow.com/help/someone-answers 来证明此答案满足您的需求
    • 如果您的 XML 文件只包含一个 &lt;tag&gt;,您可以将 for element in results.find_all("tag"): 替换为 element = results.find("tag")。请参阅 BeautifulSoup 文档的这一部分:crummy.com/software/BeautifulSoup/bs4/doc/#find
    • 您好,感谢您的回复!我做了如下element = results.find('tag')element 变量包含以下&lt;tag&gt;TR=111111 Sandbox=3000613&lt;/tag&gt;,而不是预期的所有 stat 标签。不确定是我做错了什么还是 XML 文件有问题。
    • 我想我意识到发生了什么。 XML 文件中有几个&lt;tags&gt;&lt;tag&gt;something&lt;/tag&gt;&lt;/tags&gt;。我敢肯定这与find 混淆了。可能需要使用一些正则表达式来获得我需要的&lt;tag&gt;&lt;stat&gt;values&lt;/tag&gt;&lt;/stat&gt;
    【解决方案2】:

    问题是find_all('tag')返回整个标题为tag的html块:

    >>> results.find_all('tag')                                                                      
    [<tag>                                                                                     
    <stat fail="0" pass="1">TR=111111 Sandbox=3000613</stat>                                   
    <stat fail="0" pass="1">TR=121212 Sandbox=3000618</stat>                                   
    <stat fail="0" pass="1">TR=999999 Sandbox=3000617</stat>                                   
    </tag>]
    

    您的意图是收集每个stat 块,因此您应该使用results.find_all('stat')

    >>> stat_blocks = results.find_all('stat')                                                                      
    [<stat fail="0" pass="1">TR=111111 Sandbox=3000613</stat>, <stat fail="0" pass="1">TR=121212 Sandbox=3000618</stat>, <stat fail="0" pass="1">TR=999999 Sandbox=3000617</stat>]
    

    从那里,修复代码以将“传递”压缩成一个列表是微不足道的:

    >>> passes = [s['pass'] if s is not None else None for s in stat_blocks]                   
    >>> passes                                                                                   
    ['1', '1', '1']  
    

    或打印:

    >>> for s in stat_blocks:                                                                  
    ...     print(s['pass'])                                                                   
    ...                                                                                        
    1                                                                                          
    1                                                                                          
    1     
    

    在 python 中,测试结果非常重要,因为输入是方式过于动态而无法相信你的记忆。我经常在类和模块中包含一个静态 test 函数,以确保返回类型和值符合我的预期。

    【讨论】:

    • 谢谢,有道理。我不应该提到 XML 文件中有更多的 stats 属性,但我只对 tag 节点内的属性感兴趣。感谢您的回复,不胜感激!
    • @Xour 啊,很公平,那你就用results.find_all('tag').find_all('stat')。对您认为有用且信息丰富的任何答案进行投票,并仔细检查您是否选择了最佳答案。干杯!
    【解决方案3】:

    您的“标签”可以有多个“统计”条目。你只有一个“标签”条目吗?

    如果是这样,那么首先找到“tag”,然后遍历“tag”条目中包含的“stat”条目。比如:

    for stat in soup.find("tag").find_all("stat"):
        print(stat["pass"])
    

    【讨论】:

    • 嗨。只有一个tag 条目。但是,由于某种原因,当我运行您的代码时,它不会返回任何内容。如果我删除 .find_all("stat") 部分(仅用于调试),它将返回第一个 stat 标记。感谢您的回复!
    • 根据@Aaron3468 和我的帖子,你应该可以解决它。对“tag”进行“查找”应该返回“tag”的全部内容,即所有“stat”。不知道如何解释您所看到的。
    • 我不确定我的 XML 文件中是否有某些内容,但我尝试了这种方法(与上面 datell 建议的相同),但它什么也没返回。如果我这样做:with open('../results/output.xml') as raw_resuls: results = soup(raw_resuls, 'lxml') for stat in results.find("tag").find_all("stat"): print 'test' print(stat["pass"]) 没有打印任何内容,即使是 test 字符串,也不知道为什么。 PS:对不起,我只是无法正确格式化代码!
    • 我想我意识到发生了什么。 XML 文件中有几个&lt;tags&gt;&lt;tag&gt;something&lt;/tag&gt;&lt;/tags&gt;。我敢肯定这与find 混淆了。可能需要使用一些正则表达式来获得我需要的&lt;tag&gt;&lt;stat&gt;values&lt;/tag&gt;&lt;/stat&gt;
    • 可能不需要正则表达式。它是一个嵌套循环。只需使用汤上的 find_all 遍历标签,寻找“标签”项目。在每个项目中,为“stat”元素执行单独的 find_all。第一个 find_all 在“汤”上,第二个 find_all 在第一个循环返回的各个元素上。
    【解决方案4】:

    如果你和我一样在这里,正在寻找一个最简单和简短的解决方案,试试这个从你的标签中获取属性。

    soup = BeautifulSoup(''' 
        <html> 
            <h2 class="hello"> Heading 1 </h2> 
            <h1> Heading 2 </h1> 
        </html> 
        ''', "lxml") 
      
    # Get the whole h2 tag 
    tag = soup.h2 
      
    # Get the attribute 
    attribute = tag['class'] 
    

    【讨论】:

      猜你喜欢
      • 2017-08-17
      • 2018-11-05
      • 2013-09-14
      • 1970-01-01
      • 2016-09-19
      • 1970-01-01
      • 2016-08-14
      • 2021-05-21
      • 2015-08-28
      相关资源
      最近更新 更多