【问题标题】:XML-parsing respecting empty elements in between ("<lb/>") in Pyhton/BeautifulSoup在 Python/BeautifulSoup 中(“<lb/>”)之间的空元素的 XML 解析
【发布时间】:2020-01-30 22:36:58
【问题描述】:

我需要在Python3 中使用BeautifulSoup 解析XML 文档,以从属性中获取所有值——由空元素(嵌套与否)相互分隔。一次获取 所有 值很容易,但我需要将它们分开。

请参阅下面的 MWE:我需要 c-元素中的所有 val-值,由 lb- 或 pb-元素分隔。现在的列表输出是:[1, 2, 3, 4, 5, 6, 7, 8],但我需要一个类似[[1, 2], [3,4], [5, 6], [7, 8]](或dict)的列表。

我认为问题在于,我无法关联下一个空元素(它不是sibling,也不是next_element)。

感谢您的帮助!

MWE-XML-示例:“file.xml”

<text>
 <nested>
  <c val="1">A</c>
  <c val="2">B</c>
 </nested>
 <lb/>
 <nested>
  <nested>
   <c val="3">C</c>
   <c val="4">D</c>
   <lb/>
   <pb/>
   <c val="5">E</c>
   <c val="6">F</c>
   <c type="empty"> </c>
  </nested>
 </nested>
 <lb/>
 <c val="7">G</c>
 <c val="8">H</c>
</text>

MWE-python3-示例

from bs4 import BeautifulSoup
with open("file.xml", "r", encoding="utf-8") as fh:
    soup = BeautifulSoup(fh.read(), "lxml-xml")
    listoflists = list()
    val_list = list()

    for c in soup.find_all("c"):

        if not "val" in c.attrs:
            continue

        else:
            val_list.append( int(c["val"]) )

        if c.next_sibling == (soup.lb or soup.pb): # <-- not working?
            listoflists.append(val_list)
            continue

    print(val_list)
    print(listoflists)

【问题讨论】:

    标签: python xml beautifulsoup


    【解决方案1】:

    您可以使用itertools.groupby 对元素进行分组。

    例如(如果txt 是问题中的代码 sn-p):

    from bs4 import BeautifulSoup
    from itertools import groupby
    
    soup = BeautifulSoup(txt, 'xml')
    
    out = []
    for v, g in groupby(soup.select('c[val], lb, pb'), lambda k: k.name == 'c'):
        if v:
            out.append([c['val'] for c in g])
    
    print(out)
    

    打印出来:

    [['1', '2'], ['3', '4'], ['5', '6'], ['7', '8']]
    

    编辑(没有.select()的版本):

    from bs4 import BeautifulSoup
    from itertools import groupby
    
    soup = BeautifulSoup(txt, 'xml')
    
    out = []
    for v, g in groupby(soup.find_all(['c', 'lb', 'pb']), lambda k: k.name == 'c'):
        if v:
            out.append([c['val'] for c in g if 'val' in c.attrs])
    
    print(out)
    

    打印:

    [['1', '2'], ['3', '4'], ['5', '6'], ['7', '8']]
    

    【讨论】:

    • 感谢您的回答。我不知道为什么,但它为我输出了[['1', '2', '3', '4', '5', '6', '7', '8']]。
    • 是的,文件/内容相同。
    • @dia 然后它应该打印与我相同的输出。检查你的bs4 版本(应该是4.8.2)
    • 好的,我的版本是4.6.0。但是更新我的系统/python 等不会更新bs4。 (在 linux 中工作 /apt-get。)——还有其他解决方案吗? (我必须运行超过 2 GB 的 xml 文件。)
    • Andrej,不用担心,旧版本完美运行。
    【解决方案2】:

    为你推荐一个库,不依赖第三方库,更轻更快。

    from simplified_scrapy.simplified_doc import SimplifiedDoc
    from itertools import groupby
    html ='''
    <text>
     <nested>
      <c val="1">A</c>
      <c val="2">B</c>
     </nested>
     <lb/>
     <nested>
      <nested>
       <c val="3">C</c>
       <c val="4">D</c>
       <lb/>
       <pb/>
       <c val="5">E</c>
       <c val="6">F</c>
       <c type="empty"> </c>
      </nested>
     </nested>
     <lb/>
     <c val="7">G</c>
     <c val="8">H</c>
    </text>
    '''
    doc = SimplifiedDoc(html)
    # First
    out = []
    for v, g in groupby(doc.getElementsByTag(['c','lb','pb']), lambda k: k.tag == 'c'):
        if v:
            out.append([c['val'] for c in g if 'val' in c])
    print(out)
    
    # Second
    listoflists = list()
    val_list = list()
    lstC = doc.cs.contains('',attr='val')
    for c in lstC:
      val_list.append(int(c["val"]))
      next = c.next
      if not next or not next.val:
        listoflists.append(val_list)
        val_list = list()
    print (listoflists)
    

    结果:

    [['1', '2'], ['3', '4'], ['5', '6'], ['7', '8']]
    [[1, 2], [3, 4], [5, 6], [7, 8]]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-09
      • 1970-01-01
      • 2011-05-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多