• Python之lxml


    作者:Shane
    出处:http://bluescorpio.cnblogs.com

     

    lxml takes all the pain out of XML.
    Stephan Richter

        lxml是Python语言里和XML以及HTML工作的功能最丰富和最容易使用的库。lxml是为libxml2和libxslt库的一个Python化的绑定。它与众不同的地方是它兼顾了这些库的速度和功能完整性,以及纯Python API的简洁性,大部分与熟知的ElementTree API兼容但比之更优越。

    安装lxml:

    要求:需要Python2.3或更后的版本

    使用easy_install工具,以超级用户或管理员的角色run下面的命令:

    easy_install lxml

    在windows下,最好指定版本号:easy_install lxml==2.2.6

    使用lxml进行开发

    lxml.etree指南

    通常使用lxml.etree的方式

    >>> from lxml import etree

     

    Element类,一个Element是ElementTree API的主要容器类,大部分的XML tree功能都是通过这个类来访问的。Elements可以非常容易地通过Element工厂方法来创建。

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

    元素的XML tag名字是通过tag属性来访问的

    >>> print root.tag # root

    Elements是在XML树状结构中组织的,为创建子元素并将它们加到父元素上,可以使用append()方法。

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

    我们还有更高效的方法:SubElement工厂方法,它使用和Element工厂方法相同的参数,不过额外需要父节点作第一个参数:

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

    可以使用tostring()方法来看得到的XML

    >>> print etree.tostring(root, pretty_print=True)
    <root>
    <child1/>
    <child2/>
    <child3/>
    </root>

    元素是列表

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

    可以使用insert()方法插入新的子节点:

    >>> root.insert(0, etree.Element("child0"))
    删除子节点:

    >>> root[0] = root[-1] # this moves the element!
    >>> for child in root:
    ... print(child.tag)
    child3
    child1
    child2

    如果想把一个元素拷贝到不同的地方,需要创建一个独立的deep copy。

    >>> from copy import deepcopy
    >>> element = etree.Element("neu")
    >>> element.append( deepcopy(root[1]) )
    >>> print(element[0].tag)
    child1
    >>> print([ c.tag for c in root ])
    [’child3’, ’child1’, ’child2’]

    getparent()返回父节点:
    >>> root is root[0].getparent() # lxml.etree only!
    True

    元素的兄弟或邻居节点是通过next和previous属性来访问的
    The siblings (or neighbours) of an element are accessed as next and previous elements:
    >>> root[0] is root[1].getprevious() # lxml.etree only!
    True
    >>> root[1] is root[0].getnext() # lxml.etree only!
    True

    带属性的元素

    XML元素支持属性,可以用Element工厂方法直接创建。

    >>> root = etree.Element("root", interesting="totally")
    >>> etree.tostring(root)
    b’<root interesting="totally"/>’

    可以使用set和get方法访问这些属性:

    >>> print root.get("interesting")
    totally
    >>> root.set("interesting", "somewhat")
    >>> print root.get("interesting")
    somewhat

    也可以使用attrib性质的字典接口

    >>> attributes = root.attrib
    >>> print(attributes["interesting"])
    somewhat
    >>> print(attributes.get("hello"))
    None
    >>> attributes["hello"] = "Guten Tag"
    >>> print(attributes.get("hello"))
    Guten Tag
    >>> print(root.get("hello"))
    Guten Tag

     

    元素可以包含文字:

    >>> root = etree.Element("root")
    >>> root.text = "TEXT"
    >>> print(root.text)
    TEXT
    >>> etree.tostring(root)
    ’<root>TEXT</root>’

    如果XML用在(X)HTML中,文本也可以在不同的元素中显示:
    <html><body>Hello<br/>World</body></html>
    元素有tail属性,它包含XML 树中元素直接跟的,直到下个元素的文本。

    >>> html = etree.Element("html")
    >>> body = etree.SubElement(html, "body")
    >>> body.text = "TEXT"
    >>> etree.tostring(html)
    b’<html><body>TEXT</body></html>’
    >>> br = etree.SubElement(body, "br")
    >>> etree.tostring(html)
    b’<html><body>TEXT<br/></body></html>’
    >>> br.tail = "TAIL"
    >>> etree.tostring(html)
    b’<html><body>TEXT<br/>TAIL</body></html>’

     

    使用XPath查找文本

    另一个抽取XML树的文本内容是XPath,
    >>> print(html.xpath("string()")) # lxml.etree only!
    TEXTTAIL
    >>> print(html.xpath("//text()")) # lxml.etree only!
    [’TEXT’, ’TAIL’]

    如果经常使用,可以包装成一个方法:

    >>> build_text_list = etree.XPath("//text()") # lxml.etree only!
    >>> print(build_text_list(html))
    [’TEXT’, ’TAIL’]

    也可以通过getparent方法得到父节点

    >>> texts = build_text_list(html)
    >>> print(texts[0])
    TEXT
    >>> parent = texts[0].getparent()
    >>> print(parent.tag)
    body
    >>> print(texts[1])
    TAIL
    >>> print(texts[1].getparent().tag)
    br
    You can also find out if it’s normal text content or tail text:
    >>> print(texts[0].is_text)
    True
    >>> print(texts[1].is_text)
    False
    >>> print(texts[1].is_tail)
    True

     

    树的迭代:

    Elements提供一个树的迭代器可以迭代访问树的元素。

    >>> root = etree.Element("root")
    >>> etree.SubElement(root, "child").text = "Child 1"
    >>> etree.SubElement(root, "child").text = "Child 2"
    >>> etree.SubElement(root, "another").text = "Child 3"
    >>> print(etree.tostring(root, pretty_print=True))
    <root>
    <child>Child 1</child>
    <child>Child 2</child>
    <another>Child 3</another>
    </root>

    >>> for element in root.iter():
    ... print("%s - %s" % (element.tag, element.text))
    root – None
    child - Child 1
    child - Child 2
    another - Child 3

    如果知道感兴趣的tag,可以把tag的名字传给iter方法,起到过滤作用。

    >>> for element in root.iter("child"):
    ... print("%s - %s" % (element.tag, element.text))
    child - Child 1
    child - Child 2

    默认情况下,迭代器得到一个树的所有节点,包括ProcessingInstructions, Comments and Entity的实例。如果想确认只有Elements对象返回,可以把Element factory作为参数传入。

    >>> root.append(etree.Entity("#234"))
    >>> root.append(etree.Comment("some comment"))
    >>> for element in root.iter():
    ... if isinstance(element.tag, basestring):
    ... print("%s - %s" % (element.tag, element.text))
    ... else:
    ... print("SPECIAL: %s - %s" % (element, element.text))
    root - None
    child - Child 1
    child - Child 2
    another - Child 3
    SPECIAL: ê - ê
    SPECIAL: <!--some comment--> - some comment


    >>> for element in root.iter(tag=etree.Element):
    ... print("%s - %s" % (element.tag, element.text))
    root - None
    child - Child 1
    child - Child 2
    another - Child 3
    >>> for element in root.iter(tag=etree.Entity):
    ... print(element.text)

     

    序列化:

    序列化通常使用tostring()方法来返回一个字符串,或者ElementTree.write()方法来写入一个文件,一个类文件的对象,或者一个URL(通过FTP的PUT或者HTTP的POST)。二者都使用相同的关键字参数比如pretty_print来格式化输出或者encoding来选择一个特定的输出编码而不是简单的ASCII。
    >>> root = etree.XML("<root><a><b/></a></root>")
    >>> etree.tostring(root)
    ’<root><a><b/></a></root>’

    >>> print etree.tostring(root, xml_declaration=True)
    <?xml version=’1.0’ encoding=’ASCII’?>
    <root><a><b/></a></root>

    >>> print etree.tostring(root, encoding="iso-8859-1")
    <?xml version=’1.0’ encoding=’iso-8859-1’?>
    <root><a><b/></a></root>

    >>> print etree.tostring(root, pretty_print=True)
    <root>
    <a>
    <b/>
    </a>
    </root>

    Note that pretty printing appends a newline at the end.

    注意pretty打印在末尾添加一个新行。

    从lxml2.0起,serialisation可以做的不止XML序列化,可以序列化到HTML或者通过传递函数关键字来提取文本内容。

    >>> root = etree.XML("<html><head/><body><p>Hello<br/>World</p></body></html>")
    >>> etree.tostring(root) # default: method = ’xml’
    ’<html><head/><body><p>Hello<br/>World</p></body></html>’
    >>> etree.tostring(root, method="xml") # same as above
    ’<html><head/><body><p>Hello<br/>World</p></body></html>’
    >>> etree.tostring(root, method="html")
    ’<html><head></head><body><p>Hello<br>World</p></body></html>’

    >>> print etree.tostring(root, method="html", pretty_print=True)
    <html>
    <head></head>
    <body><p>Hello<br>World</p></body>
    </html>

    >>> etree.tostring(root, method="text")
    b’HelloWorld’

    对XML序列化而言,默认的文本编码是ASCII

    >>> br = root.find(".//br")
    >>> br.tail = u"W/xf6rld"
    >>> etree.tostring(root, method="text") # doctest: +ELLIPSIS
    Traceback (most recent call last):
    ...
    UnicodeEncodeError: ’ascii’ codec can’t encode character u’/xf6’ ...
    >>>etree.tostring(root, method="text", encoding="UTF-8")
    b’HelloW/xc3/xb6rld’

    >>> etree.tostring(root, encoding=unicode, method="text")
    u’HelloW/xf6rld’

    ElementTree类:

    一个ElementTree主要是围绕在一个有根节点的树的文档包装类。它提供了很多方法来解析,序列化以及一般的文档处理。一个最大的区别是它作为一个整体文档来序列化。与之相对的是序列化成单个的元素。

    >>> tree = etree.parse(StringIO("""/
     <?xml version="1.0"?>
     <!DOCTYPE root SYSTEM "test" [ <!ENTITY tasty "eggs"> ]>
     <root>
     <a>&tasty;</a>
     </root>
     """))
    >>> print(tree.docinfo.doctype)
    <!DOCTYPE root SYSTEM "test">

    >>> # lxml 1.3.4 and later
    >>> print(etree.tostring(tree))
    <!DOCTYPE root SYSTEM "test" [
    <!ENTITY tasty "eggs">
    ]>
    <root>
    <a>eggs</a>
    </root>

    >>> # lxml 1.3.4 and later
    >>> print(etree.tostring(etree.ElementTree(tree.getroot())))
    <!DOCTYPE root SYSTEM "test" [
    <!ENTITY tasty "eggs">
    ]>
    <root>
    <a>eggs</a>
    </root>

    >>> # ElementTree and lxml <= 1.3.3
    >>> print(etree.tostring(tree.getroot()))
    <root>
    <a>eggs</a>
    </root>

    从字符串和文件中解析:

    fromstring()是解析字符串最容易的方法

    >>> some_xml_data = "<root>data</root>"
    >>> root = etree.fromstring(some_xml_data)
    >>> print root.tag
    root
    >>> etree.tostring(root)
    ’<root>data</root>’

    XML()方法和fromstring()方法类似,但它主要用来把XML文字写入源文件。
    >>> root = etree.XML("<root>data</root>")
    >>> print root.tag
    root
    >>> etree.tostring(root)
    ’<root>data</root>’

    parse()方法用来从文件或者类文件对象中解析
    >>> some_file_like = StringIO.StringIO("<root>data</root>")
    >>> tree = etree.parse(some_file_like)
    >>> etree.tostring(tree)
    ’<root>data</root>’

    注意parse()返回的是一个ElementTree对象,而不是字符串解析方法的Element对象。

    >>> root = tree.getroot()
    >>> print root.tag
    root
    >>> etree.tostring(root)
    ’<root>data</root>’

     

    解析器对象:lxml.etree在默认情况下使用带默认配置的标准解析器,如果想配置解析器,可以创建自己的实例。

    >>> parser = etree.XMLParser(remove_blank_text=True) # lxml.etree only!

    本例在解析的时候创建了一个移除tags之间的空的文本的解析器,这可以减少tree的大小以及避免不定的tail,如果你知道空白内容对你来说是没有任何意义的话。

    >>> root = etree.XML("<root> <a/> <b> </b> </root>", parser)
    >>> etree.tostring(root)
    b’<root><a/><b> </b></root>’
    >>> for element in root.iter("*"):
    ... if element.text is not None and not element.text.strip():
    ... element.text = None
    >>> etree.tostring(root)
    b’<root><a/><b/></root>’

    递增解析:

    lxml.etree提供了两种方法来实现递增的逐步的解析。一个方法是通过类文件对象,它重复调用read() 方法。
    >>> class DataSource:
    ... data = [ b"<roo", b"t><", b"a/", b"><", b"/root>" ]
    ... def read(self, requested_size):
    ... try:
    ... return self.data.pop(0)
    ... except IndexError:
    ... return b’’
    >>> tree = etree.parse(DataSource())
    >>> etree.tostring(tree)
    b’<root><a/></root>’

    第二个方法是通过feed解析器接口,由feed(data) 和 close() 方法提供

    >>> parser = etree.XMLParser()
    >>> parser.feed("<roo")
    >>> parser.feed("t><")
    >>> parser.feed("a/")
    >>> parser.feed("><")
    >>> parser.feed("/root>")

    >>> root = parser.close()
    >>> etree.tostring(root)
    ’<root><a/></root>’

    在调用close() 方法(或者当有exception发生的时候),可以通过调用feed() 方法重新使用parser:
    >>> parser.feed("<root/>")
    >>> root = parser.close()
    >>> etree.tostring(root)
    b’<root/>’

  • 相关阅读:
    SpringCloud------获取配置文件属性值
    SpringCloud------MyBatisPlus代码生成器的使用
    Hanoi问题 算法
    常见的时间复杂度按数量级排列
    Java 匿名类
    java内部类
    Java 接口和抽象类
    使用引用类型变量来访问所引用对象的属性和方法时,Java 虚拟机绑定规则
    Python import搜索的路径顺序
    php 多次导入文件导致 Cannot redeclare class
  • 原文地址:https://www.cnblogs.com/pangblog/p/3275738.html
Copyright © 2020-2023  润新知