开发者学堂课程【Python 爬虫实战:python 爬虫实战实现 XPath 和 lxml 】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/555/detail/7645
python 爬虫实战实现 XPath 和 lxml
内容简介:
一、HTML 解析
二、XPath**
三、测试 XML、XPath
一、HTML 解析
通过上面的库,都可以拿到 HTML 内容。
HTML 的内容返回给浏览器,浏览器就会解析它,并对它渲染。
HTML 超文本表示语言,设计的初衷就是为了超越普通文本,让文本表现力更强。
XML 扩展标记语言,不是为了代替 HTML,而是觉得 HTML 的设计中包含了过多的格式,承担了一部分数据之外的任务,所以才设计了 XML 只用来描述数据。
HTML 和 XML 都有结构,使用标记形成树型的嵌套结构。DOM( DocumentObjectModel )来解析这种嵌套树型结构,浏览器往往都提供了对DOM 操作的 API,可以用面向对象的方式来操作 DOM。
二、XPath***
http://www.w3school.com.cn/xpath/index.asp 中文教程
XPath是一门在XML文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。
工具
XMLQuirewin7 +需要.NET框架4.0-4.5。
测试 XML、XPath
(1)XPath 简介
XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。
在学习之前应该具备的知识:
在继续学习之前,应该对下面的知识有基本的了解:
HTML / XHTML
XML / XML 命名空间
(2)XPath 术语
节点(Node)
在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。
请看下面这个 XML 文档:
Harry Potter
J K. Rowling
2005
29.99
上面的 XML 文档中的节点例子:
(文档节点)
J K. Rowling (元素节点)
lang="en" (属性节点)
基本值(或称原子值,Atomic value)
基本值是无父或无子的节点。
基本值的例子:
J K. Rowling
"en"
项目( Item )
项目是基本值或者节点。
节点关系
父(Parent)
每个元素以及属性都有一个父。
在下面的例子中,book 元素是 title、author、year 以及 price 元素的父:
Harry Potter
J K. Rowling
2005
29.99
子(Children)
元素节点可有零个、一个或多个子。
在下面的例子中,title、author、year 以及 price 元素都是 book 元素的子:
Harry Potter
J K. Rowling
2005
29.99
同胞(Sibling)
拥有相同的父的节点
在下面的例子中,title、author、year 以及 price 元素都是同胞:
Harry Potter
J K. Rowling
2005
29.99
先辈(Ancestor)
某节点的父、父的父,等等。
在下面的例子中,title 元素的先辈是 book 元素和 bookstore 元素:
Harry Potter
J K. Rowling
2005
29.99
后代(Descendant)
某个节点的子,子的子,等等。
在下面的例子中,bookstore 的后代是 book、title、author、year 以及 price 元素:
Harry Potter
J K. Rowling
2005
29.99
XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。
XML 实例文档
我们将在下面的例子中使用这个 XML 文档。
Harry Potter
29.99
Learning XML
39.95
选取节点
XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者斯特普来选取的
下列为最有用的路径表达式:
表达式 |
描述 |
nodename |
选取此节点的所有子节点。 |
/ |
从根节点选取。 |
// |
从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
. |
选取当前节点。 |
.. |
选取当前节点的父节点 |
@ |
选取属性 |
实例
在下面的表格里,已给列出一些路径表达式以及表达式结果:
路径表达式 |
结果 |
bookstore |
选取bookstore元素的所有子节点 |
/bookstore |
选取根元素bookstore。 注释:假如路径起始于正斜杠(/),则此路径始终代表到某元素的绝对路径! |
bookstore/book |
选取属于bookstore的子元素的所有book元素 |
//book |
选取所有book子元素,而不管它们在文档中的位置 |
bookstore//book |
选择属于bookstore元素的后代的所有book元素,而不管它们位于bookstore之下的什么 位置。 |
//@lang |
选取名为lang的所有属性 |
谓语(Predicates)
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
实例
在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:
路径表达式 |
结果 |
/bookstore/book[1] |
选取属于bookstore子元素的第一个book元素。 |
/bookstore/book[last()] |
选取属于bookstore子元素的最后一个book元素。 |
/bookstore/book[last()*1] |
选取属于bookstore子元素的倒数第二个book元素 |
/bookstore/book[position()<3] |
选取最前面的两个属于 |
|
bookstore元素的子元素的book元素 |
//title[@lang] |
选取所有拥有名为lang的属性的title元素 |
//title[@lang=‘eng‘] |
选取所有title元素,且这些元素拥有值为eng的lang属性 |
/bookstore/book[price>35, 00] |
选取bookstore元素的所有book元素,且其中的price元素的值须大于35. 00 |
/bookstore/book[price>35.00]/title |
选取bookstore元素中的book元素的所有title元素,且其中的price元素的值须大于35.00 |
选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
通配符 |
描述 |
* |
匹配任何元素节点 |
@* |
匹配任何属性节点 |
node() |
匹配任何类型节点 |
实例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:路径表达式 |
结果 |
/bookstore/* |
选取bookstore元素的所有子元素 |
//* |
选取文档中的所有元素 |
//title[@*] |
选取所有带有属性的title元素 |
选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
实例
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
实例
在下面表格中,列出来一些路径表达式,以及表达式的结果:
路径表达式 |
结果 |
//book/title | //book/price |
选取book元素的所有title和price元素 |
//title | //price |
选取文档中的所有 title 和 price 元素。 |
/bookstore/book/title | //price |
选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |
三、测试 XML、XPath
Gambardella ,Matthew
XML Developer's Guide
Computer
44. 95
2000-10-01
< description >An in-depth look at creating applications
with XML.
Ralls, Kim
Midnight Rain
Fantasy
5. 95
2000-12-16
< description >A former architect battles corporate zombies,
an evil sorceress, and her own childhood to become queen
of the world.
Corets,Eva
Maeve Ascendant
Fantasy
5. 95
2000-11-17
< description >After the collapse of a nanotechnology
society in England, the young survivors lay the
foundation for a new society.
Corets, Eva
Oberon's Legacy
Fantasy
5. 95
2001-03-10
< description >In post- apocalypse England, the mysterious
agent known only as Oberon helps to create a new life
for the inhabitants of London. Sequel to Maeve
Ascendant.
将上述代码保存名为 books.xml 文件
通过 XMLQuire.exe 运行提取需要的东西
当鼠标点击代码所在行是,上述就会出现相对应的 XPath,这种是绝对路径
点击 XPath 后,会出现所需要的工具栏,
在工具栏中输入 /bookstore 会出现一下效果
与此同时,在原来的页面中会做出相应变化。
如果在工具栏中直接输入 book,该工具不会查找出来,这是由于他不是这段代码中的根节点,需要用到//,可以从根下开始寻找所有结点。
XPath 主要根据当前文档的特点来写对应 XPath 的表达式,还可以去找所有带有id的值,//@id 表示 id 是一个属性,可以通过这种方法或的所有的属性值搜索结果。
如果写成//book@id,这种写法是错误的,应该写成 //book[@id],若在后面加入“id=bk102” 变成 //book[@id=“bk102“]就会在查找栏中找到相应内容。
类似的,如果写成 //book[@*],则表示所有有book属性的都找出来。
通过以上操作可以快速找到所要的结点和结点对应的内容
/根结点
元素节点
Corets,Eva 元素节点,
id="bk104" 是属性节点,id 是元素节点 book 的属性
节点之间的嵌套形成父子 (parent、children) 关系。
具有同一个父节点的不同节点是兄弟 (sibling) 关系。
谓语( Predicates )
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
谓语就是查询的条件。
操作符或表达式 |
含义 |
/ |
从根节点开始找 |
// |
从当前节点开始任意层找 |
. |
当前节点 |
.. |
当前节点的父节点 |
@ |
选择属性 |
节点名 |
选取所有节点的父节点 |
* |
匹配任意元素节点 |
@* |
匹配任意属性节点 |
node() |
匹配任意类型的节点 |
text() |
匹配text类型节点 |
XPath 轴
轴可定义相对于当前节点的节点集。
轴名称 |
结果 |
ancestor |
选取当前节点的所有先辈(父、祖父等)。 |
ancestor-or-self |
选取当前节点的所有先辈(父、祖父等)以及当前节点本身。 |
attribute |
选取当前节点的所有属性。 |
child |
选取当前节点的所有子元素。 |
descendant |
选取当前节点的所有后代元素(子、孙等) |
descendant-or-self |
选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 |
following |
选取文档中当前节点的结束标签之后的所有节点。 |
namespace |
选取当前节点的所有命名空间节点。 |
parent |
选取当前节点的父节点 |
preceding |
选取文档中当前节点的开始标签之前的所有节点 |
preceding-sibling |
选取当前节点之前的所有同级节点。 |
self |
选取当前节点。 |
工具使用图:
注意:在表达式编写框中点回车键时,会发现东西不存在,再写其他的时候会发现执行不出来,需要用滚轮或光标来查看是否有多行,还有再点击debug模式后,开启之后需要再点击一下,否则表达式编写框不能够修改
在表达式编写框中输入 //bookstore//title|/bookstore//price
就会找见所有 price 和 title 所在的位置
左侧展开的就是树,在里面双击切换上下文节点,前面的小房子图标表示一篇文章的上下文
表达式编写框就相当于当前上下文节点,通过上下文的切换来选择所需内容,在编程过程中是可以控制上下节点,但在测试中想要选择上下节点是无法指定的,会选择这种方式来指定。
表示在当前节点下找到 price ,若觉得此种方式不便,也可以写. /price 来表示在当前节点下寻找。
XPATH 实例
以斜杠开始的称为绝对路径,表示从根开始。
不以斜杆开始的称为相对路径,一般都是依照当前节点来计算。当前节点在上下文环境中,当前节点很可能已经不是根节点了。
一般为了方便,往往 xml 如果层次很深,都会使用//来查找节点。
以下为主要路径表达式及含义:
title--- 选取当前节点下所有 title 子节点
/book---从根结点找子节点是 book 的,找不到
book/title---当前节点下所有子节点 book 下的 title 节点
//title---从根节点向下找任意层中 title 的节点
book/little---当前节点下所有 book 子节点下任意层次的 title 节点
//@name---任意层下含有 name 的属性,取回的是属性
//book[@id]---任意层下含有 name 属性的 book 节点
//book[@id="bk101"]---任意层下含有 name 属性且等于'bk101'的 book 节点
/bookstore/book[1]---根节点 bookstore 下第一个 book 节点,从1开始
/bookstore/book[1]/@id---根节点 bookstore 下第一个 book 节点的 id 属性
/bookstore/book[last()-1]---根节点 bookstore 下倒数第二个 book 节点,函数last0
/bookstore/*---匹配根节点 bookstore 的所有子节点,不递归
//*---匹配所有子孙节点
//*[@*]---匹配所有有属性的节点
//book[@*]---匹配所有有属性的 book 节点
//@*---匹配所有属性
//book/title]/price---匹配 book 下的 title 节点或者任意层下的 price
//book[position()=2] ---匹配 book 节点,取第二个
//book/title//price---匹配book下的title节点或者任意层下的price
//book[position()=2]---匹配 book 节点,取第二个
//book[position()
//book[price>40]---匹配 price 节点值大于40的 book 节点
//book[2]/node()---匹配位置为2的 book 节点下的所有类型的节点
//book[1]/text()---匹配第一个 book 节点下的所有文本子节点
//book[1]//text()---匹配第一个 book 节点下的所有文本节点
//*[local-name()="book]---匹配所有节点且不带限定名的节点名称为book的所有节点
//book/child::node()[local-name()='price'and text()<10]---所有 book 节点的子节点中名字叫做 price 的且其内容小于10的节点,等价于//book/price[text()<10]
lxmI
Ixml是 Python 下功能丰富的XML、HTML 解析库,性能非常好,是对 libxml2 和libxslt 的封装。最新版支持 Python 2.6+,python3 支持到3.6。
CentOS 编译安装需要
#yum install libxml2 -devel libxslt-devel
注意,不同平台不一样,参看 http://lxmI.de/installation.html
lmxl安装
$pip install lxml
from lxml import etree
#使用etree构建HTML
root=etree. Element('html')
print(type(root))
print(root. tag)
body=etree. Element('body')
root. append(body)
print(etree. tostring(root))
sub=etree. Sub Element(body,'child1')#增加子节点
print(type(sub))
sub=etree. Subblement (body,'child2') append(etree. Element('child21'))
print(etree, tostring(root, pretty _ print=True). decode()
可以在浏览器上搜索 lxml 的官网,网页上内容丰富,可以在上面自学。一般会在上面学习一些简单的内容,比如构建切面,解析数据之类。
首先如何下载 lmxl,点击右上角的菜单,打开目录栏,选择 Installing lxml 目录。
其中会要求 c 语言的某些库以及依赖,都在网页中详细描述。
会直接用 pip install lxml 来安装。
如果要指定版本需要直接在后面加版本名称:pip install lxml==3.4.2,还会有在Windows 和 Linux 下安装的介绍,所需要的库在 Linux 下安装是没有太大的问题,但在 Windows 下会有较大问题,在编译过程中C语言的库会出现缺失库,版本不对的问题,所以在 Windows 中去写代码,用 Linux 去测试,在 Windows 中测试是不作数的,因为 python 主要是在 Linux 下运行的,可以详细查看网页文档来学习。
CentOS中的libxml2 -devel libxslt-devel 相当于 C 语言中的头文件,需要自行安装头文件
输入并允许以下代码:
from lxml import etree
root=etree. Element('html')
body=etree. Element('body')
root. append(body)
print(etree. tostring(root))
运行结果如下:
=====
在上述代码基础上创建子节点:
from lxml import etree
root=etree. Element('html')
body=etree. Element('body')
root. append(body)
div1 = etree.SubElement(body,’div’)
div2 = etree.SubElement(body,’div’)
print(etree. tostring(root))//也可以加入preet_print打印出来。
还可以通过其他方式创建 HTML,比如 Beautiful Soup 结合 lxml 做一些处理
etree 还提供了2个有用的函数
etype.HTML(text)解析HTML文档,返回根节点
anode. xpath('xpath路径')对节点使用xpath语法
from lxml import etree
import requests
url='
https://movie.douban.com/'
ua=“Mozilla/5.8(Windows NT6.1) Applewebkit /537. 36(KH1ML, like Gecko) Chrome/ss.e. assa.7 Safari/537. 36"
with requests. get(url, headers={'User-agent':ua}) as response;
content=response. text#HTML内容
html=etree.HTML(content)#分析HTML,返回DOM根节点titles—html.xpath("//div[@class="billboard-bd"]//trtd/a/text()")#返回文本列表
for t in titles:#豆瓣电影之本周口碑榜
print(t)
从豆瓣电影中提取“本周口碑榜”
打开豆瓣页面右击鼠标选择并打开审查元素
现需要将审查元素中的东西提取出来,就要进行操作
按下图中 copy 出 XPath 内容,该东西不能使用,因为无法确定下回的元素与内容中的是否一致,所以不可以按下图方式提取。
在 js 中提到过要求 HTML 的 id 唯一,可以通过 id 找到
现在通过用 ChroPath 工具来找到 billboard ,需要对 div 有要求,还需要找到每一行
文字内容:
from lxml import etree
import requests
urls=['
https://movie.douban.com/']
session=requests. Session()
with session:
for url in urls:
response=session.get(url,headers={'User-agent's"Mozilla/5.0(Windows NT 6.1) Applewebkit /537. 36(KBTM.like
})
content = response . text
#print(content)
#xpath//div[@class='billboard-bd']//tr/td/a/text()]
html=etree.HTML(content)titles=html.xpath("//div[@class="billboard-bd']//tr/td/a/text()”)
for t in titles:
print(t)
运行后得到结果。
若想获得带有序号的排行榜,则要运行以下代码:
from lxml import etree
import requests
urls=['
https://movie.douban.com/']
session=requests. Session()
with session:
for url in urls:
response=session.get(url,headers={'User-agent's"Mozilla/5.0(Windows NT 6.1) Applewebkit /537. 36(KBTM.like
})
content = response . text
#print(content)
#xpath//div[@class='billboard-bd']//tr//text()]
html=etree.HTML(content)titles=html.xpath("//div[@class="billboard-bd']//tr")
for t in titles:
print(t)
print(’-‘*30)
同样,还可以运行以下代码:
from lxml import etree
import requests
urls=['
https://movie.douban.com/']
session=requests. Session()
with session:
for url in urls:
response=session.get(url,headers={'User-agent's"Mozilla/5.0(Windows NT 6.1) Applewebkit /537. 36(KBTM.like
})
content = response . text
#print(content)
#xpath//div[@class='billboard-bd']//tr]
html=etree.HTML(content)titles=html.xpath("//div[@class="billboard-bd']//tr//")
for t in titles:
txt=title.xpath(‘.//text()’)
print(‘’.join(map(lambda x:x.strip(),txt)))
print(‘-’*30)