Python(00):解析xml文件(sax、dom、ElementTree)和lxml
一、Python对XML的解析
常见的XML编程接口有DOM和SAX,这两种接口处理XML文件的方式不同,使用场合也不同。
python有三种方法解析XML:SAX,DOM和ElementTree
1、DOM(Document Object Model)
DOM的解析器在解析一个XML文档时,一次性读取整个文档,把文档中所有元素保存在内存中的一个树结构里,之后利用DOM提供的不同函数来读取该文档的内容和结构,也可以把修改过的内容写入XML文件。
由于DOM是将XML读取到内存,然后解析成一个树,如果要处理的XML文本比较大的话,就会很耗内存,所以DOM一般偏向于处理一些小的XML(如配置文件)比较快。
python中用xml.dom.minidom来解析xml文件。
from xml.dom.minidom import parse DOMTree = parse(r\'book.xml\') # minidom解析器打开xml文档并将其解析为内存中的一棵树 booklist = DOMTree.documentElement # 获取xml文档对象,就是拿到树的根 if booklist.hasAttribute(\'type\'): print(\'Root element is \', booklist.getAttribute(\'type\')) # 判断根节点booklist是否有type属性,有则获取并打印属性值 books = booklist.getElementsByTagName(\'book\') # 获取booklist对象中所有的book节点的list集合 print(\'book节点的个数为:\', len(books)) print(\'book节点的个数为:\', books.length) for book in books: print("*******************book*******************") if book.hasAttribute(\'category\'): print(\'category is \', book.getAttribute(\'category\')) # 根据节点名title/author/pageNumber得到这些节点的集合list title = book.getElementsByTagName(\'title\')[0] author = book.getElementsByTagName(\'author\')[0] pageNumber = book.getElementsByTagName(\'pageNumber\')[0] print(\'title is \', title.childNodes[0].data) print(\'author is \', author.childNodes[0].data) print(\'pageNumber is \', pageNumber.childNodes[0].data)
2、SAX(simple API for XML)
Python标准库中包含SAX解析器,SAX是用的是事件驱动模型,通过在解析XML过程中触发一个个的事件并调用用户定义的回调函数来处理XML文件。
解析的基本过程:
读到一个XML开始标签,就会开始一个事件,然后事件就会调用一系列的函数去处理一些事情,当读到一个结束标签时,就会触发另一个事件。所以,我们写XML文档如果有格式错误的话,解析就会出错。
这是一种流式处理,一边读一边解析,占用内存少。适用场景如下:
1、对大型文件进行处理;
2、只需要文件的部分内容,或者只需从文件中得到特定信息。
3、想建立自己的对象模型的时候。
from xml.sax import * class DengHandler(ContentHandler): def startDocument(self): print("----开始解析xml文档----") def endDocument(self): print("----xml文档解析完毕----") def startElement(self,name,attrs): if name == "author": print("名字:",attrs[\'name\']," 日期:",attrs["birth"]) parse("deng.xml",DengHandler())
3、ElementTree(元素树)
ElementTree就像一个轻量级的DOM,具有方便友好的API。代码可用性好,速度快,消耗内存少。
因DOM需要将XML数据映射到内存中的树,一是比较慢,二是比较耗内存;而SAX流式读取XML文件,比较快,占用内存少,但需要用户实现回调函数(handler),所以一般选用ElementTree(元素树)。
二、xml.etree.ElementTree解析XML
<?xml version="1.0"?> <data> <country name="Liechtenstein"> <rank updated="yes">2</rank> <year>2008</year> <gdppc>141100</gdppc> <neighbor name="Austria" direction="E"/> <neighbor name="Switzerland" direction="W"/> </country> <country name="Singapore"> <rank updated="yes">5</rank> <year>2011</year> <gdppc>59900</gdppc> <neighbor name="Malaysia" direction="N"/> </country> <country name="Panama"> <rank updated="yes">69</rank> <year>2011</year> <gdppc>13600</gdppc> <neighbor name="Costa Rica" direction="W"/> <neighbor name="Colombia" direction="E"/> </country> </data>
1、遍历和查找xml
xml协议在各个语言里的都是支持的,在python中可以用以下模块操作xml:
# print(root.iter(\'year\')) #全文搜索 # print(root.find(\'country\')) #在root的子节点找,只找一个 # print(root.findall(\'country\')) #在root的子节点找,找所有 import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() print(root.tag) # 遍历xml文档 for child in root: print(\'========>\', child.tag, child.attrib, child.attrib[\'name\']) for i in child: print(i.tag, i.attrib, i.text) # 只遍历year 节点 for node in root.iter(\'year\'): print(node.tag, node.text)
2、修改和删除节点
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml") root = tree.getroot() # 修改 for node in root.iter(\'year\'): new_year = int(node.text) + 1 node.text = str(new_year) node.set(\'updated\', \'yes\') node.set(\'version\', \'1.0\') tree.write(\'test.xml\') # 删除node for country in root.findall(\'country\'): rank = int(country.find(\'rank\').text) if rank > 50: root.remove(country) tree.write(\'output.xml\')
3、添加节点
# 在country内添加(append)节点year2 import xml.etree.ElementTree as ET tree = ET.parse("a.xml") root = tree.getroot() for country in root.findall(\'country\'): for year in country.findall(\'year\'): if int(year.text) > 2000: year2 = ET.Element(\'year2\') year2.text = \'新年\' year2.attrib = {\'update\': \'yes\'} country.append(year2) # 往country节点下添加子节点 tree.write(\'a.xml.swap\')
4、自己创建xml文档
import xml.etree.ElementTree as ET new_xml = ET.Element("namelist") name = ET.SubElement(new_xml, "name", attrib={"enrolled": "yes"}) age = ET.SubElement(name, "age", attrib={"checked": "no"}) sex = ET.SubElement(name, "sex") sex.text = \'33\' name2 = ET.SubElement(new_xml, "name", attrib={"enrolled": "no"}) age = ET.SubElement(name2, "age") age.text = \'19\' et = ET.ElementTree(new_xml) # 生成文档对象 et.write("test.xml", encoding="utf-8", xml_declaration=True) ET.dump(new_xml) # 打印生成的格式
三、lxml.etree解析XML(推荐)
lxml 是一种使用 Python 编写的库,可以迅速、灵活地处理 XML,支持 XPath。
lxml.etree和xml.etree.ElementTree两个的操作方式看起来差不多,但lxml要更好一些,使用更简洁。解析xml的时候,自动处理各种编码问题。而且它天生支持 XPath 1.0、XSLT 1.0、定制元素类。
不过,lxml不是Python自带的标准库。需要自己安装,如下方式安装:
$ pip install lxml
from lxml import etree with open(\'./books.xml\') as f: # print(f.read()) text = f.read() html = etree.HTML(text.encode()) # print(html) print(html.tag) print(html.xpath(\'//title\')) # 从根节点向下找任意层中title的节点 print(html.xpath(\'//book//title\')) print(html.xpath(\'//book[@id="bk102"]\')) print(html.xpath(\'//book[@id]\')) print(html.xpath(\'//@id\')) # 取回的是属性 print(html.xpath(\'//*[@id]\')) print(html.xpath(\'//bookstore/book[1]\')) print(html.xpath(\'//bookstore/book[1]/@id\')) # [\'bk101\'] print(html.xpath(\'//bookstore/book[last()]/@id\')) # last()为最后一个节点 print(html.xpath(\'//*[contains(local-name(), "store")]\')) # [<Element bookstore at 0x2ce5648>] # local-name()为当前标签名字 print(html.xpath(\'//bookstore/*\')) # 匹配根节点bookstore下的所有子节点,不递归; print(html.xpath(\'//*[@*]\')) # 匹配所有有属性的节点 print(html.xpath(\'//@*\')) # 匹配所有属性 print(html.xpath(\'//book/title|//book/price\')) # 匹配book节点下title标签或prices标签 print(html.xpath(\'//book[position()=2]/@id\')) # [\'bk102\'] print(html.xpath(\'//book[price > 40]/@id\')) print(html.xpath(\'//book[1]/text()\')) # 匹配第一个book节点下所有文本子节点 print(html.xpath(\'//book[1]//text()\')) # 匹配第一个book节点下所有文本节点 print(html.xpath(\'//*[contains(@class,"even")]\')) # 匹配属性class中包含even字符串的节点
可以使用 lxml 的 etree 库来进行爬取网站信息。
从豆瓣电影中提取“本周口碑榜”:
import requests from lxml import etree # lxml 是c语言的库,效率非常高 url = \'http://movie.douban.com\' headers = {\'User-agent\': "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) \ Chrome/55.0.2883.75 Safari/537.36"} response = requests.get(url, headers=headers) with response: if response.status_code == 200: text = response.text html = etree.HTML(text) print(html.tag) titles = html.xpath(\'//div[@class="billboard-bd"]//a/text()\') for title in titles: print(title) print("*********************")