Saya ingin menggunakan metode "findall" untuk menemukan beberapa elemen dari file xml sumber dalam modul ElementTree.
Namun, file xml sumber (test.xml) memiliki namespace. Saya memotong sebagian file xml sebagai sampel:
<?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>
Contoh 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 bisa berfungsi, karena ada namespace "{http://www.test.com}", sangat tidak nyaman untuk menambahkan namespace di depan setiap tag.
Bagaimana saya bisa mengabaikan namespace ketika menggunakan metode "find", "findall" dan sebagainya?
python
namespaces
find
elementtree
findall
KevinLeng
sumber
sumber
tree.findall("xmlns:DEAL_LEVEL/xmlns:PAID_OFF", namespaces={'xmlns': 'http://www.test.com'})
cukup nyaman?tree.findall("{0}DEAL_LEVEL/{0}PAID_OFF".format('{http://www.test.com}'))
Jawaban:
Alih-alih memodifikasi dokumen XML itu sendiri, yang terbaik adalah menguraikannya dan kemudian memodifikasi tag di hasilnya. Dengan cara ini Anda dapat menangani beberapa ruang nama dan alias namespace:
Ini didasarkan pada diskusi di sini: http://bugs.python.org/issue18304
Pembaruan:
rpartition
alih-alihpartition
memastikan Anda mendapatkan nama tagpostfix
meskipun tidak ada namespace. Dengan demikian Anda bisa menyingkatnya:sumber
et.findall('{*}sometag')
. Dan itu juga merusak pohon elemen itu sendiri, bukan hanya "melakukan pencarian mengabaikan ruang nama saja, tanpa mem-parsing ulang dokumen dll, mempertahankan informasi namespace". Nah, untuk kasus ini Anda perlu mengamati untuk beralih melalui pohon, dan lihat sendiri, jika simpul cocok dengan keinginan Anda setelah menghapus namespace.Jika Anda menghapus atribut xmlns dari xml sebelum menguraikannya maka tidak akan ada namespace yang ditambahkan ke setiap tag di pohon.
sumber
=
tanda sama dengan.Jawaban sejauh ini secara eksplisit menempatkan nilai namespace dalam skrip. Untuk solusi yang lebih umum, saya lebih suka mengekstrak namespace dari xml:
Dan gunakan dalam metode find:
sumber
namespace
Berikut ini adalah ekstensi untuk jawaban nonagon, yang juga menghilangkan atribut namespaces dari:
UPDATE: ditambahkan
list()
agar iterator berfungsi (diperlukan untuk Python 3)sumber
Memperbaiki jawaban oleh ericspod:
Alih-alih mengubah mode parse secara global, kita dapat membungkusnya dalam objek yang mendukung konstruksi.
Ini kemudian dapat digunakan sebagai berikut
Keindahan cara ini adalah tidak mengubah perilaku apa pun untuk kode yang tidak terkait di luar blok with. Saya akhirnya membuat ini setelah mendapatkan kesalahan di perpustakaan yang tidak terkait setelah menggunakan versi oleh ericspod yang juga kebetulan menggunakan expat.
sumber
xml.etree.ElementTree.XMLParser
entah bagaimana dioptimalkan dan monyet-patchingexpat
sama sekali tidak berpengaruh.Anda dapat menggunakan konstruksi pemformatan string yang elegan juga:
atau, jika Anda yakin bahwa PAID_OFF hanya muncul di satu tingkat di hierarki:
sumber
Jika Anda menggunakan
ElementTree
dan tidak,cElementTree
Anda dapat memaksa Expat untuk mengabaikan pemrosesan namespace dengan menggantiParserCreate()
:ElementTree
mencoba menggunakan Expat dengan memanggilParserCreate()
tetapi tidak memberikan opsi untuk tidak memberikan string pemisah namespace, kode di atas akan menyebabkannya diabaikan tetapi diperingatkan ini dapat merusak hal lain.sumber
ElementTree.fromstring(s, parser=None)
saya mencoba mengoper parser untuk itu.Saya mungkin terlambat untuk ini, tetapi saya rasa
re.sub
itu bukan solusi yang baik.Namun penulisan ulang
xml.parsers.expat
tidak berfungsi untuk versi Python 3.x,Penyebab utamanya adalah
xml/etree/ElementTree.py
lihat di bagian bawah kode sumberYang agak menyedihkan.
Solusinya adalah dengan menyingkirkannya terlebih dahulu.
Diuji dengan Python 3.6.
try
Pernyataan coba berguna jika suatu saat dalam kode Anda Anda memuat ulang atau mengimpor modul dua kali Anda mendapatkan beberapa kesalahan aneh sepertiSialan kode sumber etree terlihat sangat berantakan.
sumber
Mari kita gabungkan jawaban nonagon dengan jawaban mzjn untuk pertanyaan terkait :
Menggunakan fungsi ini kita:
Buat iterator untuk mendapatkan ruang nama dan objek pohon yang diuraikan .
Iterate atas iterator yang dibuat untuk mendapatkan dict namespaces yang nantinya bisa kita lewati di masing
find()
- masing ataufindall()
dipanggil sebagai disarankan oleh iMom0 .Saya pikir ini adalah pendekatan terbaik di sekitar karena tidak ada manipulasi baik dari sumber XML atau hasil
xml.etree.ElementTree
keluaran yang diuraikan apa pun yang terlibat.Saya juga ingin menghargai jawaban barny dengan memberikan bagian penting dari teka-teki ini (bahwa Anda bisa mendapatkan root yang diurai dari iterator). Sampai saya benar-benar melintasi pohon XML dua kali di aplikasi saya (satu kali untuk mendapatkan ruang nama, kedua untuk root).
sumber
find()
danfindall()
. Anda cukup memberi makan metode tersebut dengan dict namespaces dariparse_xml()
dan menggunakan awalan namespace di kueri Anda. Misalnya:et_element.findall(".//some_ns_prefix:some_xml_tag", namespaces=xml_namespaces)