Xpath原理:先将HTML文档转为XML文档,再用xpath查找HTML节点或元素
什么是xml?
1、xml指可扩展标记语言
2、xml是一种标记原因,类似于html
3、xml的设计宗旨是传输数据,而非显示数据
4、xml标签需要我们自己自定义
5、xml被设计为具有自我描述性
xml和html的区别?
1、xml被设计为传输和存储数据,其焦点是数据的内容
2、html是显示数据以及如何更好的显示数据
# xml文档示例
# 这里面的标签都是自定义的
<?xml version="1.0" encoding="utf-8"?>
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowing</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category="web">
<title lang="en">XQuery Kick Start</title>
<author>James McGovern</author>
<author>Per Bothner</author>
<author>Kurt Cagle</author>
<author>James Linn</author>
<year>2005</year>
<price>49.99</price>
</book>
<book category="web" cover="paperback">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
1、父(parent)
每个元素及属性都有一个父
下面xml例子中,book元是title,author,year,price元素的父
<?xml version="1.0" encoding="utf-8"?>
<book>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
2、子(children)
元素节点可能有零个,一个或者多个子
在下面的例子中title,author,year,price都是book元素的子
<?xml version="1.0" encoding="utf-8"?>
<book>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
3、同胞(sibling)
拥有相同的父的节点
在下面例子中title,author,year,price元素都是同胞
<?xml version="1.0" encoding="utf-8"?>
<book>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
4、先辈(ancestor)
某节点的父、父的父,等等
下面例子中,title元素的先辈是book和bookstore
<?xml version="1.0" encoding="utf-8"?>
<bookstore>
<book>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
</bookstore>
5、后代
某节点的子,子的子,等等
下面例子中,bookstore后代是book,title,author,year,price元素
<?xml version="1.0" encoding="utf-8"?>
<bookstore>
<book>
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
</bookstore>
什么是xpath
xpath(xml path language)是一门在xml文档中查找信息的语言,可以用来在xml文档对元素和属性进行遍历
xml path language:xml路径语言
选取节点
xpath使用路径表达式来选取xml文档中的节点或者节点集,这些路径表达式和我们在常规表达式和我们在常规的电脑文件系统里看到的表达式非常相似
下面列出了最常用的路径表达式:
表达式 描述
nodename 选取此节点的所有子节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置
. 选取当前节点
… 选取当前节点的父节点
@ 选取属性
在下面表格中,我们列出了一些路径表达式以及表达式的结果:
bookstore 选取bookstore元素的所有子节点
/bookstore 选取根元素bookstore。注释:假如路径起始于正斜杠(/)则此路径始终代表到某元素的绝对路径
bookstore/book 选取属于bookstore的子元素的所有book元素
//book 选取所有book子元素,而不管他们在文档中的位置
bookstore//book 选取属于bookstore元素的后代的所有book元素,而不管他们位于bookstore之下的什么位置
…@lang 选取名为lang的所有属性
选取位置节点
xpath通配符可用来选取未知的xml元素
通配符 描述
-
匹配任何元素的节点
@* 匹配任何属性的节点
node() 匹配任何类型的节点
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 结果
/bookstore/* 选取bookstore元素的所有子节点
//* 选取文档中的所有元素
html/node()/meta/@* 选取HTML下面任意节点下的meta节点的所有属性
//title[@*] 选取所有带有属性的title元素
选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果
路径表达式 结果
//book/title|//book/price 选取book元素的所有title和price元素
//title|//price 选取文档中的所有title和price元素
/bookstore/book/title|//price 选取属于bookstore元素的book元素的所有title元素,以及文档中的所有price元素
这些就是xpath的语法内容妙哉运用到python抓取是要先转换为xml
lxml库
1、lxml库是一个HTML/xml的解析器,主要功能是如何解析和提取HTML/xml数据
2、lxml和正则一样,都是通过c语言实现的,他是一款高性能的python html/xml的解析器,我们可以利用之前所学习的xpath语法,来快速定位特定元素以及节点信息
3、lxml python 的官方文档:http://lxml.de/index/html
4、需要安装c语言库 pip install lxml
初步使用
我们利用它来解析HTML代码,简单示例:
html = '''
<div class="Sq_leftNav_forum">
<ul class="Sq_lineBox Sq_leftNav_forumList">
<li><a href="/shuo/forum/00B002">找对象</a></li>
<li><a href="/shuo/forum/001002">新鲜事</a></li>
<li><a href="/shuo/forum/001004">同城互助</a></li>
<li><a href="/shuo/forum/007005">同城活动</a></li>
<li><a href="/shuo/forum/00D001">虞城有爱</a></li>
<li><a href="/shuo/forum/010004">二手闲置</a></li>
<li><a href="/shuo/forum/010001">找工作</a></li>
<li><a href="/shuo/forum/00B001">情感</a></li>
<li><a href="/shuo/forum/002001001">美食</a></li>
</ul>
</div>
'''
# 使用lxml的etree模块
from lxml import etree
# 利用etree.HTML()构造一个xpath解析对象(转为xml文档)
xml_doc = etree.HTML(html)
# print(xml_doc)
# etree.tostring()输出修正后的HTML代码
html_doc = etree.tostring(xml_doc)
# print(html_doc) # 自动补全了body,html标签
# print(type(html_doc)) # bytes类型
print(html_doc.decode('utf-8')) # 利用decode()方法将其转成str类型
print(type(html_doc.decode('utf-8')))
<html><body><div class="Sq_leftNav_forum">
<ul class="Sq_lineBox Sq_leftNav_forumList">
<li><a href="/shuo/forum/00B002">找对象</a></li>
<li><a href="/shuo/forum/001002">新鲜事</a></li>
<li><a href="/shuo/forum/001004">同城互助</a></li>
<li><a href="/shuo/forum/007005">同城活动</a></li>
<li><a href="/shuo/forum/00D001">虞城有爱</a></li>
<li><a href="/shuo/forum/010004">二手闲置</a></li>
<li><a href="/shuo/forum/010001">找工作</a></li>
<li><a href="/shuo/forum/00B001">情感</a></li>
<li><a href="/shuo/forum/002001001">美食</a></li>
</ul>
</div>
</body></html>
<class 'str'>
文件读取
除了直接读取字符串,lxml还支持从文件里读取内容,我们新建一个hello.html
from lxml import etree
# etree.parse()读取外部文件
html = etree.parse('./hello.html')
# print(type(html))
# print(html)
# 调用tostring()方法即可输出修正后的HTML代码
result = etree.tostring(html)
# print(result)
print(result.decode('utf-8'))
<div class="Sq_leftNav_forum">
<ul class="Sq_lineBox Sq_leftNav_forumList">
<li><a href="/shuo/forum/00B002">找对象</a></li>
<li><a href="/shuo/forum/001002">新鲜事</a></li>
<li><a href="/shuo/forum/001004">同城互助</a></li>
<li><a href="/shuo/forum/007005">同城活动</a></li>
<li><a href="/shuo/forum/00D001">虞城有爱</a></li>
<li><a href="/shuo/forum/010004">二手闲置</a></li>
<li><a href="/shuo/forum/010001">找工作</a></li>
<li><a href="/shuo/forum/00B001">情感</a></li>
<li><a href="/shuo/forum/002001001">美食</a></li>
</ul>
</div>
html = '''
<div>
<ul>
<li class="Sq_leftNav_forum1"><a href="/shuo/forum/00B002">找对象</a></li>
<li class="Sq_leftNav_forum2"><a href="/shuo/forum/001002">新鲜事</a></li>
<li class="Sq_leftNav_forum1"><a href="/shuo/forum/001004">同城互助</a></li>
<li class="Sq_leftNav_forum2"><a href="/shuo/forum/007005">同城活动</a></li>
<li class="Sq_leftNav_forum1"><a href="/shuo/forum/00D001">虞城有爱</a></li>
</ul>
</div>
'''
from lxml import etree
html_doc = etree.HTML(html) # xml
result = html_doc.xpath('//li')
# print(result)
for i in result:
r = etree.tostring(i).decode('utf-8')
print(r)
<li class="Sq_leftNav_forum1"><a href="/shuo/forum/00B002">找对象</a></li>
<li class="Sq_leftNav_forum2"><a href="/shuo/forum/001002">新鲜事</a></li>
<li class="Sq_leftNav_forum1"><a href="/shuo/forum/001004">同城互助</a></li>
<li class="Sq_leftNav_forum2"><a href="/shuo/forum/007005">同城活动</a></li>
<li class="Sq_leftNav_forum1"><a href="/shuo/forum/00D001">虞城有爱</a></li>
2、继续获取
- 标签的所有class属性
-
html_doc = etree.HTML(html) # xml result = html_doc.xpath('//li/@class') print(result)
['Sq_leftNav_forum1', 'Sq_leftNav_forum2', 'Sq_leftNav_forum1', 'Sq_leftNav_forum2', 'Sq_leftNav_forum1']
3、获取
- 标签下的标签里的所有href
-
html_doc = etree.HTML(html) # xml result = html_doc.xpath('//li/a/@href') print(result)
['/shuo/forum/00B002', '/shuo/forum/001002', '/shuo/forum/001004', '/shuo/forum/007005', '/shuo/forum/00D001']
4、继续获取
- 标签下href为/shuo/forum/00B002的标签的文本内容
-
html_doc = etree.HTML(html) # xml result = html_doc.xpath('//li/a[@href="/shuo/forum/00B002"]/text()') print(result)
['找对象']
注意:只要涉及到条件,加[]
只要获取属性值,加@
通过text()取内容