2012-05-26 42 views
6

Nói rằng tôi có đầu vào còn lại như sau:Extract danh sách lĩnh vực từ reStructuredText

{"foo": "bar"} 

Tôi cố gắng để sử dụng này:

Some text ... 

:foo: bar 

Some text ... 

Những gì tôi muốn kết thúc với một dict như thế này là:

tree = docutils.core.publish_parts(text) 

Nó phân tích danh sách trường, nhưng tôi kết thúc với một số giả XML trong tree["whole"]?:

<document source="<string>"> 
    <docinfo> 
     <field> 
      <field_name> 
       foo 
      <field_body> 
       <paragraph> 
        bar 

Vì số tree dict không chứa bất kỳ thông tin hữu ích nào khác và đó chỉ là một chuỗi, tôi không chắc chắn cách phân tích danh sách trường ra khỏi tài liệu reST. Tôi sẽ làm như thế nào?

Trả lời

7

Bạn có thể thử sử dụng một cái gì đó giống như mã sau đây. Thay vì sử dụng phương thức publish_parts tôi đã sử dụng publish_doctree, để có được biểu diễn giả XML của tài liệu của bạn. Sau đó tôi đã chuyển đổi thành một DOM XML để trích xuất tất cả các phần tử field. Sau đó, tôi nhận được các thành phần field_namefield_body đầu tiên của mỗi phần tử field.

from docutils.core import publish_doctree 

source = """Some text ... 

:foo: bar 

Some text ... 
""" 

# Parse reStructuredText input, returning the Docutils doctree as 
# an `xml.dom.minidom.Document` instance. 
doctree = publish_doctree(source).asdom() 

# Get all field lists in the document. 
fields = doctree.getElementsByTagName('field') 

d = {} 

for field in fields: 
    # I am assuming that `getElementsByTagName` only returns one element. 
    field_name = field.getElementsByTagName('field_name')[0] 
    field_body = field.getElementsByTagName('field_body')[0] 

    d[field_name.firstChild.nodeValue] = \ 
     " ".join(c.firstChild.nodeValue for c in field_body.childNodes) 

print d # Prints {u'foo': u'bar'} 

Module xml.dom không phải là đơn giản nhất để làm việc với (tại sao tôi cần phải sử dụng .firstChild.nodeValue thay vì chỉ .nodeValue chẳng hạn), vì vậy bạn có thể muốn sử dụng các mô-đun xml.etree.ElementTree, mà tôi tìm thấy dễ dàng hơn nhiều để làm việc với. Nếu bạn sử dụng lxml, bạn cũng có thể sử dụng ký hiệu XPATH để tìm tất cả các thành phần field, field_namefield_body.

+0

Cảm ơn, trông giống như những gì tôi đang tìm kiếm! –

0

Tôi có một giải pháp thay thế mà tôi thấy là ít gánh nặng hơn, nhưng có thể giòn hơn. Sau khi xem xét việc thực hiện lớp nút https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/docutils/nodes.py, bạn sẽ thấy rằng nó hỗ trợ phương thức đi bộ có thể được sử dụng để lấy ra dữ liệu mong muốn mà không phải tạo hai biểu diễn xml khác nhau của dữ liệu của bạn. Dưới đây là những gì tôi đang sử dụng hiện nay, trong mã tạo các mẫu prototype của tôi:

https://github.com/h4ck3rm1k3/gcc-introspector/blob/master/peewee_adaptor.py#L33

và sau đó

def walk_docstring(prop): 
    doc = prop.__doc__ 
    doctree = publish_doctree(doc) 
    class Walker: 
     def __init__(self, doc): 
      self.document = doc 
      self.fields = {} 
     def dispatch_visit(self,x): 
      if isinstance(x, docutils.nodes.field): 
       field_name = x.children[0].rawsource 
       field_value = x.children[1].rawsource 
       self.fields[field_name]=field_value 
    w = Walker(doctree) 
    doctree.walk(w) 
    # the collected fields I wanted 
    pprint.pprint(w.fields) 
0

Dưới đây là thực hiện ElementTree tôi:

from docutils.core import publish_doctree 
from xml.etree.ElementTree import fromstring 

source = """Some text ... 

:foo: bar 

Some text ... 
""" 


def gen_fields(source): 
    dom = publish_doctree(source).asdom() 
    tree = fromstring(dom.toxml()) 

    for field in tree.iter(tag='field'): 
     name = next(field.iter(tag='field_name')) 
     body = next(field.iter(tag='field_body')) 
     yield {name.text: ''.join(body.itertext())} 

Cách sử dụng

>>> next(gen_fields(source)) 
{'foo': 'bar'} 
Các vấn đề liên quan