XML ו-xpath בפייתון מוסבר - איך קוראים מסמכי XML בקוד¶
בואו נדבר בכנות. היום כשמדברים על פורמט נתונים, כולם קופצים ישר ל-JSON, וזה מוצדק ברוב המקרים החדשים. אבל XML לא נעלם - הוא עדיין נמצא בקבצי קונפיגורציה, בממשקי API ותיקים בגופים גדולים, בקבצי Office שהם בעצם XML בתחפושת, ובעוד מקומות ישנים שממשיכים לעבוד. אז שווה לדעת איך לקרוא אותו, גם אם לא תבחרו בו לפרויקט חדש.
מה זה בכלל XML¶
XML, ראשי תיבות של Extensible Markup Language, הוא פורמט טקסט שמייצג נתונים בעזרת תגיות מקוננות, בדומה מאוד ל-HTML מבחינת המבנה:
<library>
<book id="1">
<title>פייתון למתחילים</title>
<author>ישראל ישראלי</author>
</book>
<book id="2">
<title>אלגוריתמים בסיסיים</title>
<author>דנה כהן</author>
</book>
</library>
לכל תגית יש שם, יכולה להיות לה תוכן פנימי, תגיות בת מקוננות בתוכה, ומאפיינים - attributes - כמו id בדוגמה למעלה.
קריאת XML עם ElementTree¶
פייתון מגיע עם מודול מובנה לעבודה עם XML, בלי צורך להתקין שום דבר - xml.etree.ElementTree:
import xml.etree.ElementTree as ET
tree = ET.parse("library.xml")
root = tree.getroot()
print(root.tag) # library
root מייצג את התגית הראשית של המסמך, ומכאן אפשר לנוע פנימה לתוך העץ. אם התוכן מגיע כמחרוזת ולא מקובץ, למשל תגובה מ-API, אפשר להשתמש ב-ET.fromstring במקום ET.parse.
ניווט במסמך עם find ו-findall¶
כדי להגיע לתגיות ספציפיות בתוך המסמך, יש שתי מתודות מרכזיות. find מחזירה את התגית הראשונה שתואמת, ו-findall מחזירה רשימה של כל התגיות התואמות:
first_book = root.find("book")
print(first_book.find("title").text) # פייתון למתחילים
all_books = root.findall("book")
for book in all_books:
title = book.find("title").text
author = book.find("author").text
print(f"{title} - מאת {author}")
שני הדפוסים האלה מקבלים מחרוזת נתיב שנקראת ביטוי XPath - למשל "book" מחפש תגיות בת ישירות בשם הזה, ואפשר גם נתיבים עמוקים יותר כמו "book/title" כדי לרדת כמה רמות בבת אחת. זו לא תחביר מסובך - זה בעצם דרך לתאר "לך לכאן בעץ" בעזרת מחרוזת קצרה.
קריאת מאפיינים - attributes¶
בנוסף לתוכן טקסטואלי, לתגיות יש לפעמים מאפיינים, כמו ה-id בדוגמה שלנו. הם נגישים דרך attrib:
for book in root.findall("book"):
book_id = book.attrib["id"]
title = book.find("title").text
print(f"ספר {book_id}: {title}")
חיפוש עם תנאי¶
XPath תומך גם בחיפוש מותנה בעזרת סוגריים מרובעים - למשל למצוא ספר לפי id ספציפי:
זה שימושי כשלא רוצים לעבור בלולאה על כל האיברים כדי למצוא אחד ספציפי, אלא לתאר בדיוק מה מחפשים בביטוי אחד.
בניית XML חדש¶
לפעמים רוצים לא רק לקרוא XML אלא גם ליצור אותו, למשל כדי לשמור תוצאה בפורמט שמערכת ישנה יודעת לקרוא. גם לזה יש כלים באותו מודול:
new_root = ET.Element("library")
new_book = ET.SubElement(new_root, "book", id="3")
title = ET.SubElement(new_book, "title")
title.text = "מבוא לרשתות"
tree = ET.ElementTree(new_root)
tree.write("new_library.xml", encoding="utf-8")
ET.Element יוצר תגית שורש חדשה, ו-ET.SubElement מוסיף תגית בת בתוכה. זה אותו עץ שראינו בקריאה, רק בכיוון ההפוך - בונים אותו במקום לנווט בו.
XML מול JSON¶
אז למה בכלל JSON תפס את מקומו של XML ברוב הפיתוח החדש? JSON קצר יותר, קריא יותר לבני אדם, ומתאים באופן טבעי למבני נתונים של פייתון - מילונים ורשימות, בלי צורך במודול נפרד כמו ElementTree. XML מפורט יותר, עם תגיות פתיחה וסגירה לכל ערך, ותומך בדברים של-JSON אין בהם ברירת מחדל, כמו מאפיינים בתוך תגית או סכימות פורמליות לאימות מבנה המסמך.
בפועל, לפרויקט חדש שאתם מתכננים מאפס, JSON כמעט תמיד יהיה הבחירה הנוחה יותר. אבל כשאתם עובדים מול מערכת קיימת, קובץ קונפיגורציה ישן, או API ותיק שמחזיר XML, אתם לא תמיד בוחרים - אתם פשוט צריכים לדעת לקרוא את מה שמגיע אליכם. אם אתם עדיין בונים את הבסיס של פייתון, כולל עבודה עם מחרוזות ומבני נתונים שעליהם נשען כל הפרסור הזה, יש לכם את זה בקורס התכנות הבסיסי שלנו.
לסיכום¶
XML לא נעלם, הוא רק הפסיק להיות ברירת המחדל לפרויקטים חדשים. עם xml.etree.ElementTree ושליטה בסיסית ב-find ו-findall, אתם יכולים לקרוא כמעט כל מסמך XML שתיתקלו בו, בלי להתקין ספריות נוספות.