Равенство элементов Lxml с пространствами имен - PullRequest
8 голосов
/ 31 марта 2011

Я пытаюсь использовать Lxml для анализа содержимого документа .docx.Я понимаю, что lxml заменяет префиксы пространства имен фактическим пространством имен, однако это делает очень трудной проверку того, с каким тегом элемента я работаю.Я хотел бы иметь возможность сделать что-то вроде

if (someElement.tag == "w:p"):

, но поскольку lxml настаивает на предварительном заполнении пространства имен, мне нужно было бы сделать что-то вроде

if (someElemenet.tag == "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}p'):

или выполнить поискполного имени пространства имен из атрибута nsmap элемента, подобного этому

targetTag = "{%s}p" % someElement.nsmap['w']
if (someElement.tag == targetTag):

Если бы был более простой способ убедить lxml либо

  1. Дайте мне строку тега без пространства имендобавленный к нему, я могу использовать атрибут префикса вместе с этой информацией, чтобы проверить, с каким тегом я работаю, ИЛИ
  2. Просто дайте мне строку тега, используя префикс

Это будетсохранить много нажатий клавиш при написании этого парсера.Это возможно?Я что-то упустил в документации?

Ответы [ 5 ]

22 голосов
/ 31 марта 2011

Возможно использовать local-name () :

import lxml.etree as ET
tree = ET.fromstring('<root xmlns:f="foo"><f:test/></root>')
elt=tree[0]
print(elt.xpath('local-name()'))
# test
5 голосов
/ 31 марта 2011

Я не смог найти способ получить имя тега без пространства имен из элемента - lxml рассматривает полную часть пространства имен имени тега. Вот несколько вариантов, которые могут помочь ..

Вы также можете использовать класс QName для создания тега пространства имен для сравнения:

import lxml.etree
from lxml.etree import QName

tree = lxml.etree.fromstring('<root xmlns:f="foo"><f:test/></root>')
qn = QName(tree.nsmap['f'], 'test')
assert tree[0].tag == qn

Если вам нужно пустое имя тега, вам нужно написать служебную функцию для его извлечения:

def get_bare_tag(elem):
    return elem.tag.rsplit('}', 1)[-1]

assert get_bare_tag(tree[0]) == 'test'

К сожалению, насколько мне известно, вы не можете искать теги с "любым пространством имен" (например, {*}test), используя методы lxml xpath / find.

Обновлено : обратите внимание, что lxml не будет создавать тег, содержащий только { or } - он вызовет ValueError: неверное имя тега, поэтому можно с уверенностью предположить, что элемент, имя тега которого начинается с { сбалансировано.

lxml.etree.Element('{foo')
ValueError: Invalid tag name
4 голосов
/ 14 октября 2016

etree.Qname сможет получить то, что вы хотите.

from lxml import etree

# [...]

tag = etree.QName(someElement)

print(tag.namespace, tag.localname)

Для вашего примера тега будет выведено:

http://schemas.openxmlformats.org/wordprocessingml/2006/main p

Обратите внимание, что QName будет принимать либо объект Element, либо строку (например, из Element.tag).

И, как вы заметили, вы также можете использовать Element.nsmap для сопоставления произвольного префикса с пространством имен.

Так что-то вроде этого:

if tag.namespace == someElement.nsmap["w"] and tag.localname == "p":
2 голосов
/ 31 марта 2011

Чтобы сэкономить время при поиске тегов большого объема, таких как p (абзац, я полагаю) в docx или c (ячейка) в xlsx, обычно полный тег устанавливается один раз на глобальном уровне или на уровне класса :

WPML_URI = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
tag_p = WPML_URI + 'p'
tag_t = WPML_URI + 't'

Я никогда не видел объяснения, почему кто-то хотел бы использовать QName().

В другом направлении, учитывая полный тег, вы можете легко извлечь базовый тег:

base_tag = full_tag.rsplit("}", 1)[-1]

1 голос
/ 03 января 2012

Я не эксперт по Python, но у меня тоже была эта проблема (файлы "Контакты" Windows 7).Я написал следующую функцию для системы lxml.

Эта функция берет элемент и возвращает его тег с префиксом, замененным из тега ns файла.

from lxml import etree

def denstag(ee):
  tag = ee.tag
  for ns in ee.nsmap:
    prefix = "{"+ee.nsmap[ns]+"}"
    if tag.startswith(prefix):               
      return ns+":"+tag[len(prefix):]
  return tag
...