Phần tử xml python

Đây là bài hướng dẫn xử lý XML với lxml. cây etree. Nó tổng quan ngắn gọn về các khái niệm chính của , và một số cải tiến đơn giản giúp cuộc sống của bạn với tư cách là một lập trình viên dễ dàng hơn

Nếu mã của bạn chỉ sử dụng API ElementTree và không dựa vào bất kỳ chức năng nào dành riêng cho lxml. etree, bạn cũng có thể sử dụng (bất kỳ phần nào của) chuỗi nhập sau đây làm dự phòng cho ElementTree ban đầu

Để hỗ trợ viết mã di động, hướng dẫn này làm rõ trong các ví dụ phần nào của API được trình bày là phần mở rộng của lxml. etree trên bản gốc, như được định nghĩa bởi thư viện ElementTree của Fredrik Lundh

Phần tử là đối tượng vùng chứa chính cho API ElementTree. Hầu hết các chức năng của cây XML được truy cập thông qua lớp này. Các phần tử được tạo dễ dàng thông qua Element factory

>>> root = etree.Element("root")

Tên thẻ XML của các phần tử được truy cập thông qua thuộc tính thẻ

Các phần tử được tổ chức theo cấu trúc cây XML. Để tạo các phần tử con và thêm chúng vào phần tử cha, bạn có thể sử dụng phương thức append()

>>> root.append( etree.Element("child1") )

Tuy nhiên, điều này phổ biến đến mức có một cách ngắn hơn và hiệu quả hơn nhiều để thực hiện việc này. nhà máy SubElement. Nó chấp nhận các đối số giống như Element factory, nhưng cũng yêu cầu cha mẹ làm đối số đầu tiên

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")

Để thấy rằng đây thực sự là XML, bạn có thể tuần tự hóa cây mà bạn đã tạo

>>> print(etree.tostring(root, pretty_print=True))

  
  
  

Các phần tử là danh sách

Để làm cho việc truy cập vào các phần tử con này trở nên dễ dàng và đơn giản, các phần tử bắt chước hành vi của các danh sách Python thông thường càng giống càng tốt

>>> child = root[0]
>>> print(child.tag)
child1

>>> print(len(root))
3

>>> root.index(root[1]) # lxml.etree only!
1

>>> children = list(root)

>>> for child in root:
..     print(child.tag)
child1
child2
child3

>>> root.insert(0, etree.Element("child0"))
>>> start = root[:1]
>>> end   = root[-1:]

>>> print(start[0].tag)
child0
>>> print(end[0].tag)
child3

Trước ElementTree 1. 3 và lxml 2. 0, bạn cũng có thể kiểm tra giá trị thực của một Phần tử để xem nó có phần tử con hay không, tôi. e. nếu danh sách con trống

if root:   # this no longer works!
    print("The root element has children")

Điều này không còn được hỗ trợ vì mọi người có xu hướng mong đợi rằng một "thứ gì đó" được đánh giá là Đúng và mong muốn các Phần tử là "thứ gì đó", họ có thể có con hay không. Vì vậy, nhiều người dùng cảm thấy ngạc nhiên khi bất kỳ Phần tử nào cũng đánh giá là Sai trong câu lệnh if như trên. Thay vào đó, hãy sử dụng len(element), vừa rõ ràng hơn vừa ít bị lỗi hơn

>>> print(etree.iselement(root))  # test if it's some kind of Element
True
>>> if len(root):                 # test if it has children
..     print("The root element has children")
The root element has children

Có một trường hợp quan trọng khác khi hành vi của các Phần tử trong lxml (trong 2. 0 trở lên) sai lệch so với danh sách và so với ElementTree gốc (trước phiên bản 1. 3 hoặc Trăn 2. 3/7. 2)

>>> for child in root:
..     print(child.tag)
child0
child1
child2
child3
>>> root[0] = root[-1]  # this moves the element in lxml.etree!
>>> for child in root:
..     print(child.tag)
child3
child1
child2

Trong ví dụ này, phần tử cuối cùng được di chuyển đến một vị trí khác, thay vì được sao chép, tôi. e. nó sẽ tự động bị xóa khỏi vị trí trước đó khi nó được đặt ở một nơi khác. Trong danh sách, các đối tượng có thể xuất hiện ở nhiều vị trí cùng một lúc và phép gán trên sẽ chỉ sao chép tham chiếu mục vào vị trí đầu tiên để cả hai đều chứa chính xác cùng một mục

________số 8_______

Lưu ý rằng trong ElementTree ban đầu, một đối tượng Element đơn lẻ có thể nằm ở bất kỳ vị trí nào trong bất kỳ số lượng cây nào, điều này cho phép thao tác sao chép giống như với danh sách. Hạn chế rõ ràng là các sửa đổi đối với Phần tử như vậy sẽ áp dụng cho tất cả các vị trí mà nó xuất hiện trong cây, điều này có thể được dự định hoặc không.

Mặt trái của sự khác biệt này là một Phần tử trong lxml. etree luôn có chính xác một cha mẹ, có thể được truy vấn thông qua phương thức getparent(). Điều này không được hỗ trợ trong ElementTree gốc

>>> root is root[0].getparent()  # lxml.etree only!
True

Nếu bạn muốn sao chép một phần tử sang một vị trí khác trong lxml. etree, hãy xem xét việc tạo một bản sao sâu độc lập bằng cách sử dụng mô-đun sao chép từ thư viện chuẩn của Python

>>> root.append( etree.Element("child1") )
0

Anh chị em (hoặc hàng xóm) của một phần tử được truy cập dưới dạng các phần tử tiếp theo và trước đó

>>> root.append( etree.Element("child1") )
1

Các phần tử mang các thuộc tính như một lệnh

Các phần tử XML hỗ trợ các thuộc tính. Bạn có thể tạo chúng trực tiếp trong Element factory

>>> root.append( etree.Element("child1") )
2

Các thuộc tính chỉ là các cặp tên-giá trị không có thứ tự, do đó, một cách rất thuận tiện để xử lý chúng là thông qua giao diện giống như từ điển của Elements

>>> root.append( etree.Element("child1") )
3

Đối với các trường hợp bạn muốn thực hiện tra cứu mục hoặc có lý do khác để nhận đối tượng giống như từ điển 'thực', e. g. để chuyển nó đi khắp nơi, bạn có thể sử dụng thuộc tính attrib

>>> root.append( etree.Element("child1") )
4

Lưu ý rằng attrib là một đối tượng giống như dict được hỗ trợ bởi chính Element. Điều này có nghĩa là mọi thay đổi đối với Phần tử đều được phản ánh trong thuộc tính và ngược lại. Điều đó cũng có nghĩa là cây XML vẫn tồn tại trong bộ nhớ miễn là thuộc tính của một trong các Phần tử của nó được sử dụng. Để có ảnh chụp nhanh độc lập của các thuộc tính không phụ thuộc vào cây XML, hãy sao chép nó vào một dict

>>> root.append( etree.Element("child1") )
5

Các phần tử chứa văn bản

Các phần tử có thể chứa văn bản

>>> root.append( etree.Element("child1") )
6

Trong nhiều tài liệu XML (tài liệu tập trung vào dữ liệu), đây là nơi duy nhất có thể tìm thấy văn bản. Nó được đóng gói bởi một thẻ lá ở dưới cùng của hệ thống phân cấp cây

Tuy nhiên, nếu XML được sử dụng cho các tài liệu văn bản được gắn thẻ chẳng hạn như (X)HTML, thì văn bản cũng có thể xuất hiện giữa các phần tử khác nhau, ngay ở giữa cây

>>> root.append( etree.Element("child1") )
7

Ở đây,
thẻ được bao quanh bởi văn bản. Điều này thường được gọi là XML kiểu tài liệu hoặc nội dung hỗn hợp. Các phần tử hỗ trợ điều này thông qua thuộc tính đuôi của chúng. Nó chứa văn bản trực tiếp theo sau phần tử, cho đến phần tử tiếp theo trong cây XML

>>> root.append( etree.Element("child1") )
8

Hai thuộc tính. văn bản và. đuôi đủ để thể hiện bất kỳ nội dung văn bản nào trong tài liệu XML. Bằng cách này, API ElementTree không yêu cầu bất kỳ bổ sung nào ngoài lớp Element, lớp này có xu hướng cản trở khá thường xuyên (như bạn có thể biết từ các API DOM cổ điển)

Tuy nhiên, có những trường hợp văn bản đuôi cũng gây cản trở. Ví dụ: khi bạn tuần tự hóa một Phần tử từ bên trong cây, không phải lúc nào bạn cũng muốn văn bản đuôi của nó trong kết quả (mặc dù bạn vẫn muốn văn bản đuôi của các phần tử con của nó). Với mục đích này, hàm tostring() chấp nhận đối số từ khóa with_tail

>>> root.append( etree.Element("child1") )
9

Nếu bạn chỉ muốn đọc văn bản, tôi. e. không có bất kỳ thẻ trung gian nào, bạn phải nối đệ quy tất cả các thuộc tính văn bản và đuôi theo đúng thứ tự. Một lần nữa, hàm tostring() xuất hiện, lần này sử dụng từ khóa method

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
0

Sử dụng XPath để tìm văn bản

Một cách khác để trích xuất nội dung văn bản của cây là , cách này cũng cho phép bạn trích xuất các đoạn văn bản riêng biệt vào một danh sách

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
1

Nếu bạn muốn sử dụng cái này thường xuyên hơn, bạn có thể bọc nó trong một hàm

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
2

Lưu ý rằng một kết quả chuỗi được XPath trả về là một đối tượng 'thông minh' đặc biệt biết về nguồn gốc của nó. Bạn có thể hỏi nó đến từ đâu thông qua phương thức getparent() của nó, giống như bạn làm với Elements

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
3

Bạn cũng có thể biết đó là nội dung văn bản thường hay văn bản đuôi

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
4

Trong khi điều này hoạt động đối với các kết quả của hàm text(), lxml sẽ không cho bạn biết nguồn gốc của một giá trị chuỗi được tạo bởi các hàm XPath string() hoặc concat()

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
5

lặp cây

Đối với các vấn đề như trên, nơi bạn muốn duyệt qua cây một cách đệ quy và làm điều gì đó với các phần tử của nó, phép lặp cây là một giải pháp rất thuận tiện. Các phần tử cung cấp một trình lặp cây cho mục đích này. Nó mang lại các phần tử theo thứ tự tài liệu, tôi. e. theo thứ tự các thẻ của chúng sẽ xuất hiện nếu bạn tuần tự hóa cây thành XML

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
6

Nếu bạn biết bạn chỉ quan tâm đến một thẻ duy nhất, bạn có thể chuyển tên của nó cho iter() để nó lọc cho bạn. Bắt đầu với lxml 3. 0, bạn cũng có thể chuyển nhiều thẻ để chặn trên nhiều thẻ trong quá trình lặp lại

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
7

Theo mặc định, phép lặp tạo ra tất cả các nút trong cây, bao gồm các phiên bản Xử lý Hướng dẫn, Nhận xét và Thực thể. Nếu bạn muốn đảm bảo chỉ các đối tượng Phần tử được trả về, bạn có thể chuyển Element factory làm tham số thẻ

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
8

Lưu ý rằng việc chuyển tên thẻ ký tự đại diện "*" cũng sẽ mang lại tất cả các nút Phần tử (và chỉ các phần tử)

trong lxml. etree, các phần tử cung cấp cho tất cả các hướng trong cây. con cái, cha mẹ (hay đúng hơn là tổ tiên) và anh chị em

Tuần tự hóa

Tuần tự hóa thường sử dụng hàm tostring() trả về một chuỗi hoặc ElementTree. write() phương thức ghi vào tệp, đối tượng giống như tệp hoặc URL (thông qua FTP PUT hoặc HTTP POST). Cả hai cuộc gọi đều chấp nhận các đối số từ khóa giống nhau như pretty_print cho đầu ra được định dạng hoặc mã hóa để chọn một mã hóa đầu ra cụ thể khác với ASCII thuần túy

>>> child2 = etree.SubElement(root, "child2")
>>> child3 = etree.SubElement(root, "child3")
9

Lưu ý rằng bản in đẹp sẽ thêm một dòng mới ở cuối

Để kiểm soát chi tiết hơn đối với bản in đẹp, bạn có thể thêm thụt lề khoảng trắng vào cây trước khi sắp xếp theo thứ tự, sử dụng hàm indent() (được thêm vào trong lxml 4. 5)

>>> print(etree.tostring(root, pretty_print=True))

  
  
  

0

Trong lxml 2. 0 trở lên (cũng như ElementTree 1. 3), các chức năng tuần tự hóa có thể làm nhiều hơn việc tuần tự hóa XML. Bạn có thể tuần tự hóa thành HTML hoặc trích xuất nội dung văn bản bằng cách chuyển từ khóa phương thức

>>> print(etree.tostring(root, pretty_print=True))

  
  
  

1

Đối với tuần tự hóa XML, mã hóa mặc định cho tuần tự hóa văn bản thuần túy là ASCII

>>> print(etree.tostring(root, pretty_print=True))

  
  
  

2

Ở đây, tuần tự hóa thành chuỗi unicode Python thay vì chuỗi byte có thể trở nên hữu ích. Chỉ cần chuyển tên 'unicode' làm mã hóa