2008-11-27 52 views
45

Tôi hiện đang mã sau đây dựa trên Chương 12.5 của Python Cookbook:Cách nhanh nhất để phân tích cú pháp tài liệu XML lớn bằng Python là gì?

from xml.parsers import expat 

class Element(object): 
    def __init__(self, name, attributes): 
     self.name = name 
     self.attributes = attributes 
     self.cdata = '' 
     self.children = [] 
    def addChild(self, element): 
     self.children.append(element) 
    def getAttribute(self,key): 
     return self.attributes.get(key) 
    def getData(self): 
     return self.cdata 
    def getElements(self, name=''): 
     if name: 
      return [c for c in self.children if c.name == name] 
     else: 
      return list(self.children) 

class Xml2Obj(object): 
    def __init__(self): 
     self.root = None 
     self.nodeStack = [] 
    def StartElement(self, name, attributes): 
     element = Element(name.encode(), attributes) 
     if self.nodeStack: 
      parent = self.nodeStack[-1] 
      parent.addChild(element) 
     else: 
      self.root = element 
     self.nodeStack.append(element) 
    def EndElement(self, name): 
     self.nodeStack.pop() 
    def CharacterData(self,data): 
     if data.strip(): 
      data = data.encode() 
      element = self.nodeStack[-1] 
      element.cdata += data 
    def Parse(self, filename): 
     Parser = expat.ParserCreate() 
     Parser.StartElementHandler = self.StartElement 
     Parser.EndElementHandler = self.EndElement 
     Parser.CharacterDataHandler = self.CharacterData 
     ParserStatus = Parser.Parse(open(filename).read(),1) 
     return self.root 

tôi đang làm việc với tài liệu XML khoảng 1 GB. Có ai biết cách phân tích cú pháp này nhanh hơn không?

+2

Câu hỏi của bạn quá mơ hồ để thu thập bất kỳ câu trả lời hữu ích nào. Cân nhắc trả lời các câu hỏi sau: - Bạn đang cố gắng làm gì với tài liệu XML 1 GB này? - Bạn cần trình phân tích cú pháp này nhanh đến mức nào? - Bạn có thể lười biếng lặp lại thông qua tài liệu, thay vì tải mọi thứ vào bộ nhớ từ khi di chuyển? – Matt

+2

Tôi cần tải tất cả vào bộ nhớ, lập chỉ mục dữ liệu và sau đó 'duyệt qua' và xử lý nó. –

Trả lời

52

Tôi nhìn tôi như thể bạn không cần bất kỳ khả năng DOM nào từ chương trình của mình. Tôi sẽ thứ hai sử dụng thư viện (c) ElementTree. Nếu bạn sử dụng hàm iterparse của mô-đun cElementTree, bạn có thể làm việc theo cách của bạn thông qua xml và xử lý các sự kiện khi chúng xảy ra.

Lưu ý tuy nhiên, Fredriks lời khuyên về việc sử dụng cElementTree iterparse function:

để phân tích các file lớn, bạn có thể thoát khỏi các yếu tố ngay sau khi bạn đã xử lý chúng:

for event, elem in iterparse(source): 
    if elem.tag == "record": 
     ... process record elements ... 
     elem.clear() 

Mẫu trên có một nhược điểm; nó không xóa phần tử gốc, vì vậy bạn sẽ kết thúc với một phần tử đơn lẻ với nhiều phần tử con trống. Nếu tệp của bạn rất lớn, thay vì chỉ lớn, điều này có thể là một vấn đề. Để giải quyết vấn đề này, bạn cần nắm lấy phần tử gốc. Cách dễ nhất để làm điều này là để cho phép sự kiện bắt đầu, và lưu một tham chiếu đến phần tử đầu tiên trong một biến:

# get an iterable 
context = iterparse(source, events=("start", "end")) 

# turn it into an iterator 
context = iter(context) 

# get the root element 
event, root = context.next() 

for event, elem in context: 
    if event == "end" and elem.tag == "record": 
     ... process record elements ... 
     root.clear() 

các lxml.iterparse() không cho phép điều này.

4

Đăng ký cuộc gọi lại sẽ làm chậm quá trình phân tích cú pháp. [EDIT] Điều này là do (C) nhanh mã đã gọi trình thông dịch python mà chỉ là không nhanh như C. Về cơ bản, bạn đang sử dụng mã C để đọc các tập tin (nhanh) và sau đó xây dựng DOM trong Python (chậm). [/ EDIT]

Hãy thử sử dụng xml.etree.ElementTree được triển khai 100% trong C và có thể phân tích cú pháp XML mà không có bất kỳ cuộc gọi lại nào tới mã python.

Sau khi tài liệu đã được phân tích cú pháp, bạn có thể lọc nó để có được những gì bạn muốn.

Nếu điều đó vẫn còn quá chậm và bạn không cần tùy chọn DOM khác là đọc tệp vào chuỗi và sử dụng các thao tác chuỗi đơn giản để xử lý.

+0

Đây là lời khuyên rất gây hiểu lầm. Không có gì về một trình phân tích cú pháp XML dựa trên callback vốn thực chất là chậm. Hơn nữa, OP đã sử dụng các ràng buộc ngoại lệ của Python, cũng là bản địa C. – Matt

+0

Trình thông dịch trăn luôn chậm hơn mã C được biên dịch gốc. Và như bạn có thể thấy rõ trong mã trong câu hỏi, nó đăng ký mã Python để được gọi cho mọi phần tử! Và mã này cũng thực hiện rất nhiều công việc! –

+0

Điều này nên được tăng lên, callbacks trong python là thực sự chậm, bạn muốn tránh điều đó và làm càng nhiều càng tốt trong C đất. –

8

Tôi khuyên bạn nên sử dụng lxml, đó là liên kết python cho thư viện libxml2 thực sự nhanh.

Theo kinh nghiệm của tôi, libxml2 và người nước ngoài có hiệu suất rất giống nhau. Nhưng tôi thích libxml2 (và lxml cho python) bởi vì nó dường như được tích cực hơn phát triển và thử nghiệm. Ngoài ra libxml2 còn có nhiều tính năng hơn.

lxml chủ yếu là API tương thích với xml.etree.ElementTree. Và có tài liệu tốt trong trang web của nó.

+2

lxml là quy tắc! :) – ddaa

15

Bạn đã thử Mô-đun cElementTree chưa?

cElementTree được bao gồm trong Python 2.5 trở lên, dưới dạng xml.etree.cElementTree. Tham khảo benchmarks.

loại bỏ ImageShack chết liên kết

+0

hình ảnh không hiển thị: ( – fedorqui

4

Nếu ứng dụng của bạn là hiệu suất nhạy cảm và có khả năng gặp phải các file lớn (như bạn nói,> 1GB) sau đó tôi muốn mạnh khuyên không nên sử dụng mã bạn hiển thị trong câu hỏi của bạn vì lý do đơn giản là nó tải toàn bộ tài liệu vào RAM. Tôi sẽ khuyến khích bạn suy nghĩ lại về thiết kế của bạn (nếu có thể) để tránh giữ toàn bộ cây tài liệu trong RAM cùng một lúc. Không biết yêu cầu của ứng dụng của bạn là gì, tôi không thể đề xuất chính xác bất kỳ phương pháp cụ thể nào, ngoài lời khuyên chung để cố gắng sử dụng thiết kế "dựa trên sự kiện".

0

Rõ ràng PyRXP thực sự nhanh.

Họ tuyên bố đó là trình phân tích cú pháp nhanh nhất - nhưng cElementTree không có trong danh sách thống kê của họ.

1

ParseFile người nước ngoài hoạt động tốt nếu bạn không cần phải lưu trữ toàn bộ cây trong bộ nhớ, mà sớm hay muộn sẽ thổi RAM của bạn cho các tập tin lớn:

import xml.parsers.expat 
parser = xml.parsers.expat.ParserCreate() 
parser.ParseFile(open('path.xml', 'r')) 

Nó đọc các tập tin thành nhiều phần, và cung cấp chúng cho trình phân tích cú pháp mà không cần phải giải mã RAM.

Tài liệu: https://docs.python.org/2/library/pyexpat.html#xml.parsers.expat.xmlparser.ParseFile

Các vấn đề liên quan