python之lxml快速上手_Element(二)

使用XPath查找文本(Using XPath to find text)

另一个在树状结构文档中抽取文本的方式是:XPath,它同样允许你抽取单独的文本块并放到list中。

>>> print(html.xpath("string()")) # lxml.etree only!
TEXTTAIL
>>> print(html.xpath("//text()")) # lxml.etree only!
['TEXT', 'TAIL']

如果你想重复利用上述代码,你可以把它封装到一个函数中:

# 注意,此处使用text(),而不是string()、concat(),后续有说明
>>> build_text_list = etree.XPath("//text()") # lxml.etree only!
>>> print(build_text_list(html))
['TEXT', 'TAIL']

注意,由XPath返回的文本结果是一个非常“智能”的对象,它知道自己的出身。你可以使用getparent()来了解它来自哪里(即获取它的父节点),就像你对Element对象所做的那样。

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

你还可以知道它到底是普通的文本内容,还是尾部文本(tail text):

>>> print(texts[0].is_text)
True
>>> print(texts[1].is_text)
False
>>> print(texts[1].is_tail)
True

既然text()函数返回的文本对象可以告诉你它的“归属”(即获取它的父节点),当你使用XPath函数string()、concat()时,它将不再告诉你它的“归属”。

>>> stringify = etree.XPath("string()")
>>> print(stringify(html))
TEXTTAIL
>>> print(stringify(html).getparent())
None

树的迭代(Tree iteration)

为了应对上面的问题,你可能需要递归遍历并使用对它拥有的节点做一些事情,而树的迭代恰好是一个很方便的解决方案。为实现这一目的,Elements提供了树迭代器。就像生成器一样,在有序的文档(document order)返回(yield)节点。

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

如果你仅仅对某一些标签感兴趣,你可以把它的标签名传递给iter()来为你过滤掉不需要的标签。从lxml 3.0开始,你也可以同时传递多个标签名:

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

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

默认情况下,迭代器会返回树的所有节点,包括ProcessingInstructions(处理指令),Comments(注释)、Entity instances(实体实例)。如果你想确认只有元素节点(Elements)返回,你可以把Element工厂函数作为tag关键字参数传递给iter():

>>> 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: &#234; - &#234;
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)
&#234;

注意,传递通配符“*”仍具会返回所有元素节点(仅包含Element)。

序列化(Serialisation)

序列化通常使用tostring()函数,它会返回一个字符串,或者你也可以使用ElementTree.write()方法将其写入到一个文件、类文件对象、一个URL。两种调用方式都接收同样的关键字参数,比如pretty_print(格式化输出),encoding(编码格式)可以指定除ASCII之外的输出编码格式,以及xml_declaration(是否加入XML声明):

>>> root = etree.XML('<root><a><b/></a></root>')

>>> etree.tostring(root)
b'<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>

注意,美化输出(指定pretty_print=True时)会在文件最末位加一空白行。
在lxml 2.0及之后的版本,序列化函数能做的不仅局限于XML序列化。你也可以将其序列化成HTML,或者抽取文本,只需要通过method关键字参数:

>>> root = etree.XML(
...    '<html><head/><body><p>Hello<br/>World</p></body></html>')

>>> etree.tostring(root) # default: method = 'xml'
b'<html><head/><body><p>Hello<br/>World</p></body></html>'

>>> etree.tostring(root, method='xml') # same as above
b'<html><head/><body><p>Hello<br/>World</p></body></html>'

>>> etree.tostring(root, method='html')
b'<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 = next(root.iter('br'))  # get first result of iteration
>>> 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'

在下面的例子中,序列化成一个python Unicode字符串,比字节串(byte strings)更加易于处理。只需指定encoding为unicode即可:

>>> etree.tostring(root, encoding='unicode', method='text')
u'HelloW\xf6rld'
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值