Python lxml и начало с xpath?

63
6

Разумно новичок в Python и невероятно новый для XML, гораздо меньше пытается его разобрать. Мне нужно определить узлы кластера из серии файлов конфигурации XML в веб-формате. Я выяснил, как получить 75% из них; теперь, я иду за краевым кейсом, и я не уверен, как действовать дальше.

Файлы конфигурации Weblogic XML начинаются с определений пространства имен, а затем с несколькими дочерними элементами, некоторые из которых имеют собственные дочерние элементы.

Элементом, который меня интересует, является <server> который обычно имеет подэлемент, называемый <listen-address> содержащий имя хоста, которое я ищу.

Следуя парадигме "мы любим стандарты, у нас их много", эта модель не работает повсюду. Там, где это не работает, мне нужно найти подэлемент <server> называемый <machine>. Этот элемент содержит псевдоним, который расширяется в другом корневом дочернем элементе на том же уровне, что и <server>.

Итак, картина стоит 1000 слов:

Примечание: пытался избежать отправки тонны xml; но, судя по всему, мои попытки экономии полосы пропускания затрудняют задачу. Извиняюсь. Я обрезал ненужные или связанные с безопасностью дочерние элементы.

<?xml version='1.0' encoding='UTF-8'?>
<domain xmlns="http://xmlns.oracle.com/weblogic/domain" xmlns:sec="http://xmlns.oracle.com/weblogic/security" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:wls="http://xmlns.oracle.com/weblogic/security/wls" xsi:schemaLocation="http://xmlns.oracle.com/weblogic/security/wls http://xmlns.oracle.com/weblogic/security/wls/1.0/wls.xsd http://xmlns.oracle.com/weblogic/domain http://xmlns.oracle.com/weblogic/1.0/domain.xsd http://xmlns.oracle.com/weblogic/security/xacml http://xmlns.oracle.com/weblogic/security/xacml/1.0/xacml.xsd http://xmlns.oracle.com/weblogic/security/providers/passwordvalidator http://xmlns.oracle.com/weblogic/security/providers/passwordvalidator/1.0/passwordvalidator.xsd http://xmlns.oracle.com/weblogic/security http://xmlns.oracle.com/weblogic/1.0/security.xsd">
<name>EDIServices_Domain</name>
<domain-version>12.2.1.0.0</domain-version>
<security-configuration>
<name>EDIServices_Domain</name>
<default-realm>myrealm</default-realm>
<node-manager-username>snipped</node-manager-username>
<node-manager-password-encrypted>not_really_my_pwd</node-manager-password-encrypted>
</security-configuration>
<server>
<name>EDIServices_AS</name>
<listen-port>60010</listen-port>
<web-server>
<name>EDIServices_AS</name>
<web-server-log>
<file-name>/opt/app/oracle/user_projects/logs/EDIServices_Domain/access.log</file-name>
<file-count>24</file-count>
<file-min-size>10000</file-min-size>
<rotate-log-on-startup>true</rotate-log-on-startup>
<elf-fields>c-ip cs-uri date time cs-method cs-uri sc-status</elf-fields>
<log-file-format>common</log-file-format>
</web-server-log>
</web-server>
<listen-address></listen-address>
</server>
<server>
<name>EDIServices_MS1</name>
<machine>EDIServices_MC1</machine>
<listen-port>60014</listen-port>
<cluster>EDIServices_CS</cluster>
<listen-address></listen-address>
</server>
<server>
<name>EDIServices_MS2</name>
<machine>EDIServices_MC2</machine>
<listen-port>60014</listen-port>
<cluster>EDIServices_CS</cluster>
<listen-address></listen-address>
</server>
<cluster>
<name>EDIServices_CS</name>
<cluster-messaging-mode>unicast</cluster-messaging-mode>
<dynamic-servers>
<maximum-dynamic-server-count>0</maximum-dynamic-server-count>
</dynamic-servers>
</cluster>
<production-mode-enabled>true</production-mode-enabled>
<configuration-version>12.2.1.0.0</configuration-version>
<machine xsi:type="unix-machineType">
<name>EDIServices_MC1</name>
<node-manager>
<name>EDIServices_MC1</name>
<nm-type>SSL</nm-type>
<listen-address>host001</listen-address>
<listen-port>7001</listen-port>
</node-manager>
</machine>
<machine xsi:type="unix-machineType">
<name>EDIServices_MC2</name>
<node-manager>
<name>EDIServices_MC2</name>
<listen-address>host002</listen-address>
<listen-port>7001</listen-port>
</node-manager>
</machine>
</domain>

Итак, запустив его в "нормальной" конфигурации, я получаю:

$ ./lxml configs/EntsvcSoa_Domain_config.xml  
EntsvcSoa_AS => host003.myco.com
EntsvcSoa_MS1 => host004.myco.com
EntsvcSoa_MS2 => host005.myco.com

Запустив его против abi-normal config, я в настоящее время получаю:

$ ./lxml configs/EDIServices_Domain_config.xml
EDIServices_MS1 => EDIServices_MC1
EDIServices_MS2 => EDIServices_MC2

Используя приведенные выше примеры, я хотел бы перевести EDIServices_MC1 и EDIServices_MC2 на host001 и host002 соответственно.

Первичный цикл:

ПРИМЕЧАНИЕ: в интересах завершения, здесь весь сценарий:

#!/usr/bin/env python3

from lxml import etree
import re
import sys
import os
import pprint

if len(sys.argv) != 2:
print('Format: ./wl_clusters ${weblogic_config_file}')
sys.exit(1)

if not os.path.isfile(sys.argv[1]):
print('Format: ./wl_clusters ${weblogic_config_file}')
sys.exit(2)

config = sys.argv[1]

# set up lxml structures
tree = etree.parse(config)
root = tree.getroot()

# set up xml namespace govno
ns = root.nsmap[None]
namespaces = { 'ns': ns }

for server in root.findall('ns:server', namespaces):
cs = server.find('ns:cluster', namespaces)
if cs is None:
continue
# cluster_name = server.find('ns:cluster', namespaces).text
cluster_name = cs.text
listen_address = server.find('ns:listen-address', namespaces)
server_name = listen_address.text
if server_name is None:
machine = server.find('ns:machine', namespaces)
if machine is None:
continue
else:
server_name = machine.text

print("%-15s => %s" % (cluster_name, server_name))

(мне потребовалось несколько дней, чтобы написать 12 строк кода... хорошо, что я не делаю этого для жизни :))

Перефразированный, мне нужно найти под дочерним элементом <machine>, имя которого соответствует имени под соответствующим дочерним <server>. Из некоторых примеров в Интернете я считаю, что xpath может помочь, но я не смог получить даже простые примеры работы.

спросил(а) 2021-01-25T13:09:23+03:00 4 месяца, 4 недели назад
1
1 ответ
-4

Вот пример использования xpath с вашим файлом. Я обрабатываю каждый элемент "домен" и показываю "имена *", который работает, так как они находятся на одном уровне внутри.

from lxml import etree
xp = etree.XPath('./*/name')
for event, elem in etree.iterparse("delme.xml", events=('end',), tag="domain"):
for name in xp(elem):
print(name.text)

Вывод:

EDIServices_MS1
EDIServices_MS2
EDIServices_MC1
EDIServices_MC2

Изменение: создала грубую xpath для соответствия обоим шаблонам.

ответил(а) 2021-01-25T13:09:23+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема