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:

python
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 Blog

find, 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:

python
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 Author
Varsayılan değerle findtext() 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

python
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:

python
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)                        # Paid

Programatik 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:

python
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:

python
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:

python
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.