Фильтрация XML-файла с использованием Python Minidom

Я пытаюсь отфильтровать XML-файл, используя минидом Python. Я хочу вернуть список адресов электронной почты (<wd:Email_Address>) на основе того, что этот адрес является РАБОЧИМ адресом электронной почты. Мне нужно использовать элемент <wd:ID wd:type="Communication_Usage_Type_ID">WORK</wd:ID> для фильтрации адресов электронной почты. Ниже приведен файл:

<?xml version = "1.0" encoding = "UTF-8"?>
<env:Envelope xmlns:env = "http://schema.xmlsoap.org/soap/envelope/">
<env:Body>
    <wd:Get_Working_Response xmlns:wd = "urn:com.workway/bsvc"
                             wd:version = "v40.1">
        <wd:Request_Criteria>
            <wd:Transaction_Log_Criteria_Data>
            </wd:Transaction_Log_Criteria_Data>
            <wd:Field_And_Parameter_Criteria_Data>
            </wd:Field_And_Parameter_Criteria_Data>
            <wd:Eligibility_Criteria_Data>
            </wd:Eligibility_Criteria_Data>
        </wd:Request_Criteria>
        <wd:Response_Filter>
        </wd:Response_Filter>
        <wd:Response_Group>
        </wd:Response_Group>
        <wd:Response_Results>
        </wd:Response_Results>
        <wd:Response_Data>
            <wd:Worker>
                <wd:Worker_Reference>
                    <wd:ID wd:type = "WID">787878787878787</wd:ID>
                    <wd:ID wd:type = "Employee_ID">123456</wd:ID>
                </wd:Worker_Reference>
                <wd:Worker_Descriptor>John Smith</wd:Worker_Descriptor>
                <wd:Worker_Data>
                    <wd:Worker_ID>123456</wd:Worker_ID>
                    <wd:User_ID>jsmith</wd:User_ID>
                    <wd:Personal_Data>
                            <wd:Email_Address_Data>
                                <wd:Email_Address>[email protected]</wd:Email_Address>
                                <wd:Usage_Data wd:Public = "0">
                                    <wd:Type_Data wd:Primary = "1">
                                        <wd:Type_Reference>
                                            <wd:ID wd:type = "WID">000000000000000</wd:ID>
                                            <wd:ID wd:type = "Communication_Usage_Type_ID">HOME</wd:ID>
                                        </wd:Type_Reference>
                                    </wd:Type_Data>
                                </wd:Usage_Data>
                                <wd:Email_Reference>
                                    <wd:ID wd:type = "WID">99999999999999999999999</wd:ID>
                                    <wd:ID wd:type = "Email_ID">EMAIL_REFERENCE-3-3960</wd:ID>
                                </wd:Email_Reference>
                                <wd:ID>EMAIL_REFERENCE-3-3960</wd:ID>
                            </wd:Email_Address_Data>
                            <wd:Email_Address_Data>
                                <wd:Email_Address>[email protected]</wd:Email_Address>
                                <wd:Usage_Data wd:Public = "1">
                                    <wd:Type_Data wd:Primary = "1">
                                        <wd:Type_Reference>
                                            <wd:ID wd:type = "WID">999999999999999999999999999</wd:ID>
                                            <wd:ID wd:type = "Communication_Usage_Type_ID">WORK</wd:ID>
                                        </wd:Type_Reference>
                                    </wd:Type_Data>
                                </wd:Usage_Data>
                                <wd:Email_Reference>
                                    <wd:ID wd:type = "WID">999999999999999999999999</wd:ID>
                                    <wd:ID wd:type = "Email_ID">EMAIL_REFERENCE-3-4017</wd:ID>
                                </wd:Email_Reference>
                                <wd:ID>EMAIL_REFERENCE-3-4017</wd:ID>
                            </wd:Email_Address_Data>
                            </wd:Personal_Data>
                    </wd:Worker_Data>   
            </wd:Worker>
        </wd:Response_Data>
    </wd:Get_Working_Response>
</env:Body>
</env:Envelope>

На данный момент мне удалось получить список (рабочих элементов), содержащий отфильтрованные элементы DOM для WORK. Я думаю, мне нужно использовать это, чтобы каким-то образом отфильтровать файл и поместить результаты в список (lNodesWithLevel2), который содержит только элементы Email_Address_Data для рабочих писем. Если у меня будут только эти элементы, я смогу получить значения Email_Address. Любая помощь приветствуется. Я открыт для использования других библиотек, если это проще. Вот что у меня есть на данный момент:

xmlDoc = minidom.parse('XML_Example.xml')

workelements =[]
lNodesWithLevel1 = xmlDoc.getElementsByTagName('wd:ID')
for mynodes in lNodesWithLevel1:
    if mynodes.firstChild.nodeValue == 'WORK':
        workelements.append(mynodes)

lNodesWithLevel2 = [lNode for lNode in xmlDoc.getElementsByTagName('wd:Email_Address_Data')
                 if lNode.getElementsByTagName('wd:ID') == li]

Если возможны другие пакеты, то lxml + xpath с пространствами имен будет простым решением.

LMC 08.03.2024 18:20

Спасибо за предложение. Я рассмотрю это. Я новичок в использовании Python и использую его только для конкретных проектов. Можно ли добиться того, чего я хочу, используя минидом? Или это слишком ограничено, учитывая структуру моего файла? Я должен упомянуть, что мне удалось извлечь другие аспекты файла, которые здесь не упомянуты, с помощью minidom.

Nick 08.03.2024 19:16
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
79
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Я хочу вернуть список адресов электронной почты (<wd:Email_Address>) на основе того, что этот адрес является РАБОЧИМ адресом электронной почты. Мне нужно использовать элемент <wd:ID wd:type="Communication_Usage_Type_ID">WORK</wd:ID> для фильтрации адресов электронной почты.

Использование основной библиотеки Python ElementTree

import xml.etree.ElementTree as ET

xml_data = '''<env:Envelope xmlns:env = "http://schema.xmlsoap.org/soap/envelope/">
<env:Body>
    <wd:Get_Working_Response xmlns:wd = "urn:com.workway/bsvc"
                             wd:version = "v40.1">
        <wd:Request_Criteria>
            <wd:Transaction_Log_Criteria_Data>
            </wd:Transaction_Log_Criteria_Data>
            <wd:Field_And_Parameter_Criteria_Data>
            </wd:Field_And_Parameter_Criteria_Data>
            <wd:Eligibility_Criteria_Data>
            </wd:Eligibility_Criteria_Data>
        </wd:Request_Criteria>
        <wd:Response_Filter>
        </wd:Response_Filter>
        <wd:Response_Group>
        </wd:Response_Group>
        <wd:Response_Results>
        </wd:Response_Results>
        <wd:Response_Data>
            <wd:Worker>
                <wd:Worker_Reference>
                    <wd:ID wd:type = "WID">787878787878787</wd:ID>
                    <wd:ID wd:type = "Employee_ID">123456</wd:ID>
                </wd:Worker_Reference>
                <wd:Worker_Descriptor>John Smith</wd:Worker_Descriptor>
                <wd:Worker_Data>
                    <wd:Worker_ID>123456</wd:Worker_ID>
                    <wd:User_ID>jsmith</wd:User_ID>
                    <wd:Personal_Data>
                            <wd:Email_Address_Data>
                                <wd:Email_Address>[email protected]</wd:Email_Address>
                                <wd:Usage_Data wd:Public = "0">
                                    <wd:Type_Data wd:Primary = "1">
                                        <wd:Type_Reference>
                                            <wd:ID wd:type = "WID">000000000000000</wd:ID>
                                            <wd:ID wd:type = "Communication_Usage_Type_ID">HOME</wd:ID>
                                        </wd:Type_Reference>
                                    </wd:Type_Data>
                                </wd:Usage_Data>
                                <wd:Email_Reference>
                                    <wd:ID wd:type = "WID">99999999999999999999999</wd:ID>
                                    <wd:ID wd:type = "Email_ID">EMAIL_REFERENCE-3-3960</wd:ID>
                                </wd:Email_Reference>
                                <wd:ID>EMAIL_REFERENCE-3-3960</wd:ID>
                            </wd:Email_Address_Data>
                            <wd:Email_Address_Data>
                                <wd:Email_Address>[email protected]</wd:Email_Address>
                                <wd:Usage_Data wd:Public = "1">
                                    <wd:Type_Data wd:Primary = "1">
                                        <wd:Type_Reference>
                                            <wd:ID wd:type = "WID">999999999999999999999999999</wd:ID>
                                            <wd:ID wd:type = "Communication_Usage_Type_ID">WORK</wd:ID>
                                        </wd:Type_Reference>
                                    </wd:Type_Data>
                                </wd:Usage_Data>
                                <wd:Email_Reference>
                                    <wd:ID wd:type = "WID">999999999999999999999999</wd:ID>
                                    <wd:ID wd:type = "Email_ID">EMAIL_REFERENCE-3-4017</wd:ID>
                                </wd:Email_Reference>
                                <wd:ID>EMAIL_REFERENCE-3-4017</wd:ID>
                            </wd:Email_Address_Data>
                            </wd:Personal_Data>
                    </wd:Worker_Data>   
            </wd:Worker>
        </wd:Response_Data>
    </wd:Get_Working_Response>
</env:Body>
</env:Envelope>
'''

# Parse the XML data
root = ET.fromstring(xml_data)

# Namespace dictionary
ns = {'wd': 'urn:com.workway/bsvc'}

for email_elem_root in root.findall('.//wd:Email_Address_Data', ns):
    email = email_elem_root.find('./wd:Email_Address', ns).text
    should_collect: bool = email_elem_root.find('.//wd:ID[@wd:type = "Communication_Usage_Type_ID"]', ns).text == 'WORK'
    if should_collect:
        print("Collecting Email:", email)
    else:
        print("Ignoring Email:", email)

выход

Ignoring Email: [email protected]
Collecting Email: [email protected]

Это очень круто и кажется гораздо более гибким, чем минидом. Я тоже собираюсь попробовать это. Спасибо!

Nick 09.03.2024 00:47

Вот пример использования lxml. Если вы собираетесь работать с XML, XPath стоит потраченного времени на изучение.

from lxml import etree

tree = etree.parse("input.xml")
xpath = "//wd:Email_Address_Data[wd:Usage_Data//wd:ID[@wd:type='Communication_Usage_Type_ID']='WORK']/wd:Email_Address"
work_emails = [email_elem.text for email_elem in tree.xpath(xpath, namespaces = {"wd": "urn:com.workway/bsvc"})]

print(work_emails)

Это выводит:

['[email protected]']

Спасибо и за это! Я обязательно попробую это решение. Мне нужно больше экспериментировать с lxml и etree.

Nick 09.03.2024 00:48
Ответ принят как подходящий

С помощью xml.dom.minidom вы можете:

import xml.dom.minidom

xmlDoc = xml.dom.minidom.parse('XML_Example.xml')

business = []
for email in xmlDoc.getElementsByTagName("wd:Email_Address_Data"):
    for t in email.getElementsByTagName("wd:ID"):
        if t.getAttribute("wd:type") == "Communication_Usage_Type_ID":
            business_mail = t.firstChild.nodeValue
    for m in email.getElementsByTagName("wd:Email_Address"):
        if business_mail == "WORK":
            business.append(m.firstChild.nodeValue)

print("WORK EMAILs:", business) 

Выход:

WORK EMAILs: ['[email protected]']

Только что попробовал это с полным файлом, и он возвращает ожидаемые результаты. Еще раз спасибо!

Nick 09.03.2024 00:57

Если предпочтение ограничено только minidom, рассмотрите возможность запуска списков/диктовок на вызовах getElementsByTagName() для извлечения данных каждого электронного письма каждого работника:

def get_nodes(el):
   return {
      f"{t.tagName}{i}": t.firstChild.nodeValue.strip()
      for i, t in enumerate(el.getElementsByTagName('*'))
   }

worker_data = [
   {i: [get_nodes(e) for e in w.getElementsByTagName('wd:Email_Address_Data')]
   for i, w in enumerate(xmlDoc.getElementsByTagName('wd:Worker'))}
]


pprint(worker_data)
# [{0: [{'wd:Email_Address0': '[email protected]',
#       'wd:Email_Reference6': '',
#       'wd:ID4': '000000000000000',
#       'wd:ID5': 'HOME',
#       'wd:ID7': '99999999999999999999999',
#       'wd:ID8': 'EMAIL_REFERENCE-3-3960',
#       'wd:ID9': 'EMAIL_REFERENCE-3-3960',
#       'wd:Type_Data2': '',
#       'wd:Type_Reference3': '',
#       'wd:Usage_Data1': ''},
#      {'wd:Email_Address0': '[email protected]',
#       'wd:Email_Reference6': '',
#       'wd:ID4': '999999999999999999999999999',
#       'wd:ID5': 'WORK',
#       'wd:ID7': '999999999999999999999999',
#       'wd:ID8': 'EMAIL_REFERENCE-3-4017',
#       'wd:ID9': 'EMAIL_REFERENCE-3-4017',
#       'wd:Type_Data2': '',
#       'wd:Type_Reference3': '',
#       'wd:Usage_Data1': ''}]}]

И чтобы получить все рабочие электронные письма работника:

for wd in worker_data:
   for k, v in wd.items():
      for e in v:
          if 'WORK' in e.values():
              print("Worker: " + e["wd:Email_Address0"])

# Worker Email:[email protected]

Online Demo

Другие вопросы по теме