Анализ дочерних тегов XML с тем же именем с помощью ElementTree

Я пытаюсь разобрать файл XML (в новом для меня Python) со следующей структурой:

<xml>
<document>
<fit>
<grp> some tags </grp>
<prp> 
<p> <id> 1674 </id> </p>
<drp> 
<name> Joe </name>
<post> 
<company> abc </company>
<company> Ltd. </company>
</post>
</drp>
</prp>
</fit>
</document>
<document>
.
.
.
</xml>

Чтобы извлечь такую ​​информацию, как идентификатор, имя, компания, а затем записать их в csv, я попробовал следующий код:

tree = ET.parse(file)
root=tree.getroot()
with open(csvfile, 'a') as f:
    writer=csvDictWriter(f, ['ID', 'NAME', 'NCOMP'], delimiter=', ')
    writer.writeheader()
    result = {}
    for child in root.findall('./fit'):
        result['ID'] = ( "" .join(child.find('p').find('id').text))
        result['NAME'] = ( "" .join(child.find('drp').find('name'))
        result['NCOMP'] = ( "" .join(child.find('drp').find('post').find('company')
        writer.write(result)

Однако для названия компании я получаю только содержимое первого тега, затем я попытался использовать цикл for и добавить в список, подобный этому:

Com = []
for each in child.find('drp').find('post'):
    coms = each.find('company')
    Com = Com.append[coms]
    result['NCOMP'] = Com

Желаемый результат:

ID.      NAME.     NCOMP
1674.    Joe.      abc Ltd.

Как я могу изменить код, чтобы он включал значение обоих тегов?


person python6    schedule 24.04.2020    source источник
comment
Добро пожаловать в stackoverflow! Хорошо сформулированный первый вопрос :)   -  person Adrian Torrie    schedule 24.04.2020


Ответы (2)


Попробуйте что-нибудь в этом духе; он использует lxml для сбора данных через xpath и pandas для их хранения в кадре данных:

data = """
<xml>
   <document>
      <fit>
         <grp>some tags</grp>
         <prp>
            <p>
               <id>1674</id>
            </p>
            <drp>
               <name>Joe</name>
               <post>
                  <company>abc</company>
                  <company>Ltd.</company>
               </post>
            </drp>
         </prp>
      </fit>
   </document>
</xml>
"""

from lxml import etree
import pandas as pd

columns = ["ID", "NAME", "NCOMP"]
rows = []

doc = etree.XML(data)

targets = doc.xpath('//prp')
for target in targets:
    row = []
    id = target.xpath('./p/id/text()')[0]
    name = target.xpath('./drp/name/text()')[0]
    ncomp = target.xpath('./drp//post//company/text()')
    row.extend([id,name,' '.join(ncomp)])    
    rows.append(row)
pd.DataFrame(rows,columns=columns)

Вывод:

    ID     NAME     NCOMP
0   1674    Joe     abc Ltd.

Редактировать - версия ET.

первый:

import xml.etree.ElementTree as ET

затем, начиная с doc, заменить на:

doc = ET.fromstring(data)
et_targets = doc.findall('.//prp')
for target in et_targets:
    row = []
    id = target.findall('./p/id')[0]
    name = target.findall('./drp/name')[0]
    ncomp = target.findall('./drp//post//company')[0]
    row.extend([id.text,name.text,' '.join(ncomp.text)])    
    rows.append(row)
pd.DataFrame(rows,columns=columns)

Выход должен быть таким же.

person Jack Fleeting    schedule 24.04.2020
comment
Спасибо, но нельзя ли реализовать то же самое с помощью Element Tree? - person python6; 26.04.2020
comment
@python6 Вероятно; но поддержка xpath в ET очень ограничена, поэтому я стараюсь не использовать ее, если нет абсолютно никакого другого выбора. - person Jack Fleeting; 26.04.2020
comment
Ой! Я должен был включить мою версию Python 2.6, поэтому я не могу использовать lxml. Моя ошибка! - person python6; 27.04.2020
comment
Найти все возвращает список, и объект списка выдает текст без атрибута - person python6; 27.04.2020
comment
@python6 findall() действительно возвращает список, и список действительно не имеет текстового атрибута, но list()[0] имеет этот атрибут. Я только что попробовал еще раз, и это работает. Так что я не уверен, что проблема на вашей стороне. - person Jack Fleeting; 27.04.2020

Другое решение.

from simplified_scrapy import SimplifiedDoc,req,utils
html = '''
<xml>
<document>
<fit>
<grp> some tags </grp>
<prp> 
<p> <id> 1674 </id> </p>
<drp> 
<name> Joe </name>
<post> 
<company> abc </company>
<company> Ltd. </company>
</post>
</drp>
</prp>
</fit>
</document>
<document>
.
.
.
</xml>
'''
doc = SimplifiedDoc(html)
rows = []
rows.append(['ID', 'NAME', 'NCOMP'])
for document in doc.documents:
  rows.append([document.id.text,document.name.text," ".join(document.companys.text)])
utils.save2csv('test.csv',rows)

Результат:

ID,NAME,NCOMP
1674,Joe,abc Ltd.
person dabingsou    schedule 25.04.2020