Python'da XML'i nasıl ayrıştırabilirim?


1003

XML içeren bir veritabanında birçok satır var ve belirli bir düğüm özniteliğinin örneklerini saymak için bir Python komut dosyası yazmaya çalışıyorum.

Ağacım şöyle:

<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>

Python kullanarak özniteliklere "1"ve "2"XML'de nasıl erişebilirim ?


Yanıtlar:


780

Ben öneririm ElementTree. Örneğin aynı API diğer uyumlu uygulamalar vardır lxmlve cElementTreePython standart kitaplığı kendisi; ancak, bu bağlamda, esas olarak ekledikleri şey daha da hızlıdır - programlama kısmının kolaylığı, ElementTreetanımlayan API'ye bağlıdır .

Önce rootXML'den örneğin XML işleviyle veya bir dosyayı aşağıdaki gibi bir öğeyle ayrıştırarak bir Element örneği oluşturun :

import xml.etree.ElementTree as ET
root = ET.parse('thefile.xml').getroot()

Veya adresinde gösterilen diğer birçok yoldan herhangi biri ElementTree. Sonra şöyle bir şey yapın:

for type_tag in root.findall('bar/type'):
    value = type_tag.get('foobar')
    print(value)

Ve benzer, genellikle oldukça basit, kod kalıpları.


41
Python ile gelen xml.etree.cElementTree'yi görmezden geliyorsunuz ve bazı yönlerden daha hızlı tham lxml ("lxml's iterparse (), cET'deki e-postadan biraz daha yavaş") - lxml yazarından e-posta).
John Machin

7
ElementTree çalışır ve Python'a dahildir. Ancak sınırlı XPath desteği vardır ve bir öğenin üst öğesine geçemezsiniz, bu da geliştirmeyi yavaşlatabilir (özellikle bunu bilmiyorsanız). Ayrıntılar için bkz. Python xml query get parent .
Samuel

11
lxmlhızdan daha fazlasını ekler. Birkaç senaryoda çok faydalı olabilecek üst düğüm, XML kaynağındaki satır numarası gibi bilgilere kolay erişim sağlar.
Saheel Godhane

13
ElementTree'nin bazı güvenlik açığı sorunları var gibi görünüyor, bu dokümanlardan bir alıntı: Warning The xml.etree.ElementTree module is not secure against maliciously constructed data. If you need to parse untrusted or unauthenticated data see XML vulnerabilities.
Cristik

5
@Cristik Çoğu xml ayrıştırıcısında olduğu gibi, XML güvenlik açıkları sayfasına bakın .
gitaarik

427

minidom en hızlı ve oldukça basittir.

XML:

<data>
    <items>
        <item name="item1"></item>
        <item name="item2"></item>
        <item name="item3"></item>
        <item name="item4"></item>
    </items>
</data>

Python:

from xml.dom import minidom
xmldoc = minidom.parse('items.xml')
itemlist = xmldoc.getElementsByTagName('item')
print(len(itemlist))
print(itemlist[0].attributes['name'].value)
for s in itemlist:
    print(s.attributes['name'].value)

Çıktı:

4
item1
item1
item2
item3
item4

9
"İtem1" değerini nasıl elde edersiniz? Örneğin: <item name = "item1"> Değer1 </item>
swmcdonnell

88
Herkesin aynı soruyu sorması durumunda anladım. It's s.childNodes [0] .nodeValue
swmcdonnell

1
Örneğimi beğendim, uygulamak istiyorum ama minidom fonksiyonlarını nerede bulabilirim. Python minidom web sitesi bence berbat.
Drewdin

1
Ayrıca neden itemdoğrudan belgenin en üst seviyesinden bulur ? yolu ( data->items) sağlasaydınız daha temiz olmaz mıydı ? çünkü, aynı data->secondSetOfItemszamanda adlandırılmış düğümlere sahip itemolsaydınız ve iki gruptan yalnızca birini listelemek isteseydiniz item?
Ocak'ta


240

BeautifulSoup'u kullanabilirsiniz :

from bs4 import BeautifulSoup

x="""<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>"""

y=BeautifulSoup(x)
>>> y.foo.bar.type["foobar"]
u'1'

>>> y.foo.bar.findAll("type")
[<type foobar="1"></type>, <type foobar="2"></type>]

>>> y.foo.bar.findAll("type")[0]["foobar"]
u'1'
>>> y.foo.bar.findAll("type")[1]["foobar"]
u'2'

İnfo @ibz için teşekkürler, Evet, Aslında, Kaynak iyi biçimlendirilmemişse, ayrıştırıcılar için ayrıştırılması zor olacaktır.
SİZ

45
üç yıl sonra bs4 ile bu, özellikle kaynak iyi biçimlendirilmemişse çok esnek, harika bir çözümdür
cedbeu

8
@YOU DEPRECIATED BeautifulStoneSoup. Sadece kullanınBeautifulSoup(source_xml, features="xml")
andilabs

5
Başka bir 3 yıl sonra, sadece XML kullanarak yüklemeye çalıştım ElementTree, maalesef kaynakları yerlerde ayarlamadım, ancak BeautifulSoupherhangi bir değişiklik yapmadan hemen çalışmadıkça ayrıştırılamıyor !
ViKiG

8
@andi "Kullanımdan kaldırıldı" demek istediniz. "Amortismana tabi tutulmuş", genellikle normal kullanımdan kaynaklanan yaş veya aşınma ve yıpranma nedeniyle değerinde azalma anlamına gelir.
jpmc26

98

Orada birçok seçenek var. Hız ve bellek kullanımı önemliyse cElementTree mükemmel görünür. Kullanarak dosya okuma ile karşılaştırıldığında çok az yükü vardır readlines.

İlgili metrikler, cElementTree web sitesinden kopyalanan aşağıdaki tabloda bulunabilir :

library                         time    space
xml.dom.minidom (Python 2.1)    6.3 s   80000K
gnosis.objectify                2.0 s   22000k
xml.dom.minidom (Python 2.4)    1.4 s   53000k
ElementTree 1.2                 1.6 s   14500k  
ElementTree 1.2.4/1.3           1.1 s   14500k  
cDomlette (C extension)         0.540 s 20500k
PyRXPU (C extension)            0.175 s 10850k
libxml2 (C extension)           0.098 s 16000k
readlines (read as utf-8)       0.093 s 8850k
cElementTree (C extension)  --> 0.047 s 4900K <--
readlines (read as ascii)       0.032 s 5050k   

@Jfs tarafından işaret edildiği gibi , cElementTreePython ile birlikte gelir:

  • Python 2: from xml.etree import cElementTree as ElementTree.
  • Python 3: from xml.etree import ElementTree(hızlandırılmış C sürümü otomatik olarak kullanılır).

9
CElementTree kullanmanın bir dezavantajı var mı? Beyinsiz görünüyor.
mayhewsw

6
Görünüşe göre onlar nereden indirmek ve hiçbir bağlantı çalışır anlamaya çalışırken 15 dakika geçirdim gibi OS X kitaplığı kullanmak istemiyorum. Belgelerin eksikliği iyi projelerin gelişmesini önler, daha fazla insanın bunu fark etmesini ister.
Stunner

8
@Stunner: stdlib'de yani bir şey indirmenize gerek yok. Açık Python 2: from xml.etree import cElementTree as ElementTree. Python 3'te: from xml.etree import ElementTree(hızlandırılmış C sürümü otomatik olarak kullanılır)
jfs

1
@mayhewsw ElementTreeBelirli bir görev için nasıl verimli bir şekilde kullanılacağını bulmak daha fazla çaba gösterir . Belleğe uyan belgeler için kullanımı çok daha kolaydır minidomve daha küçük XML belgeleri için iyi çalışır.
Acumenus

44

Basitlik için xmltodict öneririm .

XML'inizi bir OrderedDict ile ayrıştırır;

>>> e = '<foo>
             <bar>
                 <type foobar="1"/>
                 <type foobar="2"/>
             </bar>
        </foo> '

>>> import xmltodict
>>> result = xmltodict.parse(e)
>>> result

OrderedDict([(u'foo', OrderedDict([(u'bar', OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])]))]))])

>>> result['foo']

OrderedDict([(u'bar', OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])]))])

>>> result['foo']['bar']

OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])])

3
Kabul. XPath'a veya karmaşık bir şeye ihtiyacınız yoksa, bu kullanımı çok daha kolaydır (özellikle yorumlayıcıda); JSON yerine XML yayınlayan REST API'leri için kullanışlıdır
Dan Passaro

4
OrderedDict'in yinelenen anahtarları desteklemediğini unutmayın. Çoğu XML aynı türden birden çok kardeşle doludur (örneğin, bir bölümdeki tüm paragraflar veya çubuğunuzdaki tüm türler). Bu sadece çok sınırlı özel durumlar için işe yarayacaktır.
TextGeek

2
@TextGeek Bu durumda, result["foo"]["bar"]["type"]tüm <type>öğelerin bir listesidir , bu yüzden hala çalışıyor (yapı belki biraz beklenmedik olsa da).
luator

38

lxml.objectify gerçekten basittir.

Örnek metninizi alma:

from lxml import objectify
from collections import defaultdict

count = defaultdict(int)

root = objectify.fromstring(text)

for item in root.bar.type:
    count[item.attrib.get("foobar")] += 1

print dict(count)

Çıktı:

{'1': 1, '2': 1}

counther öğenin sayısını varsayılan anahtarlarla bir sözlükte saklar, böylece üyeliği kontrol etmeniz gerekmez. Ayrıca bakmayı da deneyebilirsiniz collections.Counter.
Ryan Ginstrom

20

Python, göçmen XML ayrıştırıcısına bir arayüze sahiptir.

xml.parsers.expat

Doğrulayıcı olmayan bir ayrıştırıcıdır, bu nedenle kötü XML yakalanmaz. Ancak dosyanızın doğru olduğunu biliyorsanız, bu oldukça iyidir ve muhtemelen istediğiniz tam bilgiyi alırsınız ve gerisini anında atabilirsiniz.

stringofxml = """<foo>
    <bar>
        <type arg="value" />
        <type arg="value" />
        <type arg="value" />
    </bar>
    <bar>
        <type arg="value" />
    </bar>
</foo>"""
count = 0
def start(name, attr):
    global count
    if name == 'type':
        count += 1

p = expat.ParserCreate()
p.StartElementHandler = start
p.Parse(stringofxml)

print count # prints 4

+1 çünkü garip kaynak karakterlerle çalışacak bir doğrulama olmayan ayrıştırıcı arıyorum. Umarım bu bana istediğim sonuçları verecektir.
Nathan C.Tresch

1
Örnek '09'da yapıldı ve bu böyle yapıldı.
Tor Valamo

14

Declxml önerebilirim .

Tam açıklama: Bu kütüphaneyi yazdım, çünkü XML ve Python veri yapıları arasında düzinelerce satır ayrıştırma / serileştirme kodu ElementTree ile yazmaya gerek kalmadan bir yol arıyordum.

Declxml ile, XML belgenizin yapısını ve XML ile Python veri yapıları arasında nasıl eşleme yapılacağını tanımlamak için işlemciler kullanırsınız . İşlemciler hem serileştirme hem de ayrıştırma için olduğu gibi temel bir doğrulama düzeyi için de kullanılır.

Python veri yapılarına ayrılmak kolaydır:

import declxml as xml

xml_string = """
<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>
"""

processor = xml.dictionary('foo', [
    xml.dictionary('bar', [
        xml.array(xml.integer('type', attribute='foobar'))
    ])
])

xml.parse_from_string(processor, xml_string)

Hangi çıktıyı üretir:

{'bar': {'foobar': [1, 2]}}

Verileri XML'ye serileştirmek için aynı işlemciyi de kullanabilirsiniz

data = {'bar': {
    'foobar': [7, 3, 21, 16, 11]
}}

xml.serialize_to_string(processor, data, indent='    ')

Hangi aşağıdaki çıktıyı üretir

<?xml version="1.0" ?>
<foo>
    <bar>
        <type foobar="7"/>
        <type foobar="3"/>
        <type foobar="21"/>
        <type foobar="16"/>
        <type foobar="11"/>
    </bar>
</foo>

Sözlükler yerine nesnelerle çalışmak istiyorsanız, nesneleri nesnelere ve nesnelere dönüştürmek için işlemciler tanımlayabilirsiniz.

import declxml as xml

class Bar:

    def __init__(self):
        self.foobars = []

    def __repr__(self):
        return 'Bar(foobars={})'.format(self.foobars)


xml_string = """
<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>
"""

processor = xml.dictionary('foo', [
    xml.user_object('bar', Bar, [
        xml.array(xml.integer('type', attribute='foobar'), alias='foobars')
    ])
])

xml.parse_from_string(processor, xml_string)

Hangi aşağıdaki çıktıyı üretir

{'bar': Bar(foobars=[1, 2])}

13

Başka bir olasılık eklemek için, basit bir xml--python-nesne kütüphanesi olduğu için untangle'ı kullanabilirsiniz . Burada bir örnek var:

Kurulum:

pip install untangle

Kullanımı:

XML dosyanız (biraz değişti):

<foo>
   <bar name="bar_name">
      <type foobar="1"/>
   </bar>
</foo>

Özelliklere şununla erişiliyor untangle:

import untangle

obj = untangle.parse('/path_to_xml_file/file.xml')

print obj.foo.bar['name']
print obj.foo.bar.type['foobar']

Çıktı şöyle olacaktır:

bar_name
1

Untangle hakkında daha fazla bilgi " untangle " da bulunabilir .

Ayrıca, merak ediyorsanız, " Python ve XML " içinde XML ve Python ile çalışmak için bir araç listesi bulabilirsiniz . Ayrıca, en yaygın olanların önceki yanıtlarla belirtildiğini göreceksiniz.


Untangle'ı minidomdan farklı kılan nedir?
Aaron Mann

Minidom ile çalışmadığım için size bu ikisi arasındaki farkı söyleyemem.
jchanger

10

Burada çok basit ama etkili bir kod kullanarak cElementTree.

try:
    import cElementTree as ET
except ImportError:
  try:
    # Python 2.5 need to import a different module
    import xml.etree.cElementTree as ET
  except ImportError:
    exit_err("Failed to import cElementTree from any known place")      

def find_in_tree(tree, node):
    found = tree.find(node)
    if found == None:
        print "No %s in file" % node
        found = []
    return found  

# Parse a xml file (specify the path)
def_file = "xml_file_name.xml"
try:
    dom = ET.parse(open(def_file, "r"))
    root = dom.getroot()
except:
    exit_err("Unable to open and parse input definition file: " + def_file)

# Parse to find the child nodes list of node 'myNode'
fwdefs = find_in_tree(root,"myNode")

Bu " python xml ayrıştırma " dan.


7

XML:

<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>

Python kodu:

import xml.etree.cElementTree as ET

tree = ET.parse("foo.xml")
root = tree.getroot() 
root_tag = root.tag
print(root_tag) 

for form in root.findall("./bar/type"):
    x=(form.attrib)
    z=list(x)
    for i in z:
        print(x[i])

Çıktı:

foo
1
2

6
import xml.etree.ElementTree as ET
data = '''<foo>
           <bar>
               <type foobar="1"/>
               <type foobar="2"/>
          </bar>
       </foo>'''
tree = ET.fromstring(data)
lst = tree.findall('bar/type')
for item in lst:
    print item.get('foobar')

Bu, foobarözniteliğin değerini yazdıracaktır .


6

xml.etree.ElementTree ve lxml karşılaştırması

Bunlar, aralarında seçim yapmadan önce bilmem gereken en çok kullanılan iki kütüphanenin bazı artıları.

xml.etree.ElementTree:

  1. Gönderen standart kütüphanesinde : hayır herhangi bir modül yükleme ihtiyacı

lxml

  1. Kolayca XML bildirimi yazın : örneğin eklemeniz standalone="no"mi gerekiyor ?
  2. Güzel baskı : Ek kod olmadan güzel bir girintili XML olabilir .
  3. Objectify işlevselliği: XML'i normal bir Python nesne hiyerarşisiyle uğraşıyormuş gibi kullanmanızı sağlar .node.
  4. sourceline , kullandığınız XML öğesinin satırını kolayca almanızı sağlar.
  5. yerleşik bir XSD şema denetleyicisini de kullanabilirsiniz.

5

Python xml.dom ve xml.dom.minidom'u oldukça kolay buluyorum . DOM'nun büyük miktarlarda XML için iyi olmadığını unutmayın, ancak girdiniz oldukça küçükse, bu iyi çalışır.


2

Orada bir lib belirli API kullanmaya gerek kullanırsanız python-benedict. XML'inizden yeni bir örnek başlatın ve bir dictalt sınıf olduğu için kolayca yönetin .

Kurulum kolaydır: pip install python-benedict

from benedict import benedict as bdict

# data-source can be an url, a filepath or data-string (as in this example)
data_source = """
<foo>
   <bar>
      <type foobar="1"/>
      <type foobar="2"/>
   </bar>
</foo>"""

data = bdict.from_xml(data_source)
t_list = data['foo.bar'] # yes, keypath supported
for t in t_list:
   print(t['@foobar'])

Bu destekler ve normalleştirir I / O işlemleri birçok biçimleri ile: Base64, CSV, JSON, TOML, XML, YAMLve query-string.

GitHub'da iyi test edilmiş ve açık kaynaklıdır .


0
#If the xml is in the form of a string as shown below then
from lxml  import etree, objectify
'''sample xml as a string with a name space {http://xmlns.abc.com}'''
message =b'<?xml version="1.0" encoding="UTF-8"?>\r\n<pa:Process xmlns:pa="http://xmlns.abc.com">\r\n\t<pa:firsttag>SAMPLE</pa:firsttag></pa:Process>\r\n'  # this is a sample xml which is a string


print('************message coversion and parsing starts*************')

message=message.decode('utf-8') 
message=message.replace('<?xml version="1.0" encoding="UTF-8"?>\r\n','') #replace is used to remove unwanted strings from the 'message'
message=message.replace('pa:Process>\r\n','pa:Process>')
print (message)

print ('******Parsing starts*************')
parser = etree.XMLParser(remove_blank_text=True) #the name space is removed here
root = etree.fromstring(message, parser) #parsing of xml happens here
print ('******Parsing completed************')


dict={}
for child in root: # parsed xml is iterated using a for loop and values are stored in a dictionary
    print(child.tag,child.text)
    print('****Derving from xml tree*****')
    if child.tag =="{http://xmlns.abc.com}firsttag":
        dict["FIRST_TAG"]=child.text
        print(dict)


### output
'''************message coversion and parsing starts*************
<pa:Process xmlns:pa="http://xmlns.abc.com">

    <pa:firsttag>SAMPLE</pa:firsttag></pa:Process>
******Parsing starts*************
******Parsing completed************
{http://xmlns.abc.com}firsttag SAMPLE
****Derving from xml tree*****
{'FIRST_TAG': 'SAMPLE'}'''

Lütfen cevabınızın sorunu nasıl çözdüğünü açıklayan bir bağlam da ekleyin. Yalnızca kod yanıtları teşvik edilmez.
Pedram Parsian

-1

Kaynak bir xml dosyasıysa, bu örnek gibi söyleyin

<pa:Process xmlns:pa="http://sssss">
        <pa:firsttag>SAMPLE</pa:firsttag>
    </pa:Process>

aşağıdaki kodu deneyebilirsiniz

from lxml import etree, objectify
metadata = 'C:\\Users\\PROCS.xml' # this is sample xml file the contents are shown above
parser = etree.XMLParser(remove_blank_text=True) # this line removes the  name space from the xml in this sample the name space is --> http://sssss
tree = etree.parse(metadata, parser) # this line parses the xml file which is PROCS.xml
root = tree.getroot() # we get the root of xml which is process and iterate using a for loop
for elem in root.getiterator():
    if not hasattr(elem.tag, 'find'): continue  # (1)
    i = elem.tag.find('}')
    if i >= 0:
        elem.tag = elem.tag[i+1:]

dict={}  # a python dictionary is declared
for elem in tree.iter(): #iterating through the xml tree using a for loop
    if elem.tag =="firsttag": # if the tag name matches the name that is equated then the text in the tag is stored into the dictionary
        dict["FIRST_TAG"]=str(elem.text)
        print(dict)

Çıktı

{'FIRST_TAG': 'SAMPLE'}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.