Saya ingin menggunakan metode "findall" untuk menemukan beberapa elemen dari sumber file xml di ElementTree modul.
Namun, sumber file xml (test.xml) memiliki namespace. Aku memotong bagian dari file xml sebagai contoh:
<?xml version="1.0" encoding="iso-8859-1"?>
<XML_HEADER xmlns="http://www.test.com">
<TYPE>Updates</TYPE>
<DATE>9/26/2012 10:30:34 AM</DATE>
<COPYRIGHT_NOTICE>All Rights Reserved.</COPYRIGHT_NOTICE>
<LICENSE>newlicense.htm</LICENSE>
<DEAL_LEVEL>
<PAID_OFF>N</PAID_OFF>
</DEAL_LEVEL>
</XML_HEADER>
Sampel kode python di bawah ini:
from xml.etree import ElementTree as ET
tree = ET.parse(r"test.xml")
el1 = tree.findall("DEAL_LEVEL/PAID_OFF") # Return None
el2 = tree.findall("{http://www.test.com}DEAL_LEVEL/{http://www.test.com}PAID_OFF") # Return <Element '{http://www.test.com}DEAL_LEVEL/PAID_OFF' at 0xb78b90>
Meskipun hal ini dapat bekerja, karena ada namespace "{http://www.test.com", it's sangat nyaman untuk menambahkan namespace di depan masing-masing tag.
Bagaimana bisa aku mengabaikan namespace ketika menggunakan metode "cari", "findall" dan sebagainya?
Bukan memodifikasi dokumen XML itu sendiri,'s terbaik untuk mengurai dan kemudian memodifikasi kategori dalam hasil. Dengan cara ini anda dapat menangani beberapa namespaces dan nama alias:
from io import StringIO # for Python 2 import from StringIO instead
import xml.etree.ElementTree as ET
# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
prefix, has_namespace, postfix = el.tag.partition('}')
if has_namespace:
el.tag = postfix # strip all namespaces
root = it.root
Hal ini didasarkan pada pembahasan berikut ini: http://bugs.python.org/issue18304
Jika anda menghapus atribut xmlns dari xml sebelum parsing itu maka tidak ada't menjadi namespace ditambahkan untuk setiap tag di pohon.
import re
xmlstring = re.sub(' xmlns="[^"]+"', '', xmlstring, count=1)
Jawaban yang sejauh ini secara eksplisit menempatkan namespace nilai dalam script. Untuk lebih generik solusi, saya lebih suka ekstrak namespace dari xml:
import re
def get_namespace(element):
m = re.match('\{.*\}', element.tag)
return m.group(0) if m else ''
Dan menggunakannya dalam menemukan metode:
namespace = get_namespace(tree.getroot())
print tree.find('./{0}parent/{0}version'.format(namespace)).text
Berikut ini's ekstensi untuk nonagon's jawaban, yang juga strip namespaces off atribut:
from StringIO import StringIO
import xml.etree.ElementTree as ET
# instead of ET.fromstring(xml)
it = ET.iterparse(StringIO(xml))
for _, el in it:
if '}' in el.tag:
el.tag = el.tag.split('}', 1)[1] # strip all namespaces
for at in el.attrib.keys(): # strip namespaces of attributes too
if '}' in at:
newat = at.split('}', 1)[1]
el.attrib[newat] = el.attrib[at]
del el.attrib[at]
root = it.root
Peningkatan pada jawaban oleh ericspod:
Bukan mengubah mengurai mode secara global bisa kita bungkus ini di sebuah objek yang mendukung dengan membangun.
from xml.parsers import expat
class DisableXmlNamespaces:
def __enter__(self):
self.oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: self.oldcreate(encoding, None)
def __exit__(self, type, value, traceback):
expat.ParserCreate = self.oldcreate
Ini kemudian dapat digunakan sebagai berikut
import xml.etree.ElementTree as ET
with DisableXmlNamespaces():
tree = ET.parse("test.xml")
Keindahan dari cara ini adalah bahwa hal itu tidak mengubah perilaku yang tidak terkait kode luar dengan blok. Akhirnya saya membuat ini setelah mendapatkan kesalahan dalam berhubungan perpustakaan setelah menggunakan versi ericspod yang juga terjadi untuk menggunakan expat.
Anda dapat menggunakan elegan string pemformatan membangun serta:
ns='http://www.test.com'
el2 = tree.findall("{%s}DEAL_LEVEL/{%s}PAID_OFF" %(ns,ns))
atau, jika anda're yakin bahwa PAID_OFF hanya muncul di satu tingkat di pohon:
el2 = tree.findall(".//{%s}PAID_OFF" % ns)
Jika anda're menggunakan ElementTree
dan tidak cElementTree
anda dapat memaksa Expat untuk mengabaikan namespace pengolahan dengan mengganti ParserCreate()
:
from xml.parsers import expat
oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)
ElementTree
mencoba untuk menggunakan Expat dengan memanggil ParserCreate()
tetapi tidak memberikan pilihan untuk tidak memberikan namespace pemisah string, kode di atas akan menyebabkan itu untuk mengabaikan tapi berhati-hatilah, ini bisa memecahkan hal-hal lain.
Let's menggabungkan nonagon's jawaban dengan mzjn's jawaban untuk pertanyaan terkait:
def parse_xml(xml_path: Path) -> Tuple[ET.Element, Dict[str, str]]:
xml_iter = ET.iterparse(xml_path, events=["start-ns"])
xml_namespaces = dict(prefix_namespace_pair for _, prefix_namespace_pair in xml_iter)
return xml_iter.root, xml_namespaces
Dengan menggunakan fungsi ini kita:
Membuat sebuah iterator untuk mendapatkan kedua namespaces dan parsing tree objek.
Iterate atas dibuat iterator untuk mendapatkan namespaces dict yang kita dapat
kemudian lulus dalam masing-masing find()
atau findall()
call sebagai sugested oleh
iMom0.
Kembali diurai pohon's elemen root objek dan namespaces.
Saya pikir ini adalah pendekatan yang terbaik di sekitar karena ada's tidak ada manipulasi baik dari sumber XML atau dihasilkan diurai xml.etree.ElementTree
output apapun yang terlibat.
I'd seperti juga untuk kredit barny's jawaban dengan menyediakan bagian penting dari teka-teki ini (bahwa anda bisa mendapatkan diurai akar dari iterator). Sampai aku benar-benar dilalui pohon XML dua kali di aplikasi saya (sekali untuk mendapatkan namespaces, kedua untuk root).
Saya mungkin terlambat untuk ini, tapi saya tidak berpikir kembali.sub
adalah solusi yang baik.
Namun menulis ulang xml.parsers.expat
tidak bekerja untuk Python 3.x versi,
Penyebab utama adalah xml/etree/ElementTree.py
lihat bagian bawah dari kode sumber
# Import the C accelerators
try:
# Element is going to be shadowed by the C implementation. We need to keep
# the Python version of it accessible for some "creative" by external code
# (see tests)
_Element_Py = Element
# Element, SubElement, ParseError, TreeBuilder, XMLParser
from _elementtree import *
except ImportError:
pass
Yang agak sedih.
Solusinya adalah dengan menyingkirkan terlebih dahulu.
import _elementtree
try:
del _elementtree.XMLParser
except AttributeError:
# in case deleted twice
pass
else:
from xml.parsers import expat # NOQA: F811
oldcreate = expat.ParserCreate
expat.ParserCreate = lambda encoding, sep: oldcreate(encoding, None)
Diuji pada Python 3.6.
Coba coba-coba
pernyataan ini berguna dalam kasus di suatu tempat dalam kode anda anda reload atau mengimpor modul dua kali anda mendapatkan beberapa kesalahan aneh seperti
btw sialan dpohon source code terlihat benar-benar berantakan.