XPath与lxml库介绍及爬虫案例
XPath介绍
XPath(XML Path Language)是一门在XML文档中查找信息的语言,可用来在XML文档中对元素和属性进行遍历。
XPath的节点
XPath的节点有7种类型:文档节点,元素节点,属性节点,文本节点,命名空间节点,处理指令节点,注释节点。对于我们需要关注的是前面4个节点。下面看xml文档。
<?xml version="1.0" encoding="ISO-8859-1"?>
<bookstore>
<book>
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
<bookstore> # 文档节点
<book> # 元素节点,属于<bookstore>的子节点
<title>/<author>/<year>/<price> # 元素节点,属于<book>节点的子节点
<title lang="en">Harry Potter</title>
lang # 属性节点,是<title>节点的属性
Harry Potter # 文本节点,是<title>节点的文本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
XPath选取节点
XPath使用路径表达式在XML文档中选取节点。节点是通过沿着路径或者step来选取的。
路径表达式:
表达式 描述
nodename 选取此节点的所有节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
谓语
谓语用来查找某个特定的节点或者包含某个指定的值节点。
谓语被嵌在方括号[]中。
在下面表格中,列出带有谓语的一些路径表达式,以及结果:
路径表达式 结果
/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=’en’] 选取所有 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() 匹配任何类型的节点
选取若干路径
通过在路径表达式中使用”|”运算符,可以选取若干路径。
路径表达式 描述
//book/title | //book/price 选取 book 元素的所有 title 和 price 元素
//title | // price 选取文档中的所有 title 和 price 元素
/bookstore/book/title | //price 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素
XPath运算符
lxml类库介绍
lxml类库是一个Html/XML的解析器,主要功能是如何解析和提取HTML/XML数据。
lxml的安装
pip install lxml
1
lxml的简单使用
etree将文本转成html:
# 将文本转成html对象
html = etree.HTML(text)
# 将html对象转成html的文本信息
etree.tostring(html)
1
2
3
4
5
示例:
from lxml import etree
if __name__ == '__main__':
text = '''
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul>
</div>
'''
# 将文本转成html对象
html = etree.HTML(text)
# 将对象转成html文本
result = etree.tostring(html)
# 打印输出
print(result.decode('utf-8'))
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
输出结果:会自动添加,标签,补齐缺少的标签。
<html>
<body>
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a> </li>
</ul>
</div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
解析html转成文本
新建一个text.html文件,文件内容:
<html>
<body>
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
解析文本输出:
from lxml import etree
if __name__ == '__main__':
# 解析text.html文件
html = etree.parse('text.html')
# 将html对象转成str
result = etree.tostring(html)
# 输出
print(result.decode('utf-8'))
1
2
3
4
5
6
7
8
9
10
输出结果:
<html>
<body>
<div>
<ul>
<li class="item-0"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a></li>
</ul>
</div>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
XPath与lxml联合使用
获取所有\
标签
from lxml import etree
if __name__ == '__main__':
# 解析text.html文件,返回一个html对象
html = etree.parse('text.html')
print(type(html)) # 输出html对象类型 <class 'lxml.etree._ElementTree'>
# xpath解析语法解析,获取所有的<li>标签的内容
result = html.xpath('//li')
print(type(result)) # 输出结果的类型 <class 'list'>
for item in result:
print(type(item)) # 输出每个item对象的类型 <class 'lxml.etree._Element'>
print(etree.tostring(item).decode('utf-8')) # 输出<li>标签的文本内容 如:<li class="item-0"><a href="link1.html">first item</a></li>
1
2
3
4
5
6
7
8
9
10
11
12
13
输出结果:
获取\
标签的class=’item-1’属性
from lxml import etree
if __name__ == '__main__':
html = etree.parse('text.html')
result = html.xpath('//li[@class="item-1"]')
for item in result:
print(etree.tostring(item).decode('utf-8'))
1
2
3
4
5
6
7
8
9
输出结果:
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
1
2
3
获取\
标签下的href为link1.html的标签
if __name__ == '__main__':
html = etree.parse('text.html')
result = html.xpath('//li/a[@href="link1.html"]')
for item in result:
print(etree.tostring(item).decode('utf-8')) # 输出 匹配的信息 <a href="link1.html">first item</a>
print(item.text) # 输出标签的<a>文本信息 first item
1
2
3
4
5
6
7
输出结果:
<a href="link1.html">first item</a>
first item
1
2
XPath爬虫案例
#!/usr/bin/env python
# encoding: utf-8
"""
__author__: Widsom Zhang
__time__: 2017/11/13 18:32
"""
import json
import random
import urllib.request
from lxml import etree
def download_image(url, headers):
"""
下载图片
:param url: 图片的url
:param headers: http的请求头
:return:
"""
# 截取图片的url
lists = url.split('/')
# 拼接图片保存的地址路径
filename = 'image/' + lists[-1]
# 将请求到的数据写入文件
with open(filename, 'wb')as f:
f.write(get_response(url, headers))
def write_image_url(url):
"""
将图片的url写入文件
:param url:
:return:
"""
# 以拼接的方式写入
with open("image/imageurl.txt", 'a')as f:
# 每写入一个换行
f.write(url + "\n")
def get_response(url, headers):
"""
获取响应对象
:param url: 请求的url
:param headers: 请求头信息
:return: 返回服务器的响应信息
"""
req = urllib.request.Request(url, headers=headers)
resp = urllib.request.urlopen(req)
return resp.read()
def parse_image(result):
"""
解析html信息,获取image的url的策略
:param result: html信息
:return:
"""
# 通过etree库将html信息转成对象
html = etree.HTML(result)
# 通过xpath解析规则,获取需要的图片url信息
images = html.xpath('//li[@class="box"]/a/img/@src')
for image in images:
print(image)
# 下载图片
# download_image(image, headers) # 下载图片太慢,这里注释了
# 将图片的url写入文件
write_image_url(image)
if __name__ == '__main__':
"""
xpath爬虫示例:
爬取的网站是:http://tu.duowan.com/m/bxgif
使用fiddler软件抓包分析:
在浏览器中输入上面的url,加载到30条需要的数据,随着滚动条往下拖动,数据再次加载且浏览器的url没有变化
初步判断采用的是ajax框架加载数据,通过抓包工具找到加载的url。
ajax加载的url:
http://tu.duowan.com/m/bxgif?off ... =0.2097926790117744
url返回的json数据格式:
{
"html": "...",
"more": true,
"offset": 60,
"enabled": true
}
http://tu.duowan.com/m/bxgif?off ... =0.9387482944610999
{
"html": "...",
"more": true,
"offset": 90,
"enabled": true
}
注:html字段是html中的"<li>..."的html数据,可以使用lxml和xpath解析,具体看代码
通过查看html页面的源码,可以发现,offset是json数据返回的offset,order字段是固定的,math字段是一个(0,1)的随机数。
"""
# 需要爬取的url
url = 'http://tu.duowan.com/m/bxgif'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36'
}
# 将请求url的响应信息,通过xpath解析规则解析
parse_image(get_response(url, headers))
# 每次请求30条数据
offset = 30
more = True
# 循环遍历30次,获取需要的数据(为什么是30,因为该网站数据不多,也就1000多)
while more:
# 拼接url
url2 = 'http://tu.duowan.com/m/bxgif?offset=' + str(offset) + '&order=created&math=' + str(random.random())
print(url2)
result2 = get_response(url2, headers)
# 解析json数据
dict = json.loads(result2)
# 获取html的value值
result = dict['html']
# offset的值
offset = dict['offset']
print(type(offset))
print(str(offset))
# 获取more的value值
more = dict['more']
# 如果more为true,表示有更多
if more:
# 解析image的url
parse_image(result)
---------------------
【转载,仅作分享,侵删】
作者:张行之
来源:CSDN
原文:https://blog.csdn.net/qq_33689414/article/details/78553499
版权声明:本文为博主原创文章,转载请附上博文链接!
|
|