2009-12-16 38 views
702

Tôi có nhiều hàng trong cơ sở dữ liệu chứa xml và tôi đang cố gắng viết một tập lệnh Python sẽ đi qua các hàng đó và đếm số lượng phiên bản của một thuộc tính nút cụ thể hiển thị. Ví dụ: cây của tôi trông giống như:Làm cách nào để phân tích cú pháp XML bằng Python?

<foo> 
    <bar> 
     <type foobar="1"/> 
     <type foobar="2"/> 
    </bar> 
</foo> 

Làm cách nào để truy cập thuộc tính 1 và 2 trong XML bằng Python?

+0

liên quan: [? Python xml ElementTree từ một nguồn chuỗi] (https://stackoverflow.com/q/647071/3357935) –

Trả lời

531

Tôi đề nghị ElementTree. Có các triển khai tương thích khác của cùng một API, chẳng hạn như lxmlcElementTree trong thư viện chuẩn của Python; nhưng, trong bối cảnh này, những gì họ chủ yếu thêm là tốc độ nhanh hơn - phần dễ lập trình phụ thuộc vào API, trong đó ElementTree định nghĩa.

Sau khi tạo phiên bản Element e từ XML, ví dụ: với XML chức năng, hoặc bằng cách phân tích một tập tin với một cái gì đó giống như

import xml.etree.ElementTree 
e = xml.etree.ElementTree.parse('thefile.xml').getroot() 

hoặc bất kỳ trong nhiều cách khác hiển thị ở ElementTree, bạn chỉ cần làm một cái gì đó như:

for atype in e.findall('type'): 
    print(atype.get('foobar')) 

và tương tự, thường khá đơn giản , các mẫu mã.

+28

Bạn dường như bỏ qua xml.etree.cElementTree mà đi kèm với Python và trong một số khía cạnh là nhanh hơn tham lxml ("lxml của iterparse() là hơi chậm hơn so với một trong cET" - e-mail từ lxml tác giả). –

+6

ElementTree hoạt động và được bao gồm với Python. Mặc dù có sự hỗ trợ XPath giới hạn và bạn không thể đi qua đến phần tử cha của một phần tử, nó có thể làm chậm sự phát triển (đặc biệt nếu bạn không biết điều này). Xem [python xml query get parent] (http://stackoverflow.com/questions/5373902/python-xml-query-get-parent) để biết chi tiết. – Samuel

+9

'lxml' tăng thêm tốc độ. Nó cung cấp quyền truy cập dễ dàng vào các thông tin như nút cha, số dòng trong nguồn XML, v.v. có thể rất hữu ích trong một số trường hợp. –

183

Bạn có thể sử dụng BeautifulSoup

from bs4 import BeautifulSoup 

x="""<foo> 
    <bar> 
     <type foobar="1"/> 
     <type foobar="2"/> 
    </bar> 
</foo>""" 

y=BeautifulSoup(x) 
>>> y.foo.bar.type["foobar"] 
u'1' 

>>> y.foo.bar.findAll("type") 
[<type foobar="1"></type>, <type foobar="2"></type>] 

>>> y.foo.bar.findAll("type")[0]["foobar"] 
u'1' 
>>> y.foo.bar.findAll("type")[1]["foobar"] 
u'2' 
+6

Trên thực tế, có 'BeautifulStoneSoup' trong BeautifulSoup cho XML – YOU

+0

Cảm ơn bạn đã biết thông tin @ibz, Yeah, Trên thực tế, Nếu nguồn không được định dạng tốt, sẽ rất khó phân tích cú pháp cho các trình phân tích cú pháp. – YOU

+36

ba năm sau đó với bs4 đây là một giải pháp tuyệt vời, rất linh hoạt, đặc biệt nếu nguồn không được định dạng tốt – cedbeu

16

Tôi vẫn là một người mới sử dụng Python, nhưng ấn tượng của tôi là ElementTree là tính năng tối tân trong phân tích và xử lý XML bằng Python.

Đánh dấu Pilgrim có a good section về Phân tích cú pháp XML với ElementTree trong cuốn sách Dive Into Python 3 của mình.

17

Python có giao diện cho trình phân tích cú pháp xml của người nước ngoài.

xml.parsers.expat 

Đây là trình phân tích cú pháp không hợp lệ, vì vậy xml xấu sẽ không bị phát hiện. Nhưng nếu bạn biết tập tin của bạn là chính xác, thì điều này là khá tốt, và bạn sẽ có thể nhận được thông tin chính xác mà bạn muốn và bạn có thể loại bỏ phần còn lại trên bay.

stringofxml = """<foo> 
    <bar> 
     <type arg="value" /> 
     <type arg="value" /> 
     <type arg="value" /> 
    </bar> 
    <bar> 
     <type arg="value" /> 
    </bar> 
</foo>""" 
count = 0 
def start(name, attr): 
    global count 
    if name == 'type': 
     count += 1 

p = expat.ParserCreate() 
p.StartElementHandler = start 
p.Parse(stringofxml) 

print count # prints 4 
+0

+1 bởi vì tôi đang tìm kiếm một trình phân tích cú pháp không hợp lệ sẽ làm việc với các ký tự nguồn khó đọc. Hy vọng rằng điều này sẽ cho tôi kết quả tôi muốn. –

+1

Ví dụ được thực hiện trong '09 và đây là cách thực hiện. –

6

tôi tìm ra Python xml.domxml.dom.minidom khá dễ dàng. Hãy nhớ rằng DOM không tốt cho số lượng lớn XML, nhưng nếu đầu vào của bạn là khá nhỏ thì điều này sẽ làm việc tốt.

346

minidom là nhanh nhất và khá thẳng về phía trước:

XML:

<data> 
    <items> 
     <item name="item1"></item> 
     <item name="item2"></item> 
     <item name="item3"></item> 
     <item name="item4"></item> 
    </items> 
</data> 

PYTHON:

from xml.dom import minidom 
xmldoc = minidom.parse('items.xml') 
itemlist = xmldoc.getElementsByTagName('item') 
print(len(itemlist)) 
print(itemlist[0].attributes['name'].value) 
for s in itemlist: 
    print(s.attributes['name'].value) 

OUTPUT

4 
item1 
item1 
item2 
item3 
item4 
+7

Làm thế nào để bạn nhận được giá trị của "item1"? Ví dụ: Value1 swmcdonnell

+73

Tôi đã tìm ra, trong trường hợp có ai đó có cùng một câu hỏi. Đó là s.childNodes [0] .nodeValue – swmcdonnell

+1

Tôi thích ví dụ của bạn, tôi muốn thực hiện nó nhưng tôi có thể tìm các chức năng minidom ở đâu. Trang web python minidom hút trong quan điểm của tôi. – Drewdin

35

lxml.objectify thực sự là đơn giản.

Chụp văn bản mẫu của bạn:

from lxml import objectify 
from collections import defaultdict 

count = defaultdict(int) 

root = objectify.fromstring(text) 

for item in root.bar.type: 
    count[item.attrib.get("foobar")] += 1 

print dict(count) 

Output:

{'1': 1, '2': 1} 
+0

Làm cách nào để thực hiện trong mã? – Clayton

+0

'count' lưu trữ số lượng của mỗi mục trong từ điển bằng các khóa mặc định, do đó bạn không phải kiểm tra tư cách thành viên. Bạn cũng có thể thử xem 'collections.Counter'. –

8

Dưới đây là một mã rất đơn giản nhưng hiệu quả sử dụng cElementTree.

try: 
    import cElementTree as ET 
except ImportError: 
    try: 
    # Python 2.5 need to import a different module 
    import xml.etree.cElementTree as ET 
    except ImportError: 
    exit_err("Failed to import cElementTree from any known place")  

def find_in_tree(tree, node): 
    found = tree.find(node) 
    if found == None: 
     print "No %s in file" % node 
     found = [] 
    return found 

# Parse a xml file (specify the path) 
def_file = "xml_file_name.xml" 
try: 
    dom = ET.parse(open(def_file, "r")) 
    root = dom.getroot() 
except: 
    exit_err("Unable to open and parse input definition file: " + def_file) 

# Parse to find the child nodes list of node 'myNode' 
fwdefs = find_in_tree(root,"myNode") 

Nguồn:

http://www.snip2code.com/Snippet/991/python-xml-parse?fromPage=1

70

Có nhiều lựa chọn ngoài kia. cElementTree trông tuyệt vời nếu tốc độ và mức sử dụng bộ nhớ là một vấn đề. Nó có rất ít chi phí so với chỉ đơn giản là đọc trong tập tin sử dụng readlines.

Các số liệu liên quan có thể được tìm thấy trong bảng dưới đây, sao chép từ cElementTree website:

library       time space 
xml.dom.minidom (Python 2.1) 6.3 s 80000K 
gnosis.objectify    2.0 s 22000k 
xml.dom.minidom (Python 2.4) 1.4 s 53000k 
ElementTree 1.2     1.6 s 14500k 
ElementTree 1.2.4/1.3   1.1 s 14500k 
cDomlette (C extension)   0.540 s 20500k 
PyRXPU (C extension)   0.175 s 10850k 
libxml2 (C extension)   0.098 s 16000k 
readlines (read as utf-8)  0.093 s 8850k 
cElementTree (C extension) --> 0.047 s 4900K <-- 
readlines (read as ascii)  0.032 s 5050k 

Như đã chỉ ra bởi @jfs, cElementTree đi kèm với Python:

  • Python 2: from xml.etree import cElementTree as ElementTree .
  • Python 3: from xml.etree import ElementTree (phiên bản C tăng tốc được sử dụng tự động).
+8

Có bất kỳ nhược điểm nào khi sử dụng cElementTree không? Nó có vẻ là không có trí tuệ. – mayhewsw

+6

Dường như họ không muốn sử dụng thư viện trên OS X vì tôi đã dành hơn 15 phút để tìm ra nơi để tải xuống và không có liên kết nào hoạt động. Thiếu tài liệu ngăn cản các dự án tốt phát triển, mong muốn nhiều người hơn sẽ nhận ra điều đó. – Stunner

+8

@Stunner: đó là trong stdlib tức là, bạn không cần phải tải về bất cứ điều gì. Trên Python 2: 'từ xml.etree nhập cElementTree thành ElementTree'. Trên Python 3: 'từ xml.etree nhập khẩu ElementTree' (phiên bản C tăng tốc được sử dụng tự động) – jfs

27

Tôi đề xuất xmltodict để đơn giản.

Nó phân tích cú pháp xml của bạn thành OrderedDict;

>>> e = '<foo> 
      <bar> 
       <type foobar="1"/> 
       <type foobar="2"/> 
      </bar> 
     </foo> ' 

>>> import xmltodict 
>>> result = xmltodict.parse(e) 
>>> result 

OrderedDict([(u'foo', OrderedDict([(u'bar', OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])]))]))]) 

>>> result['foo'] 

OrderedDict([(u'bar', OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])]))]) 

>>> result['foo']['bar'] 

OrderedDict([(u'type', [OrderedDict([(u'@foobar', u'1')]), OrderedDict([(u'@foobar', u'2')])])]) 
+2

Đồng ý. Nếu bạn không cần XPath hoặc bất cứ điều gì phức tạp, điều này đơn giản hơn nhiều để sử dụng (đặc biệt là trong trình thông dịch); tiện dụng cho các API REST xuất bản XML thay vì JSON –

-3

rec.xml: -

<?xml version="1.0"?> 
<nodes> 
    <node name="Car" child="Engine"></node> 
    <node name="Engine" child="Piston"></node> 
    <node name="Engine" child="Carb"></node> 
    <node name="Car" child="Wheel"></node> 
    <node name="Wheel" child="Hubcaps"></node> 
    <node name="Truck" child="Engine"></node> 
    <node name="Truck" child="Loading Bin"></node> 
    <node name="Piston" child="Loa"></node> 
    <node name="Spare Wheel" child=""></node> 
</nodes> 

par.py:-

import xml.etree.ElementTree as ET 
tree = ET.parse('rec.xml') 
root = tree.getroot() 

for nodes in root.findall('node'): 
    parent = nodes.attrib.get('name') 
    child = nodes.attrib.get('child') 
    print parent,child 
+2

điều này không thêm bất kỳ thứ gì không được đưa ra trong các câu trả lời trước đó. mô hình xml là khủng khiếp, nó không có ý nghĩa gì để lưu trữ một phần (nút) nhiều lần chỉ để liệt kê các con của nó. –

3
import xml.etree.ElementTree as ET 
data = '''<foo> 
      <bar> 
       <type foobar="1"/> 
       <type foobar="2"/> 
      </bar> 
     </foo>''' 
tree = ET.fromstring(data) 
lst = tree.findall('bar/type') 
for item in lst: 
    print item.get('foobar') 

này sẽ in giá trị của thuộc tính foobar.

7

Chỉ cần thêm một khả năng khác, bạn có thể sử dụng untangle vì đây là thư viện đối tượng xml-tới-python đơn giản.Ở đây bạn có một ví dụ:

Lắp đặt

pip install untangle 

Cách sử dụng

file xml của bạn (một chút thay đổi):

<foo> 
    <bar name="bar_name"> 
     <type foobar="1"/> 
    </bar> 
</foo> 

truy cập vào các thuộc tính với Gỡ rối:

import untangle 

obj = untangle.parse('/path_to_xml_file/file.xml') 

print obj.foo.bar['name'] 
print obj.foo.bar.type['foobar'] 

đầu ra sẽ là:

bar_name 
1 

Thông tin thêm về gỡ rối có thể được tìm thấy here.
Ngoài ra (nếu bạn tò mò), bạn có thể tìm thấy danh sách các công cụ để làm việc với XML và Python here (bạn cũng sẽ thấy rằng các công cụ phổ biến nhất được đề cập trong các câu trả lời trước).

2

Bạn có nghiêm túc không?

Còn lo ngại về bảo mật thì sao? Sử dụng defusedxml.

Điều này cũng được đề xuất bởi Two Scoops of Django.

So sánh về defusedxml vs các thư viện khác

lxml được bảo vệ chống lại các cuộc tấn công tỷ cười và không làm tra cứu mạng theo mặc định.

libxml2 và lxml không trực tiếp dễ bị giải nén gzip bom nhưng chúng cũng không bảo vệ bạn chống lại chúng.

xml.etree không mở rộng đối tượng và tăng ParserError khi thực thể xảy ra.

minidom không mở rộng các thực thể và chỉ trả lại nguyên văn thực thể chưa được mở rộng .

genshi.input của genshi 0.6 không hỗ trợ mở rộng đối tượng và tăng một ParserError khi một thực thể xảy ra.

Thư viện có (giới hạn) Hỗ trợ XInclude nhưng yêu cầu một bước bổ sung để xử lý việc đưa vào.

4

Tôi có thể đề xuất declxml.

Tiết lộ đầy đủ: Tôi đã viết thư viện này vì tôi đang tìm cách chuyển đổi giữa cấu trúc dữ liệu XML và Python mà không cần phải viết hàng tá dòng phân tích cú pháp/tuần tự hóa với ElementTree.

Với declxml, bạn sử dụng bộ xử lý để xác định rõ cấu trúc của tài liệu XML và cách ánh xạ giữa cấu trúc dữ liệu XML và Python. Bộ vi xử lý được sử dụng để cho cả serialization và phân tích cú pháp cũng như cho một mức độ xác nhận cơ bản.

Phân tích thành những cấu trúc dữ liệu Python là đơn giản:

import declxml as xml 

xml_string = """ 
<foo> 
    <bar> 
     <type foobar="1"/> 
     <type foobar="2"/> 
    </bar> 
</foo> 
""" 

processor = xml.dictionary('foo', [ 
    xml.dictionary('bar', [ 
     xml.array(xml.integer('type', attribute='foobar')) 
    ]) 
]) 

xml.parse_from_string(processor, xml_string) 

nào tạo ra kết quả:

{'bar': {'foobar': [1, 2]}} 

Bạn cũng có thể sử dụng bộ vi xử lý tương tự để serialize dữ liệu XML

data = {'bar': { 
    'foobar': [7, 3, 21, 16, 11] 
}} 

xml.serialize_to_string(processor, data, indent=' ') 

Sản xuất sản phẩm sau đây

<?xml version="1.0" ?> 
<foo> 
    <bar> 
     <type foobar="7"/> 
     <type foobar="3"/> 
     <type foobar="21"/> 
     <type foobar="16"/> 
     <type foobar="11"/> 
    </bar> 
</foo> 

Nếu bạn muốn làm việc với các đối tượng thay vì từ điển, bạn cũng có thể xác định bộ xử lý để chuyển đổi dữ liệu đến và từ các đối tượng.

import declxml as xml 

class Bar: 

    def __init__(self): 
     self.foobars = [] 

    def __repr__(self): 
     return 'Bar(foobars={})'.format(self.foobars) 


xml_string = """ 
<foo> 
    <bar> 
     <type foobar="1"/> 
     <type foobar="2"/> 
    </bar> 
</foo> 
""" 

processor = xml.dictionary('foo', [ 
    xml.user_object('bar', Bar, [ 
     xml.array(xml.integer('type', attribute='foobar'), alias='foobars') 
    ]) 
]) 

xml.parse_from_string(processor, xml_string) 

nào xuất ra như sau

{'bar': Bar(foobars=[1, 2])} 
Các vấn đề liên quan