2012-03-24 28 views
10

Tôi phải phân tích cú pháp tệp XML 1Gb với cấu trúc như bên dưới và trích xuất văn bản trong thẻ "Tác giả" và "Nội dung":sử dụng lxml và iterparse() để phân tích tệp XML lớn (+ - 1Gb)

<Database> 
    <BlogPost> 
     <Date>MM/DD/YY</Date> 
     <Author>Last Name, Name</Author> 
     <Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content> 
    </BlogPost> 

    <BlogPost> 
     <Date>MM/DD/YY</Date> 
     <Author>Last Name, Name</Author> 
     <Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content> 
    </BlogPost> 

    [...] 

    <BlogPost> 
     <Date>MM/DD/YY</Date> 
     <Author>Last Name, Name</Author> 
     <Content>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula.</Content> 
    </BlogPost> 
</Database> 

Cho đến nay tôi đã thử hai thứ: i) đọc toàn bộ tệp và thực hiện nó với .find (xmltag) và ii) phân tích cú pháp tệp xml bằng lxml và iterparse(). Tùy chọn đầu tiên tôi có nó hoạt động, nhưng nó rất chậm. Tùy chọn thứ hai tôi đã không quản lý để có được nó ra khỏi mặt đất.

Dưới đây là một phần của những gì tôi có:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"): 
    if element.tag == "BlogPost": 
     print element.text 
    else: 
     print 'Finished' 

Kết quả của việc đó là chỉ khoảng trống, không có văn bản trong đó.

Tôi phải làm điều gì đó sai, nhưng tôi không thể hiểu được. Ngoài ra, trong trường hợp nó không đủ rõ ràng, tôi khá mới với python và đây là lần đầu tiên tôi sử dụng lxml. Hãy giúp tôi!

+1

Vâng, các thẻ 'BlogPost' dường như không chứa bất kỳ văn bản nào trong đó. –

+0

Đúng. Điều gì sẽ là cách để có được tất cả mọi thứ đó là giữa việc mở và đóng BlogPost tag? – mvime

+0

Nếu bạn chỉ cần tất cả thông tin từ bên trong thẻ 'BlogPost', hãy làm theo lời khuyên của andrew. Nếu bạn muốn định dạng HTML, hãy áp dụng 'lxml.etree.tostring()' cho chúng. –

Trả lời

18
for event, element in etree.iterparse(path_to_file, tag="BlogPost"): 
    for child in element: 
     print child.tag, child.text 
    element.clear() 

rõ ràng cuối cùng sẽ dừng bạn từ việc sử dụng quá nhiều bộ nhớ.

[update:] để có được "tất cả mọi thứ giữa ... như là một chuỗi" tôi đoán bạn muốn một trong số:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"): 
    print etree.tostring(element) 
    element.close() 

hoặc

for event, element in etree.iterparse(path_to_file, tag="BlogPost"): 
    print ''.join([etree.tostring(child) for child in element]) 
    element.close() 

hoặc thậm chí:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"): 
    print ''.join([child.text for child in element]) 
    element.close() 
+0

Điều này hoạt động khá giống như tôi muốn tôi sẽ phải tùy chỉnh nó một chút, nhưng nó tuyệt vời. Cảm ơn! – mvime

+0

Có cách nào để nhận mọi thứ giữa bắt đầu và kết thúc các thẻ "BlogPost" dưới dạng chuỗi không? – mvime

+1

@mvime, như loại chuỗi nào? Ở định dạng HTML? Sau đó, xem bình luận của tôi ở trên, phương thức 'lxml.etree.tostring()' thực hiện điều đó. Bạn có thể cắt thẻ mở và đóng bằng ký hiệu slice (xem [bảng này] (http://docs.python.org/library/stdtypes.html#sequence-types-str-unicode-list-tuple-bytearray-buffer -xrange)) –

4

tôi thích XPath cho những thứ như:

In [1]: from lxml.etree import parse 

In [2]: tree = parse('/tmp/database.xml') 

In [3]: for post in tree.xpath('/Database/BlogPost'): 
    ...:  print 'Author:', post.xpath('Author')[0].text 
    ...:  print 'Content:', post.xpath('Content')[0].text 
    ...: 
Author: Last Name, Name 
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula. 
Author: Last Name, Name 
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula. 
Author: Last Name, Name 
Content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas dictum dictum vehicula. 

Tôi không chắc chắn nếu nó khác nhau về xử lý các file lớn, mặc dù. Nhận xét về điều này sẽ được đánh giá cao.

Làm theo cách của bạn,

for event, element in etree.iterparse(path_to_file, tag="BlogPost"): 
    for info in element.iter(): 
     if info.tag in ('Author', 'Content'): 
      print info.tag, ':', info.text 
+0

mm Tôi đã đơn giản hóa cây một chút và khi tôi thử nó nó dường như không hoạt động. Tag BlogPost ví dụ không đơn giản là '' nhưng '' và các giá trị cho Chủ sở hữu và Trạng thái thay đổi từ một mục này sang mục nhập khác. – mvime

+1

Các thuộc tính bổ sung sẽ không ảnh hưởng đến điều này; chỉ có cấu trúc cây quan trọng. Để nắm bắt tất cả các phần tử 'BlogPost', bạn cũng có thể sử dụng' cho bài đăng trong tree.xpath ('// BlogPost'): ... ' –

+1

Cảm ơn! Tôi chưa thể bỏ phiếu, nhưng bạn đã giúp tôi hiểu cách hoạt động của nó. Câu trả lời mà tôi hiểu tốt hơn và tôi đã nhận được để làm việc là Andrew mặc dù. – mvime

7

Đối với người tìm kiếm trong tương lai: Câu trả lời hàng đầu ở đây đề xuất xóa yếu tố trên mỗi lần lặp lại, nhưng điều đó vẫn khiến bạn trở nên ngày càng tăng thiết lập các phần tử trống sẽ từ từ tích tụ trong bộ nhớ:

for event, element in etree.iterparse(path_to_file, tag="BlogPost"): 
    for child in element: 
     print child.tag, child.text 
    element.clear() 

^Đây không phải là giải pháp có thể mở rộng, đặc biệt là tệp nguồn của bạn ngày càng lớn. Giải pháp tốt hơn là lấy nguyên tố gốc và xóa rằng mỗi khi bạn tải một bản ghi hoàn chỉnh. Điều này sẽ giữ cho bộ nhớ sử dụng khá ổn định (sub-20MB tôi sẽ nói).

Đây là giải pháp không yêu cầu tìm kiếm một thẻ cụ thể. Hàm này sẽ trả về một trình tạo ra tất cả các nút con số đầu tiên của các nút con số (ví dụ: <BlogPost>) bên dưới nút gốc (ví dụ: <Database>). Nó thực hiện điều này bằng cách ghi bắt đầu của thẻ đầu tiên sau nút gốc, sau đó chờ thẻ kết thúc tương ứng, cho ra toàn bộ phần tử và sau đó xóa nút gốc.

from lxml import etree 

xmlfile = '/path/to/xml/file.xml' 

def iterate_xml(xmlfile): 
    doc = etree.iterparse(xmlfile, events=('start', 'end')) 
    _, root = next(doc) 
    start_tag = None 
    for event, element in doc: 
     if event == 'start' and start_tag is None: 
      start_tag = element.tag 
     if event == 'end' and element.tag == start_tag: 
      yield element 
      start_tag = None 
      root.clear() 
Các vấn đề liên quan