爬虫数据筛选——XPath
在使用爬虫爬取的数据可以分为两种,非结构化数据:数据的内容没有固定的格式和规范,如用户名、邮箱、账号、电话号码、地址、电影名称、评分、评论、商品名称等等,对此类数据的筛选我们一般使用正则表达式,效率较高且非常精准,而对于一些有特定规范的数据如HTML网页文档、XML网页文档、JSON等等,由于数据本身存在一定的规律性,可以通过针对这些规律的分析工具进行数据的提取:**正则表达式、Xpath、BeautifulSoup4、select、css等等,本篇我们主要学习XPath,做个笔记,方便以后学习吧~
Xpath原本是在可扩展标记语言XML中进行数据查询的一种描述语言,可以很方便的在XML文档中查询到具体的数据;后续再发展过程中,对于标记语言都有非常友好的支持,如超文本标记语言HTML。
一、认识Xpath
1.Xpath应用环境的搭建——lxml的安装
正如在python中有一个内置的re模块用来支持正则表达式语法一样,python中有一个第三方的lxml模块,可以方便的支持Xpath的各种操作,可以友好的解析Xpath语法,使其用于在程序中进行结构化数据筛选。
安装命令如下:
pip install lxml
python2 -m pip install lxml
pip2 install lxml
2.在操作Xpath之前,首先需要了解一些基础的技术术语
* 根标签:在标记语言中,处在最外层的一个标签就是根标签,根标签有且仅有一个,在上述代码中\<html\>就是跟标签
* 父标签:和子标签对应,内部包含了其他元素数据,该标签就是内部标签的父标签,如\<html\>是\<head\>的父标签,\<head\>又是\<title\>的父标签,某些说法中,父标签的父标签..被称为上级标签或则先代标签或者先辈标签
* 子标签:和父标签对应,被包含的元素,就是外部元素的子标签,如\<head\>是\<html\>的子标签,\<title\>标签是\<head\>的子标签,\<tr\>是\<table\>的子标签;同样的子标签的子标签,也被称为后代标签
* 兄弟标签:两个或者多个处在相同级别的标签,有相同的父标签,如\<h1\>和\<table\>是兄弟标签,\<head\>和\<body\>是兄弟标签,\<table\>中的两个\<tr\>是兄弟标签等等
* Xpath描述语言的常见语法
和正则表达式相比较,Xpath使用最简单的语法操作完成数据的查询匹配操作
表达式 |描述
-|-
nodename| 选取此节点的所有子节点。
/| 从根节点选取。
//| 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
.| 选取当前节点。
..| 选取当前节点的父节点。
@| 选取属性。
*| 匹配任何元素节点。
@*| 匹配任何属性节点。
node()| 匹配任何类型的节点。
通过如下的方式直接操作上面的文档
路径表达式 | 结果
-|-
html| 选取 html 元素的所有子节点。
/html| 选取根元素 html。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
table/tr/td| 选取属于 table 的子元素的所有 td 元素。
//div \| //table|选取所有的div或者table节点
//table|选取所有 table 子元素,而不管它们在文档中的位置。
html//div|选择属于html元素的后代的所有div元素,而不管它们位于 html之下的什么位置。
//@href |选取名为href 的所有属性。
标签条件筛选查询匹配
路径表达式 | 结果
-|-
//table/tr[1] | 选取属于table子元素的第一个 tr 元素。
//table/tr[last()] | 选取属于 table 子元素的最后一个 tr 元素。
//table/tr[last()-1] | 选取属于 table 子元素的倒数第二个 tr 元素。
//table/tr[position()<3] | 选取最前面的两个属于 table 元素的子元素的tr元素。
//td[@width] | 选取所有拥有名为 width 的属性的 td 元素。
//td[@width='100'] | 选取所有 td 元素,且这些元素拥有属性width并且值为100。
//tr//td[span>10000] | 选取tr元素的所有td子元素,并且其中的span 元素的值须大于10000。
同样,Xpath支持数据运算操作
运算符| 描述| 实例| 返回值
-|-|-|-
+ |加法 |6 + 4| 10
- |减法 6 - 4| 2
* |乘法 6 * 4| 24
div |除法 8 div 4| 2
= |等于| price=9.80 |如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 false。
!=|不等于|price!=9.80 |如果 price 是 9.90,则返回 true。如果 price 是9.80,则返回 false。
< |小于| price<9.80 |如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
<= |小于或等于 |price<=9.80 |如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 false。
> |大于| price>9.80 |如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 false。
>= |大于或等于| price>=9.80 |如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 false。
or |或| price=9.80 or price=9.70 |如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 false。
and |与 |price>9.00 and price<9.90 |如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 false。
mod |计算除法的余数 |5 mod 2 |1
xpath在浏览器中进行测试时,可以给谷歌浏览器安装一个插件Xpath Helper插件;就可以直接在浏览器中通过xpath语法来完成对数据的匹配测试
测试通过的xpath语法,就可以直接在程序中使用了!
二、python操作Xpath
python第三方模块lxml可以对Xpath有友好的支持,lxml是C实现的一种高性能python用于HTML/XML的解析模块,可以通过Xpath语法在html文档数据中进行指定表达式数据的索引查询* 简单etree操作
```
# -*- coding:utf-8 -*-
from lxml import etree
# 模拟得到爬虫数据
content = """
<html>
<head>
<title>大牧</title>
</head>
<body>
<h1>个人简介</h1>
<div>
<p>姓名:某某某</p>
<p>住址:中国 乡下</p>
<p>座右铭:岂能尽如人意,但求无愧于心</p>
</div>
</body>
</html>
"""
# 转换成html数据
# html = etree.parse("index.html")# 从文件中直接加载html数据
html = etree.HTML(content)# 通过etree.HTML()函数将字符串转换成HTML文档对象
print dir(html)# 查看文档对象的所有函数
print html.getchildren()# 查看文档对象根节点的所有子节点
# 转换成字符数据
str_html = etree.tostring(html)# 将HTML文档对象转换成字符串
print type(str_html)# 查看输出类型
print str_html# 查看输出数据
* xpath操作
```
# -*- coding:utf-8 -*-
from lxml import etree
# 模拟得到爬虫数据
content = u"""
<html>
<head>
<title>大牧</title>
</head>
<body>
<h1 name="title">个人简介</h1>
<div name="desc">
<p name="name">姓名:<span>某某某</span></p>
<p name="addr">住址:中国 乡下</p>
<p name="info">座右铭:岂能尽如人意,但求无愧于心</p>
</div>
</body>
</html>
"""
# 将爬取到的数据转换成HTML文档
html = etree.HTML(content)
# 查询所有的p标签
p_x = html.xpath("//p")
print(p_x)
# 查询所有Name属性的值
v_attr_name= html.xpath("//@name")
print(v_attr_name)
# 查询所有包含name属性的标签
e_attr_name = html.xpath("//*[@name]")
print(e_attr_name)
# 查询所有包含name属性,并且name属性值为desc的标签
e_v_attr_name = html.xpath("//*[@name='desc']")
print(e_v_attr_name)
# 查询所有p标签的文本内容,不包含子标签
p_t = html.xpath("//p")
for p in p_t:
print (p.text)
# 查询多个p标签下的所有文本内容,包含子标签中的文本内容
p_m_t = html.xpath("//p")
for p2 in p_m_t:
print(p2.xpath("string(.)"))
```
案例操作:爬虫智联招聘中前10页的某个工作岗位名称、薪水、公司信息
# coding:utf-8 ''' 使用xpath爬取智联招聘职位信息 ''' import requests from lxml import etree # 访问路由 url='http://sou.zhaopin.com/jobs/searchresult.ashx?jl=%E5%8C%97%E4%BA%AC%2B%E4%B8%8A%E6%B5%B7%2B%E5%B9%BF%E5%B7%9E%2B%E6%B7%B1%E5%9C%B3&kw=python%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88&p=1&isadv=0' # 设置访问头 headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36' } # 发送请求 response=requests.get(url,headers=headers) # 根据网页数据,转换为html html=etree.HTML(response.text) # 使用xpath语法进行匹配 # 获取职位名称 job_names=html.xpath("//table[@class='newlist']/tr[1]/td[@class='zwmc']/div") #job_names=html.xpath("//div[@id='newlist_list_content_table']/table[@class='newlist']/tr[1]/td[@class='zwmc']/div/a") # 定义职位名称列表 name_list=[] for job_name in job_names: job_name2=job_name.xpath('string(.)').strip() name_list.append(job_name2) # 获取职位月薪 month_nums=html.xpath("//table[@class='newlist']/tr[1]/td[@class='zwyx']") #定义保存月薪的列表 num_list=[] for num in month_nums: num2=num.xpath('string(.)').strip() num_list.append(num2) # 获取公司名称 company_names=html.xpath("//table[@class='newlist']/tr[1]/td[@class='gsmc']") #定义保存月薪的列表 company_list=[] for company_name in company_names: company_name2=company_name.xpath('string(.)').strip() company_list.append(company_name2) max_list=[] max_list.append(name_list) max_list.append(num_list) max_list.append(company_list) f = open('zl.txt', 'w') i=0 for i in range(0,len(company_list)): info= max_list[0][i]+'|'+max_list[1][i]+'|'+max_list[2][i]+"\r\n" print info f.write(info.encode('utf-8')) f.close() # 打开zl.txt文件,保存内容格式如下 ''' python开发工程师|10001-15000|乐飞天下信息技术(北京)有限公司 Python开发工程师|15001-20000|乐飞天下信息技术(北京)有限公司 python开发工程师|6001-8000|北京红枣科技有限公司 ...... '''