Solución:
Si root.nsmap
contiene la table
prefijo de espacio de nombres, entonces podría:
root.xpath('.//table:table', namespaces=root.nsmap)
findall(path)
acepta {namespace}name
sintaxis en lugar de namespace:name
. Por lo tanto path
debe ser preprocesado usando el diccionario de espacio de nombres para el {namespace}name
formulario antes de pasarlo a findall()
.
Quizás lo primero que se debe notar es que los espacios de nombres están definidos en Nivel de elemento, no Nivel de documento.
Sin embargo, la mayoría de las veces, todos los espacios de nombres se declaran en el elemento raíz del documento (office:document-content
aquí), lo que nos ahorra analizarlo todo para recopilar información interna xmlns
ámbitos.
Entonces, un elemento nsmap incluye:
- un espacio de nombres predeterminado, con
None
prefijo (no siempre) - todos los espacios de nombres de los antepasados, a menos que se anulen.
Si, como mencionó ChrisR, el espacio de nombres predeterminado no es compatible, puede usar una comprensión de dict para filtrarlo en una expresión más compacta.
Tiene una sintaxis ligeramente diferente para xpath y ElementPath.
Así que aquí está el código que podría usar para obtener todas las filas de su primera tabla (probado con: lxml=3.4.2
):
import zipfile
from lxml import etree
# Open and parse the document
zf = zipfile.ZipFile('spreadsheet.ods')
tree = etree.parse(zf.open('content.xml'))
# Get the root element
root = tree.getroot()
# get its namespace map, excluding default namespace
nsmap = {k:v for k,v in root.nsmap.iteritems() if k}
# use defined prefixes to access elements
table = tree.find('.//table:table', nsmap)
rows = table.findall('table:table-row', nsmap)
# or, if xpath is needed:
table = tree.xpath('//table:table', namespaces=nsmap)[0]
rows = table.xpath('table:table-row', namespaces=nsmap)
Esta es una forma de obtener todos los espacios de nombres en el documento XML (y suponiendo que no haya conflicto de prefijos).
Utilizo esto cuando analizo documentos XML donde sé de antemano cuáles son las URL del espacio de nombres, y solo el prefijo.
doc = etree.XML(XML_string)
# Getting all the name spaces.
nsmap = {}
for ns in doc.xpath('//namespace::*'):
if ns[0]: # Removes the None namespace, neither needed nor supported.
nsmap[ns[0]] = ns[1]
doc.xpath('//prefix:element', namespaces=nsmap)