BeautifulSoup — xml — find_next: ограничение одним атрибутом

#python #arrays #xml #beautifulsoup #attributes

Вопрос:

любая помощь будет признательна! Используя приведенный ниже пример XML-файла, я получаю неверный вывод.

 Incorrect output:  Emp_F_Name: Jill Emp_M_Name: H Emp_L_Name: Jones  Desired output: Emp_F_Name: Jill Emp_M_Name: None or NULL Emp_L_Name: Jones  

Я не уверен, почему функция find_next выходит за рамки объявленного атрибута (сотрудник).

 lt;?xml version="1.0" encoding="utf-8"?gt;  lt;org value="Tech"gt;  lt;employeegt;  lt;namegt;  lt;familygt;Joneslt;/familygt;  lt;givengt;Jilllt;/givengt;  lt;/namegt;  lt;/employeegt;  lt;managergt;  lt;namegt;  lt;familygt;Fisherlt;/familygt;  lt;givengt;Juniorlt;/givengt;  lt;givengt;Hlt;/givengt;  lt;/namegt;  lt;/managergt;  lt;/orggt;  

Вот код, который я использую.

 employee = soup.find("employee")  for i in employee.find_all('name'):  fname = employee.find('given')  print("Emp_F_Name: ", fname.get_text())    mname = fname.find_next('given')  print("Emp_M_Name: ", mname.get_text())    lname = employee.find('family')  print("Emp_L_Name: ", lname.get_text())  

Когда я запускаю тот же код, но для менеджера, он, кажется, работает.

 manager = soup.find("manager")  

Комментарии:

1. Поскольку вы имеете дело с xml, вам, вероятно, лучше использовать синтаксический анализатор xml, такой как lxml.

Ответ №1:

Если структура почти идентична, вы можете попробовать «найти все ()» все элементы given и проверить, есть ли только один или два.

 given= i.find_all('given') fname = given[0] print("Emp_F_Name: ", fname.get_text())   mname = given[1].get_text() if len(given) gt; 1 else None print("Emp_M_Name: ", mname)  

Думаю, нет необходимости повторять employee , но если это так, вам следует использовать свой i

Пример

 import requests from bs4 import BeautifulSoup  xml='''lt;?xml version="1.0" encoding="utf-8"?gt;  lt;org value="Tech"gt;  lt;employeegt;  lt;namegt;  lt;familygt;Joneslt;/familygt;  lt;givengt;Jilllt;/givengt;  lt;/namegt;  lt;/employeegt;  lt;managergt;  lt;namegt;  lt;familygt;Fisherlt;/familygt;  lt;givengt;Juniorlt;/givengt;  lt;givengt;Hlt;/givengt;  lt;/namegt;  lt;/managergt;  lt;/orggt;'''  soup = BeautifulSoup(xml, 'lxml')  employee = soup.find("employee")  for i in employee.find_all('name'):  given= i.find_all('given')  fname = given[0]  print("Emp_F_Name: ", fname.get_text())    mname = given[1].get_text() if len(given) gt; 1 else None  print("Emp_M_Name: ", mname)    lname = i.find('family')  print("Emp_L_Name: ", lname.get_text())  

Выход

 Emp_F_Name: Jill Emp_M_Name: None Emp_L_Name: Jones  

Alternativ

Изолировать employee как отдельное дерево для работы с find_next() :

 employee = BeautifulSoup(str(soup.find("employee")), 'lxml')  for i in employee.find_all('name'):  fname = i.find('given')  print("Emp_F_Name: ", fname.get_text())    mname = fname.find_next('given').get_text() if fname.find_next('given') else None  print("Emp_M_Name: ", mname)    lname = i.find('family')  print("Emp_L_Name: ", lname.get_text())  

Комментарии:

1. Оба сценария сработали! Это чрезвычайно полезно. Спасибо!

Ответ №2:

Использование синтаксического анализатора XML:(нет необходимости во внешней библиотеке)

 import xml.etree.ElementTree as ET   xml = '''lt;?xml version="1.0" encoding="UTF-8"?gt; lt;org value="Tech"gt;  lt;employeegt;  lt;namegt;  lt;familygt;Joneslt;/familygt;  lt;givengt;Jilllt;/givengt;  lt;/namegt;  lt;/employeegt;  lt;managergt;  lt;namegt;  lt;familygt;Fisherlt;/familygt;  lt;givengt;Juniorlt;/givengt;  lt;givengt;Hlt;/givengt;  lt;/namegt;  lt;/managergt; lt;/orggt;'''    attrs = {'Emp_F_Name':'given',  'Emp_L_Name':'family',  'Emp_M_Name': None}  root = ET.fromstring(xml) name = root.find('.//name') for k,v in attrs.items():  print(f'{k}: {name.find(v).text if v else None}')  

выход

 Emp_F_Name: Jill Emp_L_Name: Jones Emp_M_Name: None  

Комментарии:

1. Это потрясающе. Спасибо за пример ElementTree!

2. @Jaytee Рад, что мне удалось помочь. Пожалуйста, примите ответ.