Hướng dẫn use lxml in python - sử dụng lxml trong python

Author:Stefan Behnel

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

Để biết tham khảo đầy đủ của API, hãy xem tài liệu API được tạo.

Một cách phổ biến để nhập lxml.etree như sau:

>>> from lxml import etree

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 làm lại cho phần tử gốc:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")

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

Lớp phần tử

Một phần tử là đối tượng container chính cho API ElementTree. Hầu hết các chức năng cây XML được truy cập thông qua lớp này. Các yếu tố dễ dàng được tạo ra thông qua nhà máy phần tử:

>>> 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 yếu tố được tổ chức trong 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 để làm điều này: nhà máy Subelement. Nó chấp nhận các đối số giống như nhà máy phần tử, nhưng cũng yêu cầu cha mẹ là đố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 bạn đã tạo:

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

  
  
  

Các yếu tố là danh sách

Để làm cho việc truy cập vào các subels này dễ dàng và thẳng tiến, các yếu tố bắt chước hành vi của các danh sách python bình thường càng gần 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ị sự thật của một yếu tố để xem nó có con không, tức là nếu danh sách trẻ em 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 "cái gì đó" đánh giá đúng và mong đợi các yếu tố sẽ là "một cái gì đó", họ có thể có con hay không. Vì vậy, nhiều người dùng thấy đáng ngạc nhiên khi bất kỳ yếu tố nào sẽ đánh giá sai trong một câu chuyện nếu ở trên. Thay vào đó, sử dụng Len (phần tử), vừa rõ ràng hơn và ít 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 trong đó hành vi của các phần tử trong LXML (trong 2.0 trở lên) lệch khỏi danh sách và so với phần tử gốc (trước phiên bản 1.3 hoặc Python 2.7/3.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 chuyển đến một vị trí khác, thay vì được sao chép, tức là nó được tự động 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à bài tập trên sẽ chỉ sao chép tham chiếu mục vào vị trí đầu tiên, để cả hai chứa cùng một mục chính xác:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
0

Lưu ý rằng trong phần tử gốc, một đối tượng phần tử duy nhất có thể ngồi ở bất kỳ số lượng vị trí nào trong bất kỳ số lượng cây nào, cho phép hoạt động 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 một yếu tố như vậy sẽ áp dụng cho tất cả các nơi xuất hiện trong một cái cây, có thể hoặc không thể dự định.

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 phần tử gốc.

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
1

Nếu bạn muốn sao chép một phần tử vào 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 mô -đun sao chép từ thư viện tiêu chuẩn của Python:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
2

Anh chị em (hoặc hàng xóm) của một yếu tố được truy cập như các yếu tố tiếp theo và trước đó:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
3

Các yếu tố mang thuộc tính như một dict

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 nhà máy Element:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
4

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

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
5

Đối với các trường hợp bạn muốn thực hiện tra cứu vật phẩm hoặc có những lý do khác để có được một đối tượng giống như từ điển 'thực', ví dụ: Để chuyển nó xung quanh, bạn có thể sử dụng thuộc tính Attract:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
6

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

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
7

Các yếu tố chứa văn bản

Các yếu tố có thể chứa văn bản:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
8

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 gói gọn 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ẻ như (x) HTML, văn bản cũng có thể xuất hiện giữa các yếu tố khác nhau, ngay giữa cây:

try:
  from lxml import etree
  print("running with lxml.etree")
except ImportError:
  try:
    # Python 2.5
    import xml.etree.cElementTree as etree
    print("running with cElementTree on Python 2.5+")
  except ImportError:
    try:
      # Python 2.5
      import xml.etree.ElementTree as etree
      print("running with ElementTree on Python 2.5+")
    except ImportError:
      try:
        # normal cElementTree install
        import cElementTree as etree
        print("running with cElementTree")
      except ImportError:
        try:
          # normal ElementTree install
          import elementtree.ElementTree as etree
          print("running with ElementTree")
        except ImportError:
          print("Failed to import ElementTree from any known place")
9

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

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

0

Hai thuộc tính .text và .tail là đủ để 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ỳ nút văn bản đặc biệt nào ngoài lớp phần tử, có xu hướng đi theo cách khá thường xuyên (như bạn có thể biết từ API DOM cổ điển).

Tuy nhiên, có những trường hợp văn bản đuôi cũng cản trở. Ví dụ: khi bạn nối tiếp một phần tử từ bên trong cây, bạn không phải lúc nào 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 con cái). Đối với mục đích này, hàm toString () chấp nhận đối số từ khóa với_tail:

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

1

Nếu bạn chỉ muốn đọc văn bản, tức là không có bất kỳ thẻ trung gian nào, bạn phải ghép lại 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 () đi đến giải cứu, lần này bằng cách sử dụng từ khóa phương thức:

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

2

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à XPath, cũng cho phép bạn trích xuất các khối văn bản riêng biệt vào danh sách:

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

3

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

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

4

Lưu ý rằng 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 các yếu tố:

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

5

Bạn cũng có thể tìm hiểu xem đó là nội dung văn bản thông thường hoặc văn bản đuôi:

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

6

Mặc dù điều này hoạt động cho kết quả của hàm văn bản (), LXML sẽ không cho bạn biết nguồn gốc của giá trị chuỗi được xây dựng bởi chuỗi hàm XPath

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

7

Lặp lại cây

Đối với các vấn đề như ở trên, nơi bạn muốn đi qua cây đệ quy và làm điều gì đó với các yếu tố của nó, việc lặp lại cây là một giải pháp rất thuận tiện. Các yếu tố cung cấp một bộ 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ức là theo thứ tự các thẻ của họ sẽ xuất hiện nếu bạn tuần tự hóa cây thành XML:

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

8

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 () để có bộ 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.

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

9

Theo mặc định, việc lặp lại mang lại tất cả các nút trong cây, bao gồm cả xử lý, nhận xét và các phiên bản thực thể. Nếu bạn muốn đảm bảo chỉ trả về các đối tượng phần tử, bạn có thể chuyển phần của Factory Factory dưới dạng tham số thẻ:

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

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 yếu tố cung cấp thêm các trình lặp lại cho tất cả các hướng trong cây: trẻ em, cha mẹ (hay đúng hơn là tổ tiên) và anh chị em.

Tuần tự hóa

Xét nghiệm hóa thường sử dụng hàm toString () trả về một chuỗi hoặc phương thức ElementTree.write () ghi vào một tệp, đối tượng giống như tệp hoặc URL (thông qua Post FTP Put hoặc HTTP). Cả hai cuộc gọi đều chấp nhận cùng một đối số từ khóa 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 ngoài ASCII đơn giản:

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

Lưu ý rằng Printing in lại một dòng mới ở cuối.

Để có thêm điều khiển hạt mịn hơn đối với in đẹp, bạn có thể thêm thụt lề khoảng trắng vào cây trước khi tuần tự hóa nó, sử dụng hàm thụt lề () (được thêm vào LXML 4.5):

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

Trong LXML 2.0 trở lên (cũng như ElementTree 1.3), các hàm tuần tự hóa có thể thực hiện nhiều hơn XML tuần tự hóa. Bạn có thể nối tiếp với HTML hoặc trích xuất nội dung văn bản bằng cách truyền từ khóa phương thức:

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

Đối với tuần tự hóa XML, mã hóa mặc định để tuần tự hóa văn bản đơn giản là ASCII:

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

Ở đây, tuần tự hóa thành chuỗi unicode Python thay vì chuỗi byte có thể trở nên tiện dụng. Chỉ cần chuyển tên 'Unicode' như mã hóa:

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

W3C có một bài viết hay về bộ ký tự Unicode và mã hóa ký tự.

Lớp ElementTree

Một ElementTree chủ yếu là một trình bao bọc tài liệu xung quanh một cây có nút gốc. Nó cung cấp một vài phương pháp để tuần tự hóa và xử lý tài liệu chung.

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

Một ElementTree cũng là những gì bạn lấy lại khi bạn gọi hàm parse () vào các tệp phân tích phân tích hoặc các đối tượng giống như tệp (xem phần phân tích cú pháp bên dưới).

Một trong những khác biệt quan trọng là các lớp ElementTree là một tài liệu hoàn chỉnh, trái ngược với một yếu tố duy nhất. Điều này bao gồm các hướng dẫn và nhận xét xử lý cấp cao nhất, cũng như DOCTYPE và nội dung DTD khác trong tài liệu:

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

Trong triển khai xml.etree.elementtree gốc và trong LXML lên tới 1.3.3, đầu ra trông giống như khi chỉ serialising phần tử gốc:

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

Hành vi tuần tự này đã thay đổi trong LXML 1.3.4. Trước đây, cây đã được tuần tự hóa mà không có nội dung DTD, khiến LXML mất thông tin DTD trong chu kỳ đầu vào-đầu ra.

Phân tích cú pháp từ chuỗi và tệp

lxml.etree hỗ trợ phân tích cú pháp XML theo một số cách và từ tất cả các nguồn quan trọng, cụ thể là chuỗi, tệp, URL (HTTP/FTP) và các đối tượng giống như tệp. Các hàm phân tích chính là FromString () và Parse (), cả hai được gọi với nguồn là đối số đầu tiên. Theo mặc định, họ sử dụng trình phân tích cú pháp tiêu chuẩn, nhưng bạn luôn có thể chuyển một trình phân tích cú pháp khác làm đối số thứ hai.

Hàm fromString ()

Hàm fromString () là cách dễ nhất để phân tích chuỗi:

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

Hàm xml ()

Hàm xml () hoạt động giống như hàm fromString (), nhưng thường được sử dụng để viết chữ XML ngay vào nguồn:

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

Ngoài ra còn có một hàm tương ứng html () cho các chữ HTML.

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

Hàm phân tích cú pháp ()

Hàm parse () được sử dụng để phân tích từ các tệp và các đối tượng giống như tệp.

Là một ví dụ về một đối tượng giống như tệp như vậy, mã sau sử dụng lớp byte để đọc từ một chuỗi thay vì một tệp bên ngoài. Lớp đó đến từ mô -đun IO trong Python 2.6 trở lên. Trong các phiên bản Python cũ hơn, bạn sẽ phải sử dụng lớp Stringio từ mô -đun Stringio. Tuy nhiên, trong cuộc sống thực, rõ ràng bạn sẽ tránh làm tất cả điều này cùng nhau và sử dụng các hàm phân tích chuỗi ở trên.

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

Lưu ý rằng parse () trả về một đối tượng ElementTree, không phải là đối tượng phần tử như các hàm trình phân tích cú pháp chuỗi:

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

Lý do đằng sau sự khác biệt này là parse () trả về một tài liệu hoàn chỉnh từ một tệp, trong khi các hàm phân tích chuỗi thường được sử dụng để phân tích các đoạn XML.

Hàm Parse () hỗ trợ bất kỳ nguồn nào sau đây:

  • một đối tượng tệp mở (đảm bảo mở nó ở chế độ nhị phân)
  • một đối tượng giống như tệp có phương thức .read (byte_count) trả về chuỗi byte trên mỗi cuộc gọi
  • một chuỗi tên tệp
  • Chuỗi URL HTTP hoặc FTP

Lưu ý rằng việc chuyển tên tệp hoặc URL thường nhanh hơn so với việc truyền một tệp mở hoặc đối tượng giống như tệp. Tuy nhiên, máy khách HTTP/FTP trong libxml2 khá đơn giản, vì vậy những thứ như xác thực HTTP yêu cầu thư viện yêu cầu URL chuyên dụng, ví dụ: urllib2 hoặc yêu cầu. Các thư viện này thường cung cấp một đối tượng giống như tệp cho kết quả mà bạn có thể phân tích cú pháp trong khi phản hồi đang phát trực tuyến.

Đối tượng phân tích cú pháp

Theo mặc định, lxml.etree sử dụng trình phân tích cú pháp tiêu chuẩn với thiết lập mặc định. Nếu bạn muốn định cấu hình trình phân tích cú pháp, bạn có thể tạo một thể hiện mới:

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

Điều này tạo ra một trình phân tích cú pháp loại bỏ văn bản trống giữa các thẻ trong khi phân tích cú pháp, điều này có thể làm giảm kích thước của cây và tránh văn bản đuôi lủng lẳng nếu bạn biết rằng nội dung chỉ có khoảng trắng không có ý nghĩa đối với dữ liệu của bạn. Một ví dụ:

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

Lưu ý rằng nội dung khoảng trắng bên trong thẻ không được xóa, vì nội dung tại các phần tử lá có xu hướng là nội dung dữ liệu (ngay cả khi trống). Bạn có thể dễ dàng loại bỏ nó trong một bước bổ sung bằng cách đi qua cây:

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

Xem Trợ giúp (Etree.xmlparser) để tìm hiểu về các tùy chọn trình phân tích cú pháp có sẵn.

Phân tích cú pháp gia tăng

lxml.etree cung cấp hai cách để phân tích từng bước tăng dần. Một là thông qua các đối tượng giống như tệp, trong đó nó gọi phương thức đọc () nhiều lần. Điều này được sử dụng tốt nhất khi dữ liệu đến từ một nguồn như Urllib hoặc bất kỳ đối tượng giống như tệp nào khác có thể cung cấp dữ liệu theo yêu cầu. Lưu ý rằng trình phân tích cú pháp sẽ chặn và đợi cho đến khi dữ liệu có sẵn trong trường hợp này:

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

Cách thứ hai là thông qua giao diện trình phân tích cú pháp nguồn cấp dữ liệu, được đưa ra bởi các phương thức Feed (Data) và Close ():

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

Tại đây, bạn có thể làm gián đoạn quá trình phân tích cú pháp bất cứ lúc nào và tiếp tục sau đó với một cuộc gọi khác đến phương thức Feed (). Điều này có ích nếu bạn muốn tránh chặn các cuộc gọi đến trình phân tích cú pháp, ví dụ: Trong các khung như xoắn, hoặc bất cứ khi nào dữ liệu đến chậm hoặc trong các khối và bạn muốn làm những việc khác trong khi chờ đợi phần tiếp theo.

Sau khi gọi phương thức đóng () (hoặc khi một ngoại lệ được trình phân tích cú pháp), bạn có thể sử dụng lại trình phân tích cú pháp bằng cách gọi lại phương thức Feed () của nó:

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

Phân tích phân tích theo sự kiện

Đôi khi, tất cả những gì bạn cần từ một tài liệu là một phần nhỏ ở đâu đó sâu bên trong cây, vì vậy việc phân tích toàn bộ cây vào bộ nhớ, vượt qua nó và thả nó có thể quá cao. LXML.ETREE hỗ trợ trường hợp sử dụng này với hai giao diện phân tích cú pháp theo sự kiện, một giao diện tạo ra các sự kiện phân tích cú pháp trong khi xây dựng cây (iterparse) và một cái không xây dựng cây và thay vào đó gọi các phương thức phản hồi trên đối tượng đích trong một đối tượng trong một Thời trang giống như sax.

Đây là một ví dụ iterparse () đơn giản:

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

  
  
  

0

Theo mặc định, iterparse () chỉ tạo các sự kiện khi thực hiện phân tích lại một phần tử, nhưng bạn có thể kiểm soát điều này thông qua đối số từ khóa của sự kiện:

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

  
  
  

1

Lưu ý rằng văn bản, đuôi và trẻ em của một yếu tố chưa nhất thiết phải có khi nhận được sự kiện bắt đầu. Chỉ có sự kiện cuối cùng đảm bảo rằng phần tử đã được phân tích hoàn toàn.

Nó cũng cho phép bạn .clear () hoặc sửa đổi nội dung của một phần tử để lưu bộ nhớ. Vì vậy, nếu bạn phân tích một cây lớn và bạn muốn giữ cho việc sử dụng bộ nhớ nhỏ, bạn nên làm sạch các phần của cây mà bạn không còn cần. Đối số keep_tail = true cho .clear () đảm bảo rằng nội dung văn bản (đuôi) theo phần tử hiện tại sẽ không được chạm vào. Nó rất nản lòng khi sửa đổi bất kỳ nội dung nào mà trình phân tích cú pháp có thể chưa hoàn toàn đọc qua.

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

  
  
  

2

Một trường hợp sử dụng rất quan trọng cho iterparse () đang phân tích các tệp XML được tạo lớn, ví dụ: Cơ sở dữ liệu đổ. Thông thường, các định dạng XML này chỉ có một phần tử mục dữ liệu chính được treo ngay bên dưới nút gốc và được lặp lại hàng ngàn lần. Trong trường hợp này, tốt nhất là để LXML.etree thực hiện việc xây dựng cây và chỉ để chặn chính xác một phần tử này, sử dụng API cây thông thường để trích xuất dữ liệu.

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

  
  
  

3

Nếu, vì một số lý do, việc xây dựng cây hoàn toàn không mong muốn, giao diện phân tích cú pháp mục tiêu của LXML.ETREE có thể được sử dụng. Nó tạo ra các sự kiện giống như sax bằng cách gọi các phương thức của một đối tượng đích. Bằng cách thực hiện một số hoặc tất cả các phương pháp này, bạn có thể kiểm soát sự kiện nào được tạo ra:

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

  
  
  

4

Bạn có thể sử dụng lại trình phân tích cú pháp và mục tiêu của nó thường xuyên như bạn muốn, vì vậy bạn nên quan tâm đến phương thức .close () thực sự đặt lại mục tiêu về trạng thái có thể sử dụng (cũng trong trường hợp có lỗi!).

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

  
  
  

5

Không gian tên

API ElementTree tránh các tiền tố không gian tên bất cứ khi nào có thể và triển khai không gian tên thực (URI) thay thế: thay vào đó:

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

  
  
  

6

Ký hiệu mà Elementtree sử dụng ban đầu được James Clark đưa lên. Nó có lợi thế lớn là cung cấp một tên đủ điều kiện cho một thẻ, bất kể tiền tố có thể hoặc không được sử dụng hoặc không được xác định trong một tài liệu. Bằng cách di chuyển sự Interection của các tiền tố ra khỏi đường đi, nó làm cho mã nhận thức không gian tên rõ ràng hơn và dễ dàng hơn để có được đúng.

Như bạn có thể thấy từ ví dụ, tiền tố chỉ trở nên quan trọng khi bạn tuần tự hóa kết quả. Tuy nhiên, mã trên có vẻ hơi dài dòng do tên không gian tên dài. Và retyping hoặc sao chép một chuỗi nhiều lần là lỗi dễ bị lỗi. Do đó, thông thường là lưu trữ một URI không gian tên trong một biến toàn cầu. Để điều chỉnh các tiền tố không gian tên để tuần tự hóa, bạn cũng có thể chuyển ánh xạ đến hàm nhà máy phần tử, ví dụ: Để xác định không gian tên mặc định:

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

  
  
  

7

Bạn cũng có thể sử dụng lớp trợ giúp Qname để xây dựng hoặc phân chia tên thẻ đủ điều kiện:

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

  
  
  

8

lxml.etree cho phép bạn tra cứu các không gian tên hiện tại được xác định cho một nút thông qua thuộc tính .nsmap:

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

  
  
  

9

Tuy nhiên, lưu ý rằng điều này bao gồm tất cả các tiền tố được biết đến trong bối cảnh của một yếu tố, không chỉ những phần tử mà nó tự xác định.

>>> 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
0

Do đó, việc sửa đổi dict đã trả lại không thể có bất kỳ tác động có ý nghĩa nào đến yếu tố. Bất kỳ thay đổi nào đối với nó đều bị bỏ qua.

Các không gian tên trên các thuộc tính hoạt động giống nhau, nhưng kể từ phiên bản 2.3, lxml.etree sẽ đảm bảo rằng thuộc tính sử dụng khai báo không gian tên tiền tố. Điều này là do các tên thuộc tính không bị hủy không được coi là ở trong không gian tên bởi đặc tả không gian tên XML (phần 6.2), vì vậy chúng có thể cuối cùng mất không gian tên của chúng trên một vòng tròn phân tích nối tiếp, ngay cả khi chúng xuất hiện trong một phần tử theo tên.

>>> 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
1

Bạn cũng có thể sử dụng XPath với tên đủ điều kiện:

>>> 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
2

Để thuận tiện, bạn có thể sử dụng các ký tự đại diện "*" trong tất cả các trình lặp của lxml.etree, cả cho tên thẻ và không gian tên:

>>> 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
3

Để tìm kiếm các phần tử không có không gian tên, hoặc sử dụng tên thẻ đơn giản hoặc cung cấp không gian tên trống một cách rõ ràng:

>>> 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
4

Bệnh điện tử

E-Factory cung cấp một cú pháp đơn giản và nhỏ gọn để tạo XML và HTML:E-factory provides a simple and compact syntax for generating XML and HTML:

>>> 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
5

Tạo phần tử dựa trên quyền truy cập thuộc tính giúp dễ dàng xây dựng một từ vựng đơn giản cho ngôn ngữ XML:

>>> 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
6

Một ví dụ như vậy là mô -đun lxml.html.builder, cung cấp từ vựng cho HTML.

Khi xử lý nhiều không gian tên, việc định nghĩa một yếu tố cho mỗi URI không gian tên là tốt. Một lần nữa, lưu ý làm thế nào ví dụ trên xác định trước các nhà xây dựng thẻ trong các hằng số được đặt tên. Điều đó giúp dễ dàng đặt tất cả các khai báo thẻ của một không gian tên vào một mô -đun Python và nhập/sử dụng hằng số tên thẻ từ đó. Điều này tránh những cạm bẫy như lỗi chính tả hoặc vô tình thiếu các không gian tên.

Trú mẹ nguyên tố

Thư viện ElementTree đi kèm với ngôn ngữ đường dẫn giống như XPath đơn giản được gọi là ElementPath. Sự khác biệt chính là bạn có thể sử dụng ký hiệu thẻ {Namespace} trong các biểu thức ElementPath. Tuy nhiên, các tính năng nâng cao như so sánh giá trị và các chức năng không có sẵn.

Ngoài việc triển khai XPath đầy đủ, LXML.ETREE hỗ trợ Ngôn ngữ ElementPath theo cùng một cách mà ElementTree thực hiện, thậm chí sử dụng (gần như) cùng một triển khai. API cung cấp bốn phương pháp ở đây mà bạn có thể tìm thấy trên các phần tử và ElementTrees:

  • iterfind () lặp lại trên tất cả các yếu tố phù hợp với biểu thức đường dẫn
  • findall () trả về một danh sách các yếu tố phù hợp
  • find () chỉ trả về một cách hiệu quả trận đấu đầu tiên
  • FindText () trả về nội dung .text của trận đấu đầu tiên

Dưới đây là một số ví dụ:

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

Tìm một đứa trẻ của một yếu 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
8

Tìm một phần tử ở bất cứ đâu trong cây:

>>> 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
9

Tìm các phần tử với một thuộc tính nhất định:

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

Trong LXML 3.4, có một người trợ giúp mới để tạo biểu thức cơ bản cấu trúc cho một phần tử:

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

Miễn là cây không được sửa đổi, biểu thức đường dẫn này biểu thị một định danh cho một phần tử nhất định có thể được sử dụng để tìm () nó trong cùng một cây sau đó. So với XPath, các biểu thức ElementPath có lợi thế là khép kín ngay cả đối với các tài liệu sử dụng không gian tên.

Phương thức .iter () là một trường hợp đặc biệt chỉ tìm thấy các thẻ cụ thể trong cây bằng tên của chúng, không dựa trên đường dẫn. Điều đó có nghĩa là các lệnh sau tương đương trong trường hợp thành công:

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

Lưu ý rằng phương thức .find () chỉ đơn giản là không tìm thấy không có trận đấu nào được tìm thấy, trong khi hai ví dụ khác sẽ nêu ra một ngoại lệ dừng lại.