XML Разбор Python ElementTree - Вложенный для циклов - PullRequest
1 голос
/ 20 апреля 2020

Я использую Jupyter Notebook и ElementTree (Python 3), чтобы создать фрейм данных и сохранить его как csv из файла XML. Вот формат XML (на эстонском языке):

<asutused hetk="2020-04-14T03:53:33" ver="2">
    <asutus>
        <registrikood>10000515</registrikood>
        <nimi>Osaühing B.Braun Medical</nimi>
        <aadress />
        <tegevusload>
            <tegevusluba>
                <tegevusloa_number>L04647</tegevusloa_number>
                <alates>2019-12-10</alates>
                <kuni />
                <loaliik_kood>1</loaliik_kood>
                <loaliik_nimi>Eriarstiabi</loaliik_nimi>
                <haiglaliik_kood />
                <haiglaliik_nimi />
                <tegevuskohad>
                    <tegevuskoht>
                        <aadress>Harju maakond, Tallinn, Mustamäe linnaosa, J. Sütiste tee 17/1</aadress>
                        <teenused>
                            <teenus>
                                <kood>T0038</kood>
                                <nimi>ambulatoorsed üldkirurgiateenused</nimi>
                            </teenus>
                            <teenus>
                                <kood>T0236</kood>
                                <nimi>õe vastuvõtuteenus</nimi>
                            </teenus>
                        </teenused>
                    </tegevuskoht>
                    <tegevuskoht>
                        <aadress>Harju maakond, Tallinn, Mustamäe linnaosa, J. Sütiste tee 17/1</aadress>
                        <teenused>
                            <teenus>
                                <kood>T0038</kood>
                                <nimi>ambulatoorsed üldkirurgiateenused</nimi>
                            </teenus>
                            <teenus>
                                <kood>T0236</kood>
                                <nimi>õe vastuvõtuteenus</nimi>
                            </teenus>
                        </teenused>
                    </tegevuskoht>
                </tegevuskohad>
            </tegevusluba>
            <tegevusluba>
                <tegevusloa_number>L04651</tegevusloa_number>
                <alates>2019-12-11</alates>
                <kuni />
                <loaliik_kood>2</loaliik_kood>
                <loaliik_nimi>Õendusabi</loaliik_nimi>
                <haiglaliik_kood />
                <haiglaliik_nimi />
                <tegevuskohad>
                    <tegevuskoht>
                        <aadress>Harju maakond, Tallinn, Mustamäe linnaosa, J. Sütiste tee 17/1</aadress>
                        <teenused>
                            <teenus>
                                <kood>T0038</kood>
                                <nimi>ambulatoorsed üldkirurgiateenused</nimi>
                            </teenus>
                            <teenus>
                                <kood>T0236</kood>
                                <nimi>õe vastuvõtuteenus</nimi>
                            </teenus>
                        </teenused>
                    </tegevuskoht>
                    <tegevuskoht>
                        <aadress>Harju maakond, Tallinn, Mustamäe linnaosa, J. Sütiste tee 17/1</aadress>
                        <teenused>
                            <teenus>
                                <kood>T0038</kood>
                                <nimi>ambulatoorsed üldkirurgiateenused</nimi>
                            </teenus>
                            <teenus>
                                <kood>T0236</kood>
                                <nimi>õe vastuvõtuteenus</nimi>
                            </teenus>
                        </teenused>
                    </tegevuskoht>
                </tegevuskohad>
            </tegevusluba>
        </tegevusload>
        <tootajad>
            <tootaja>
                <kood>D03091</kood>
                <eesnimi>Evo</eesnimi>
                <perenimi>Kaha</perenimi>
                <kutse_kood>11</kutse_kood>
                <kutse_nimi>Arst</kutse_nimi>
                <erialad>
                    <eriala>
                        <kood>E420</kood>
                        <nimi>üldkirurgia</nimi>
                    </eriala>
                </erialad>
            </tootaja>
            <tootaja>
                <kood>N01146</kood>
                <eesnimi>Karmen</eesnimi>
                <perenimi>Mežulis</perenimi>
                <kutse_kood>15</kutse_kood>
                <kutse_nimi>Õde</kutse_nimi>
            </tootaja>
            <tootaja>
                <kood>N01153</kood>
                <eesnimi>Nele</eesnimi>
                <perenimi>Terras</perenimi>
                <kutse_kood>15</kutse_kood>
                <kutse_nimi>Õde</kutse_nimi>
            </tootaja>
            <tootaja>
                <kood>N02767</kood>
                <eesnimi>Helena</eesnimi>
                <perenimi>Tern</perenimi>
                <kutse_kood>15</kutse_kood>
                <kutse_nimi>Õde</kutse_nimi>
            </tootaja>
            <tootaja>
                <kood>N12882</kood>
                <eesnimi>Hanna</eesnimi>
                <perenimi>Leemet</perenimi>
                <kutse_kood>15</kutse_kood>
                <kutse_nimi>Õde</kutse_nimi>
            </tootaja>
        </tootajad>
    </asutus>
</asutused>

Каждый "asutus" - это больница, и мне нужна некоторая информация внутри. Вот мой код:

tree = ET.parse("od_asutused.xml")
root = tree.getroot()

# open a file for writing
data = open('EE.csv', 'w')

# create the csv writer object
csvwriter = csv.writer(data, delimiter=';')
head = []

count = 0
for member in root.findall('asutus'):
    hospital = []
    if count == 0:
        ident = member.find('registrikood').tag
        head.append(id)
        name = member.find('nimi').tag
        head.append(name)
        address = member.find('aadress').tag
        head.append(address)
        facility_type = member.find('./tegevusload/tegevusluba/haiglaliik_nimi').tag
        head.append(facility_type)
        site_address = member.find('./tegevusload/tegevusluba/tegevuskohad/tegevuskoht/aadress').tag
        head.append(site_address)
        for elem in member.findall('tegevusload'):
            list_specs = elem.find('./tegevusluba/tegevuskohad/tegevuskoht/teenused/teenus/nimi').tag
            head.append(list_specs)
        csvwriter.writerow(head)
        count = count + 1

    ident = member.find('registrikood').text
    hospital.append(ident)
    name = member.find('nimi').text
    hospital.append(name)
    address = member.find('aadress').text
    hospital.append(address)
    facility_type = member.find('./tegevusload/tegevusluba/haiglaliik_nimi').text
    hospital.append(facility_type)
    site_address = member.find('./tegevusload/tegevusluba/tegevuskohad/tegevuskoht/aadress').text
    hospital.append(site_address)
    for spec in elem.findall('tegevusload'):
        list_specs = spec.find('./tegevusluba/tegevuskohad/tegevuskoht/teenused/teenus/nimi').text
        hospital.append(list_specs)
    csvwriter.writerow(hospital)
data.close()

#Upload csv for geocoding
df = pd.read_csv(r'EE.csv', na_filter= False, delimiter=';')

#Rename columns
df.rename(columns = {'<built-in function id>':'id', 
                     'nimi':'name',
                     'aadress':'address',
                     'haiglaliik_nimi':'facility_type',
                     'haiglaliik_kood':'facility_type_c',
                     'aadress.1':'site_address',
                     'nimi.1':'list_specs'},
          inplace = True) 

#Add columns
df['country'] = 'Estonia' 
df['cc'] = 'EE'

df.head(10)

И результат df.head (10):

Результат кадра данных

"list_specs" "пусто, независимо от того, что я делаю. Как я могу заполнить это поле списком каждого 'nimi' для каждого адреса сайта? Спасибо.

1 Ответ

0 голосов
/ 20 апреля 2020

Я нашел в вашем коде следующие пункты для изменения:

  1. По крайней мере, на моем компьютере вызов csv.writer приводит к тому, что символы новой строки удвоилась . Решение, которое я нашел, - открыть выходной файл с дополнительными параметрами:

    data = open('EE.csv', 'w', newline='\n', encoding='utf-8') 
    
  2. Нет смысла писать head с именами эстонских столбцов, а затем переименовывать колонны. Также обратите внимание, что в head.append(id) вы используете необъявленную переменную ( id ). Но это не так важно, так как я изменил весь этот раздел, написав target имена столбцов (см. Ниже).

  3. Когда вы пишете CSV-файл для чтения read_csv должно содержать фиксированное количество столбцов. Поэтому использование al oop для записи одного элемента является плохой практикой.

  4. Ваша инструкция list_specs = elem.findall(...) неверна, поскольку elem не задано в ток л oop. Вместо этого вы должны использовать member (но я решил эту деталь другим способом).

  5. Нет смысла создавать переменную только для того, чтобы использовать ее один раз. Более кратким и читабельным кодом является, например, hospital.append(member.findtext('nimi')).

  6. Чтобы избежать длинных выражений XPath , с повторяющейся начальной частью, я решил установить временную переменную "в середина этого пути, например, tgvLb = member.find('tegevusload/tegevusluba'), а затем используйте относительную XPath , начиная с этого узла.

  7. Ваша переименовать инструкция содержит одну необязательный столбец, а именно ознание_типа_ c. Вы читаете только 6 столбцы, а не 7 .

Поэтому измените среднюю часть кода на:

data = open('EE.csv', 'w', newline='\n', encoding='utf-8')
csvwriter = csv.writer(data, delimiter=';')
head = ['id', 'name', 'address', 'facility_type', 'site_address', 'list_specs']
csvwriter.writerow(head)
for member in root.findall('asutus'):
    hospital = []
    hospital.append(member.findtext('registrikood'))
    hospital.append(member.findtext('nimi'))
    hospital.append(member.findtext('aadress'))
    tgvLb = member.find('tegevusload/tegevusluba')
    hospital.append(tgvLb.findtext('haiglaliik_nimi'))
    tgvKoht = tgvLb.find('tegevuskohad/tegevuskoht')
    hospital.append(tgvKoht.findtext('aadress'))
    hospital.append(tgvKoht.findtext('teenused/teenus/nimi'))
    csvwriter.writerow(hospital)
data.close()
df = pd.read_csv(r'EE.csv', na_filter= False, delimiter=';')

и удалите df.rename из вашего кода.

...