[Python爬虫] 六、数据提取之XPath与lxml类库
往期内容提要:
- [Python爬虫] 一、爬虫原理之HTTP和HTTPS的请求与响应
- [Python爬虫] 二、爬虫原理之定义、分类、流程与编码格式
- [Python爬虫] 三、数据抓取之Requests HTTP 库
- [Python爬虫] 四、数据抓取之HTTP/HTTPS抓包工具Fiddler
- [Python爬虫] 五、数据提取之正则表达式re模块
一、非结构化数据与结构化数据
一般来讲对我们而言,需要抓取的是某个网站或者某个应用的内容,提取有用的价值。内容一般分为两部分,非结构化的数据 和 结构化的数据。
- 非结构化数据:先有数据,再有结构。
- 结构化数据:先有结构、再有数据。
- 不同类型的数据,我们需要采用不同的方式来处理。
处理方式 | 非结构化数据 | 结构化数据 |
---|---|---|
正则表达式 | 文本、电话号码、邮箱地址、HTML 文件 | XML 文件 |
XPath | HTML 文件 | XML 文件 |
CSS选择器 | HTML 文件 | XML 文件 |
JSON Path | JSON 文件 | |
转化成Python类型 | JSON 文件(json类)、XML 文件(xmltodict) |
上一章节详细向大家介绍了正则表达式,有同学说,我正则用的不好,处理HTML文档很累,有没有其他的方法?有!那就是XPath,我们可以先将 HTML文件 转换成 XML文档,然后用 XPath 查找 HTML 节点或元素。
二、了解XML
- XML 指可扩展标记语言(EXtensible Markup Language)
- XML 是一种标记语言,很类似 HTML
- XML 的设计宗旨是传输数据,而非显示数据
- XML 的标签需要我们自行定义。
- XML 被设计为具有自我描述性。
- XML 是 W3C 的推荐标准
W3School官方文档:http://www.w3school.com.cn/xml/index.asp
(1) XML 和 HTML 的区别
数据格式 | 描述 | 设计目标 |
---|---|---|
XML |
Extensible Markup Language (可扩展标记语言)
|
被设计为传输和存储数据,其焦点是数据的内容。 |
HTML |
HyperText Markup Language (超文本标记语言)
|
显示数据以及如何更好显示数据。 |
HTML DOM |
Document Object Model for HTML (文档对象模型)
|
通过 HTML DOM,可以访问所有的 HTML 元素,连同它们所包含的文本和属性。可以对其中的内容进行修改和删除,同时也可以创建新的元素。 |
(2) 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. Rowling</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> <author>Vaidyanathan Nagarajan</author> <year>2003</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>
(3) HTML DOM 模型示例
HTML DOM 定义了访问和操作 HTML 文档的标准方法,以树结构方式表达 HTML 文档。
(4) XML的节点关系
<?xml version="1.0" encoding="utf-8"?><bookstore><book><title>Harry Potter</title><author>J K. Rowling</author><year>2005</year><price>29.99</price>
</book></bookstore>
名称 | 含义 | 例子 |
---|---|---|
父(Parent) | 每个元素以及属性都有一个父 | book 元素是 title、author、year 以及 price 元素的父 |
子(Children) | 元素节点可有零个、一个或多个子 | title、author、year 以及 price 元素都是 book 元素的子 |
同胞(Sibling) | 拥有相同的父的节点 | title、author、year 以及 price 元素都是同胞 |
先辈(Ancestor) | 某节点的父、父的父,等等 | title 元素的先辈是 book 元素和 bookstore 元素 |
后代(Descendant) | 某个节点的子,子的子,等等 | bookstore 的后代是 book、title、author、year 及 price 元素 |
三、了解XPath
XPath (XML Path Language) 是一门在 XML 文档中查找信息的语言,可用来在 XML 文档中对元素和属性进行遍历。
W3School官方文档:http://www.w3school.com.cn/xpath/index.asp
(1) XPath 开发工具
- 开源的XPath表达式编辑工具:XMLQuire(XML格式文件可用)
- Chrome插件 XPath Helper
- Firefox插件 XPath Checker
这里以Chrome插件 XPath Helper为例,可以看到匹配到的标签会加载上class="xh-highlight"高光标签。初学者可以多加练习,结果会在右上方的黑色方框中回显,其中RESULTS 括号后的数字指匹配到的目标个数。
(2) 选取节点
XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
下面列出了最常用的路径表达式:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:
路径表达式 | 结果 |
---|---|
bookstore | 选取 bookstore 元素的所有子节点。 |
/bookstore | 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@lang | 选取名为 lang 的所有属性。 |
(3) 谓语(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 元素中的值须大于 35.00的 book 元素的所有 title 元素。 |
(4) 选取未知节点
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
---|---|
/bookstore/* | 选取 bookstore 元素的所有子元素。 |
//* | 选取文档中的所有元素。 |
html/node()/meta/@* | 选择html下面任意节点下的meta节点的所有属性 |
//title[@*] | 选取所有带有属性的 title 元素。 |
(5) 选取若干路径
通过在路径表达式中使用“|”运算符,您可以选取若干个路径。
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
---|---|
//book/title | //book/price | 选取 book 元素的所有 title 和 price 元素。 |
//title | //price | 选取文档中的所有 title 和 price 元素。 |
/bookstore/book/title | //price | 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。 |
(6) XPath的运算符
下面列出了可用在 XPath 表达式中的运算符:
这些就是XPath的语法内容,在运用到Python抓取时要先转换为xml。
(7) 归纳总结:
获取文本
a/text()
获取a下的文本a//text()
获取a下的所有标签的文本//a[text()='下一页']
选择文本为下一页三个字的a标签
@符号
a/@href
获取a下的href ——>举一反三:a/@scr
获取a下的scr值//div[@id="detail-list"]
——>举一反三://*[@class="aa"]
定位任意class为aa的标签
//
- 在xpath最前面表示从当前html中任意位置开始选择
li//a
表示的是li下任何一个标签
四、lxml库
lxml 是 一个HTML/XML的解析器,主要的功能是如何解析和提取 HTML/XML 数据。
lxml和正则一样,也是用 C 实现的,是一款高性能的 Python HTML/XML 解析器,我们可以利用之前学习的XPath语法,来快速的定位特定元素以及节点信息。
lxml python 官方文档:http://lxml.de/index.html
需要安装C语言库,可使用 pip 安装:
pip install lxml
(或通过wheel方式安装)
(1) 初步使用
我们利用它来解析 HTML 代码,简单示例:
# lxml_test.py# 使用 lxml 的 etree 库
from lxml import etreetext = '''
<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>
'''#利用etree.HTML,将字符串解析为HTML文档
html = etree.HTML(text)# 按字符串序列化HTML文档
result = etree.tostring(html)print(result)
输出结果:
<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>
lxml 可以自动修正 html 代码,例子里不仅补全了 li 标签,还添加了 body,html 标签。
(2) 文件读取:
除了直接读取字符串,lxml还支持从文件里读取内容。我们新建一个hello.html文件:
<!-- hello.html -->
<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"><span class="bold">third item</span></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>
再利用 etree.parse() 方法来读取文件。
# lxml_parse.pyfrom lxml import etree# 读取外部文件 hello.html
html = etree.parse('./hello.html')
result = etree.tostring(html, pretty_print=True)print(result)
输出结果与之前相同:
<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>
五、XPath实例测试
<!-- hello.html -->
<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"><span class="bold">third item</span></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>
(1) 获取所有的 <li>
标签
# xpath_li.pyfrom lxml import etreehtml = etree.parse('hello.html')
print type(html) # 显示etree.parse() 返回类型result = html.xpath('//li')print result # 打印<li>标签的元素集合
print len(result)
print type(result)
print type(result[0])
输出结果:
<type 'lxml.etree._ElementTree'>
[<Element li at 0x1014e0e18>, <Element li at 0x1014e0ef0>, <Element li at 0x1014e0f38>, <Element li at 0x1014e0f80>, <Element li at 0x1014e0fc8>]
5
<type 'list'>
<type 'lxml.etree._Element'>
(2) 继续获取 hello.html
属性
<!-- hello.html -->
<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"><span class="bold">third item</span></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>
# xpath_li.pyfrom lxml import etreehtml = etree.parse('hello.html')
result1 = html.xpath('//li/@class') // 获取 <li> 标签的所有 class 属性
result2 = html.xpath('//li/a[@href="link1.html"]') //获取<li>标签下 href 为 link1.html 的 <a> 标签
result3 = html.xpath('//li//span') //获取<li> 标签下的所有 <span> 标签 (因为 / 是用来获取子元素的,而 <span> 并不是 <li> 的子元素,所以,要用双斜杠)
result4 = html.xpath('//li/a//@class') //获取 <li> 标签下的 <a> 标签里的所有 class
result5 = html.xpath('//li[last()]/a/@href') //获取最后一个 <li> 的 <a> 的 href
result6 = html.xpath('//li[last()-1]/a') //获取倒数第二个元素的内容
result7 = html.xpath('//*[@class="bold"]') //获取 class 值为 bold 的标签名print result1
print result2
print result3
print result4
print result5
print result6
print result7
运行结果
['item-0', 'item-1', 'item-inactive', 'item-1', 'item-0']
[<Element a at 0x10ffaae18>]
[<Element span at 0x10d698e18>]
['blod']
['link5.html']
fourth item
span
六、使用XPath爬虫
现在我们用XPath来做一个简单的爬虫,我们尝试爬取某个贴吧里的所有帖子,并且将该这个帖子里每个楼层发布的图片下载到本地。
#coding=utf-8
import requests
from lxml import etree
import jsonclass Tieba:def __init__(self,tieba_name):self.tieba_name = tieba_name #接收贴吧名#设置为手机端的UAself.headers = {"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1"}def get_total_url_list(self):'''获取所有的url list'''url = "https://tieba.baidu.com/f?kw="+self.tieba_name+"&ie=utf-8&pn={}&"url_list = []for i in range(100): #通过循环拼接100个urlurl_list.append(url.format(i*50))return url_list #返回100个url的url listdef parse_url(self,url):'''一个发送请求,获取响应,同时etree处理html'''print("parsing url:",url)response = requests.get(url,headers=self.headers,timeout=10) #发送请求html = response.content.decode() #获取html字符串html = etree.HTML(html) #获取element 类型的htmlreturn htmldef get_title_href(self,url):'''获取一个页面的title和href'''html = self.parse_url(url) #返回elemet类型的html,具有xpath方法li_temp_list = html.xpath("//li[@class='tl_shadow']") #分组,按照li标签分组total_items = []for i in li_temp_list: #遍历分组# href = i.xpath("./a/@href")[0] if len(i.xpath("./a/@href"))>0 else None# if href is not None and not href.startswith("https:"):# href = "https:"+hrefhref = "https:"+i.xpath("./a/@href")[0] if len(i.xpath("./a/@href"))>0 else Nonetext = i.xpath("./a/div[1]/span[1]/text()")text = text[0] if len(text)>0 else Noneitem = dict( #放入字典href = href,text = text)total_items.append(item)return total_items #返回一个页面所有的itemdef get_img(self,url):'''获取一个帖子里面的所有图片'''html = self.parse_url(url) #返回elemet类型的html,具有xpath方法img_list = html.xpath('//div[@data-class="BDE_Image"]/@data-url')img_list = [i.split("src=")[-1] for i in img_list] #正则表达式提取图片的urlimg_list = [requests.utils.unquote(i) for i in img_list] #URL解码return img_listdef save_item(self,item):'''保存一个item'''with open("teibatupian.txt","a") as f:f.write(json.dumps(item,ensure_ascii=False,indent=2))f.write("\n")def run(self):#1、找到了url规律,url listurl_list = self.get_total_url_list()for url in url_list:#2、遍历urllist 发送请求,获得响应,etree处理html# 3、提取title,hreftotal_item = self.get_title_href(url)for item in total_item:href = item["href"]img_list = self.get_img(href) #获取到了帖子的图片列表item["img"] = img_list# 4、保存到本地print(item)self.save_item(item)if __name__ == "__main__":tieba = Tieba("CSDN")tieba.run()
基本思路:在确定爬取对象后,开始运行run
方法,get_total_url_list
方法定义了每页链接的递归方法,首先结合parse_url
方法爬得全部数据,并通过etree将全部数去导入至lxml类库中,再通过get_title_href
方法和get_img
方法采用XPath形式提取有用数据,最后通过save_item
方法实现数据存储。
爬虫一共四个主要步骤:
- 明确目标 (要知道你准备在哪个范围或者网站去搜索)
- 爬 (将所有的网站的内容全部爬下来)
- 取 (去掉对我们没用处的数据)
- 处理数据(按照我们想要的方式存储和使用)
步骤编号 | 爬虫步骤 | 对应操作 |
---|---|---|
1 | 明确目标 | Tieba(self,tieba_name) |
2 | 爬 | get_total_url_list;parse_url |
3 | 取 | get_title_href;get_img |
4 | 处理数据 | save_item |
七、CSS 选择器:BeautifulSoup4
除了 lxml 之外,Beautiful Soup 也是一个HTML/XML的解析器,主要的功能也是解析和提取 HTML/XML 数据。lxml 只会局部遍历,而Beautiful Soup 是基于HTML DOM的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml,故在此不再多述。
BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。
Beautiful Soup 3 目前已经停止开发,推荐现在的项目使用Beautiful Soup 4。使用 pip 安装即可:
pip install beautifulsoup4
官方文档:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0
抓取工具 | 速度 | 使用难度 | 安装难度 |
---|---|---|---|
正则 | 最快 | 困难 | 无(内置) |
BeautifulSoup | 慢 | 最简单 | 简单 |
lxml | 快 | 简单 | 一般 |
后期内容提要:
- [Python爬虫] 七、结构化数据提取之JSON与JsonPATH
- [Python爬虫] 八、动态HTML处理之Selenium与PhantomJS
- [Python爬虫] 九、机器图像识别之机器视觉与Tesseract
- [Python爬虫] 十、机器图像识别之文字、验证码识别
- [Python爬虫] 十一、Scrapy 框架
如果您有任何疑问或者好的建议,期待你的留言与评论!
[Python爬虫] 六、数据提取之XPath与lxml类库相关推荐
- Python 爬虫找到数据了 re XPath requests Pool
Python 爬虫找到数据了 re & XPath & requests & Pool 2018.06.16 23:18 88浏览 字号 是的,爬虫就是为了获取数据.在获取的数 ...
- Python爬虫的数据提取,一篇博客就搞定啦!
数据提取 目录 数据提取 XPath语法和lxml模块 XPath 什么是XPath XPath开发工具 XPath语法 选取节点: 谓语: 通配符 选取多个路径: 运算符: 总结 使用方式 需要注意 ...
- python爬虫科研数据提取_python爬虫数据提取四之pyquery
1 pyquery 简介:同样是一个强大的网页解析工具 它提供了和jQuery类似的语法来解析HTML文档,支持CSS选择器,使用非常方便 2 pyquery基本用法 2.1 安装 pip insta ...
- 【python】——爬虫03 数据提取[jsonpath模块、lxml模块]
目录 一.概述 1. 响应内容分类 2. xml和html 3. 数据解析 二.jsonpath模块 1. 提取数据的方法 2. jsonpath语法规则 3. jsonpath练习:获取拉钩网城市j ...
- python 爬虫与数据可视化
python 爬虫与数据可视化 1.引言 Web已经成为日新月异迅速发展的网络信息技术中的信息载体,如何有效地提取和利用搜索引擎获得互联网最有用的.可以免费公开访问的数据集,查找用户所需的价值数据或者 ...
- python 爬虫及数据可视化展示
python 爬虫及数据可视化展示 学了有关python爬虫及数据可视化的知识,想着做一些总结,加强自己的学习成果,也能给各位小伙伴一些小小的启发. 1.做任何事情都要明确自己的目的,想要做什么,打算 ...
- Python爬虫以及数据可视化分析
Python爬虫以及数据可视化分析之Bilibili动漫排行榜信息爬取分析 简书地址:https://www.jianshu.com/u/40ac87350697 简单几步,通过Python对B站番剧 ...
- Python爬虫+数据分析+数据可视化(分析《雪中悍刀行》弹幕)
Python爬虫+数据分析+数据可视化(分析<雪中悍刀行>弹幕) 哔哔一下 爬虫部分 代码部分 效果展示 数据可视化 代码展示 效果展示 视频讲解 福利环节 哔哔一下 雪中悍刀行兄弟们都看 ...
- 小白学 Python 爬虫(20):Xpath 进阶
人生苦短,我用 Python 前文传送门: 小白学 Python 爬虫(1):开篇 小白学 Python 爬虫(2):前置准备(一)基本类库的安装 小白学 Python 爬虫(3):前置准备(二)Li ...
最新文章
- 谷歌自动驾驶之父疯狂打Call, 无人车连续5小时不接管,又快又稳
- POJ3687拓扑排序+贪心
- sublime 2 中常用快捷键
- 科研人必备的学术导航,不看后悔!
- python读取大文件的某行_Python按行读取文件的实现方法【小文件和大文件读取】...
- 每天都在红绿灯前面梭行,不如自己来实现个红绿灯?
- leetcode之回溯backtracing专题5
- linux依据时间过滤文件,详解Linux查找目录下的按时间过滤的文件
- python 2 与 python 3 —— 转义及编码(\u, \x)
- 190819每日一句
- 30秒让你有效的使用天池实验室资源
- 恶意代码分析实战——反汇编
- 【数据库】数据库期末考试复习试题与答案
- 计算机知识大赛五书,2017昆山千灯镇事业单位考试常识——昆山市情解析
- 简单的博客页面客制化 v2
- java 生成水印图片工具类, MultipartFile接收上传的图片,处理成加水印之后的MultipartFile
- css实现单边斜切效果
- 区块链时代的大数据生态
- matlab牛顿环gif,牛顿环干涉实验的 Matlab模拟
- ai画面怎么调大小_AI中怎么才能把图像等比例扩大或缩小尺寸?
热门文章
- 【宝塔】宝塔一键安装项目模块, 数据为空时的默认提示
- 处理谷歌浏览器导出书签为json格式
- 操作手册和用户手册的区别
- Spring task:annotation-driven配置之 @Scheduled定时任务的fixedRate,fixedDelay,cron执行差异
- python 从日期列表中选出最大的_python – 从日期时间列表中获取最早和最晚时间...
- 播放器基础--OpenSL ES音频播放
- 使用MediaCodec+OpenSL编写简单的音频播放器
- 【笔记】范数:L1范数充当正则项,让模型获得稀疏解,解决过拟合问题
- 将txt文本由utf-8转gbk
- jsp+ssm计算机毕业设计大学生心理咨询网站【附源码】