Python'ın standart kütüphanesi sağlam bir XML ayrıştırıcısıyla birlikte gelir — pip install gerekmez.
xml.etree.ElementTree, gerçek dünyadaki XML'in büyük çoğunluğunu işler:
RSS beslemeleri,
SOAP yanıtları, yapılandırma dosyaları, Android kaynak dosyaları, Maven POM'ları.
lxml'e yalnızca
XSD
şema doğrulaması, karmaşık XPath veya gerçekten büyük dosyalarla karşılaştığınızda ihtiyaç duyarsınız.
Her ikisini de gerçek örneklerle inceleyelim.
ElementTree Temelleri — String veya Dosyadan Ayrıştırma
xml.etree.ElementTree
modülü size iki giriş noktası sunar: XML dizelerini ayrıştırmak için fromstring() ve
doğrudan dosyadan okumak için parse(). İşte RSS besleme yapısını kullanan pratik bir örnek:
import xml.etree.ElementTree as ET
rss_xml = """<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
<channel>
<title>Engineering Blog</title>
<link>https://blog.example.com</link>
<description>Articles for developers</description>
<item>
<title>Understanding Database Indexes</title>
<link>https://blog.example.com/db-indexes</link>
<pubDate>Mon, 15 Jan 2024 09:00:00 GMT</pubDate>
<category>Database</category>
</item>
<item>
<title>REST API Design Patterns</title>
<link>https://blog.example.com/rest-patterns</link>
<pubDate>Wed, 17 Jan 2024 09:00:00 GMT</pubDate>
<category>API</category>
</item>
</channel>
</rss>"""
# Dizeden ayrıştır
root = ET.fromstring(rss_xml)
# Dosyadan ayrıştır (alternatif)
# tree = ET.parse('feed.xml')
# root = tree.getroot()
print(root.tag) # rss
print(root.attrib) # {'version': '2.0'}
channel = root.find('channel')
print(channel.find('title').text) # Engineering Blogfind, findall ve findtext — Ağaçta Arama
Bu üç yöntem, veri çıkarmak için birincil araçlarınızdır. Hepsi element ağacında gezinmek için basit bir yol ifadesi (sınırlı bir XPath gibi) kabul eder:
import xml.etree.ElementTree as ET
root = ET.fromstring(rss_xml)
channel = root.find('channel')
# find() — ilk eşleşen elementi veya None döndürür
first_item = channel.find('item')
print(first_item.find('title').text) # Understanding Database Indexes
# findall() — tüm eşleşen elementlerin listesini döndürür
items = channel.findall('item')
print(len(items)) # 2
for item in items:
title = item.findtext('title') # findtext() doğrudan .text döndürür
link = item.findtext('link')
pub_date = item.findtext('pubDate')
print(f"{title} — {pub_date}")
# '/' ile iç içe geçmiş yol
all_titles = channel.findall('item/title')
print([el.text for el in all_titles])
# ['Understanding Database Indexes', 'REST API Design Patterns']
# Varsayılan değerli findtext() (eksik elementlerde AttributeError'dan kaçınır)
author = channel.findtext('item/author', default='Unknown Author')
print(author) # Unknown Authorfindtext() kullanın. find().text kullanırsanız
ve element yoksa, find() None döndürür ve .text
bir AttributeError fırlatır. findtext('tag', default=''), eksik
elementleri zarif şekilde işler — harici kaynaklardan XML ayrıştırırken çok daha güvenlidir.Nitelikleri Okuma
import xml.etree.ElementTree as ET
xml_str = """<catalog>
<product id="P001" featured="true">
<name>Mechanical Keyboard</name>
<price currency="USD">189.00</price>
</product>
<product id="P002" featured="false">
<name>USB-C Hub</name>
<price currency="USD">49.99</price>
</product>
</catalog>"""
root = ET.fromstring(xml_str)
for product in root.findall('product'):
product_id = product.get('id') # nitelikler için get()
featured = product.get('featured', 'false') # varsayılan değerle
name = product.findtext('name')
price_el = product.find('price')
price = float(price_el.text)
currency = price_el.get('currency')
print(f"{product_id}: {name} — {currency} {price} (featured: {featured})")XML Ad Alanlarını Yönetme
Ad alanları,
çoğu geliştiricinin inlediği XML ayrıştırma kısmıdır. ElementTree'de
ad alanı URI'leri etiket adlarında küme parantezleri içinde görünür: {http://...}tagname. Bunu temiz
şekilde ele almanın yolu:
import xml.etree.ElementTree as ET
soap_xml = """<?xml version="1.0"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:inv="http://www.example.com/invoice">
<soap:Body>
<inv:GetInvoiceResponse>
<inv:InvoiceId>INV-2024-0042</inv:InvoiceId>
<inv:Amount currency="EUR">1250.00</inv:Amount>
<inv:Status>Paid</inv:Status>
</inv:GetInvoiceResponse>
</soap:Body>
</soap:Envelope>"""
root = ET.fromstring(soap_xml)
# ElementTree, ad alanı öneklerini küme parantezleri içindeki URI'lere genişletir
# Daha temiz XPath tarzı aramalar için bir ad alanı haritası tanımlayabilirsiniz
ns = {
'soap': 'http://schemas.xmlsoap.org/soap/envelope/',
'inv': 'http://www.example.com/invoice'
}
# find/findall içinde ad alanı haritasını kullanın
invoice_id = root.find('.//inv:InvoiceId', ns).text
amount_el = root.find('.//inv:Amount', ns)
status = root.findtext('.//inv:Status', namespaces=ns)
print(invoice_id) # INV-2024-0042
print(amount_el.text) # 1250.00
print(amount_el.get('currency')) # EUR
print(status) # PaidProgramatik Olarak XML Oluşturma
ElementTree aynı zamanda sıfırdan XML oluşturmanıza da olanak tanır — SOAP istekleri oluşturmanız veya XML çıktısı üretmeniz gerektiğinde kullanışlıdır:
import xml.etree.ElementTree as ET
# Sipariş belgesi oluştur
order = ET.Element('order', id='ORD-9981', status='pending')
customer = ET.SubElement(order, 'customer')
ET.SubElement(customer, 'name').text = 'Jane Smith'
ET.SubElement(customer, 'email').text = '[email protected]'
items = ET.SubElement(order, 'items')
for product_id, name, qty, price in [
('P001', 'Mechanical Keyboard', 1, 189.00),
('P002', 'USB-C Hub', 2, 49.99),
]:
item = ET.SubElement(items, 'item', productId=product_id, qty=str(qty))
ET.SubElement(item, 'name').text = name
ET.SubElement(item, 'price', currency='USD').text = str(price)
# Dizgeye serileştir
ET.indent(order, space=' ') # Python 3.9+ — yerinde güzel biçimlendirme
xml_output = ET.tostring(order, encoding='unicode', xml_declaration=True)
print(xml_output)iterparse — Büyük XML Dosyalarını Akışla İşleme
Büyük XML dosyaları için (on MB veya daha fazla), parse() ile tüm belgeyi belleğe yüklemek
pahalıdır.
iterparse()
dosyayı akış olarak işler (olay odaklı SAX yaklaşımına benzer şekilde) ve elementler karşılaşıldıkça
olaylar tetikler; bu sayede elementleri işleyip atarak ilerlemenizi sağlar:
import xml.etree.ElementTree as ET
def process_large_feed(filepath):
"""Büyük bir RSS/Atom beslemesini tamamını belleğe yüklemeden işler."""
articles = []
for event, elem in ET.iterparse(filepath, events=('end',)):
if elem.tag == 'item':
articles.append({
'title': elem.findtext('title', ''),
'link': elem.findtext('link', ''),
'pub_date': elem.findtext('pubDate', ''),
})
# Kritik: belleği serbest bırakmak için işlemden sonra elementi temizle
elem.clear()
if len(articles) >= 1000:
yield from articles
articles.clear()
yield from articles # kalan öğeleri döndür
for article in process_large_feed('large_feed.xml'):
print(article['title'])Her elementi işledikten sonra yapılan elem.clear() çağrısı, dosya boyutundan bağımsız
olarak bellek kullanımını sabit tutmanın anahtarıdır. Bunu yapmazsanız, ElementTree tüm ayrıştırılmış elementleri
bellekte biriktirir ve akış avantajını kaybedersiniz.
lxml — Daha Fazla Güce İhtiyaç Duyduğunuzda
lxml
kütüphanesi, tam
XPath 1.0
desteği, XSD şema doğrulaması ve XSLT dönüşümleri ile ElementTree'nin API'sini genişleten
hızlı C tabanlı bir XML kütüphanesidir. pip install lxml ile yükleyin:
from lxml import etree
# lxml çoğu durumda ElementTree ile aynı API'yi kullanır
root = etree.fromstring(rss_xml.encode()) # lxml str değil bytes ister
# Tam XPath 1.0 — ElementTree'nin alt kümesinden çok daha güçlü
items = root.xpath('//item[position() <= 2]/title/text()')
print(items) # ['Understanding Database Indexes', 'REST API Design Patterns']
# Koşullu XPath — belirli kategorideki öğeleri al
db_items = root.xpath('//item[category="Database"]/title/text()')
print(db_items) # ['Understanding Database Indexes']
# XSD şema doğrulaması
xsd_doc = etree.parse('schema.xsd')
schema = etree.XMLSchema(xsd_doc)
xml_doc = etree.parse('data.xml')
if schema.validate(xml_doc):
print("Valid!")
else:
for error in schema.error_log:
print(f"Line {error.line}: {error.message}")XML Çalışması için Yararlı Araçlar
Python XML entegrasyonları oluştururken bu tarayıcı araçları veri tarafında yardımcı olur: XML Biçimlendirici ham API yanıtlarını okunabilir hale getirmek için, XML Doğrulayıcı ayrıştırıcınızı kurmadan önce iyi biçimliliği kontrol etmek için, ve XML'den JSON'a elementler yerine dict'lerle çalışmayı tercih ettiğinizde.
Özet
Python'ın xml.etree.ElementTree'si gerçek dünya XML senaryolarının çoğunu kapsar:
SOAP ve ad alanlı beslemeler için ad alanı haritasıyla find() ve findall(),
eksik elementlerde AttributeError'dan kaçınmak için varsayılan değerli findtext(),
ve tek seferde belleğe yükleyemediğiniz büyük dosyalar için iterparse() kullanın.
XSD doğrulaması veya tam XPath ifade dili ihtiyacınız olduğunda lxml'e başvurun.
Standart kütüphane geri kalanını gayet iyi halleder.