人生苦短,我用 Pythonhtml
前文传送门:python
小白学 Python 爬虫(2):前置准备(一)基本类库的安装github
小白学 Python 爬虫(3):前置准备(二)Linux基础入门ajax
小白学 Python 爬虫(4):前置准备(三)Docker基础入门数据库
小白学 Python 爬虫(5):前置准备(四)数据库基础框架
小白学 Python 爬虫(6):前置准备(五)爬虫框架的安装ide
小白学 Python 爬虫(10):Session 和 Cookies
小白学 Python 爬虫(11):urllib 基础使用(一)
小白学 Python 爬虫(12):urllib 基础使用(二)
小白学 Python 爬虫(13):urllib 基础使用(三)
小白学 Python 爬虫(14):urllib 基础使用(四)
小白学 Python 爬虫(15):urllib 基础使用(五)
小白学 Python 爬虫(16):urllib 实战之爬取妹子图
小白学 Python 爬虫(17):Requests 基础使用
小白学 Python 爬虫(18):Requests 进阶操做
文接上篇,咱们接着聊,上篇咱们介绍了 Xpath 一些经常使用的匹配方式, DOM 节点咱们能够匹配出来了,这并非咱们的最终目的,咱们是要从这些节点中取出来咱们想要的数据。本篇咱们接着介绍如何使用 Xpath 获取数据。
咱们先尝试下获取第一篇文章的题目,获取节点中的文本咱们可使用 text()
来进行获取,如图:
代码以下:
from lxml import etree
import requests
response = requests.get('https://www.geekdigging.com/')
html_str = response.content.decode('UTF-8')
html = etree.HTML(html_str)
result_1 = html.xpath('/html/body/section/div/div/main/article[1]/div[2]/div/h3/a/text()')
print(result_1)复制代码
结果以下:
['小白学 Python 爬虫(18):Requests 进阶操做']复制代码
哇,上面示例里面的表达式好长啊,这个怎么写出来的,怎么写的稍后再说,先介绍一下这个表达式的意思,仔细看一下,这个表达式实际上是从整个 HTML 源代码的最外层的 标签写起,一层一层的定位到了咱们所须要的节点,而后再使用
text()
方法获取了其中的文本内容。
关于这个表达式怎么来的,确定不是小编写的,这么写讲实话是有点傻,彻底不必从整个文档的最外层开始写。
其实这个是 Chrome 帮咱们生成的,具体操做可见下图:
这时 Chrome 会自动帮咱们把这个节点的表达式 copy 到当前的剪切板上,只须要咱们在程序里 ctrl + v
一下。
有些状况下,咱们可能不止须要节点中的文本数据,可能还会须要节点中的属性数据,好比上面的示例,咱们除了想知道文章标题,其实还想知道文章的跳转路径:
result_2 = html.xpath('/html/body/section/div/div/main/article[1]/div[2]/div/h3/a/@href')
print(result_2)复制代码
结果以下:
['/2019/12/11/1468953802/']复制代码
这里须要注意的是,此处和属性匹配的方法不一样,属性匹配是中括号加属性名和值来限定某个属性,如 [@class="container"]
,而此处的 @href
指的是获取节点的某个属性,两者须要作好区分。
某些时候吧,某些节点的某个属性可能有多个值,这个多见于 class
属性,因为某些编码习惯以及某些其余缘由,这个属性常常性会出现多个值,这时若是只使用其中的一个值的话,就没法匹配了。
若是这么写的话:
result_3 = html.xpath('//div[@class="post-head"]')
print(result_3)复制代码
结果以下:
[]复制代码
能够看到,这里没有匹配到任何节点,这时,咱们可使用一个函数:contains()
,上面的示例能够改为这样:
result_3 = html.xpath('//div[contains(@class, "post-head")]')
print(result_3)复制代码
这样经过 contains()
方法,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就能够进行匹配了。
除了上面的一个属性有多个值的状况,还常常会出现须要使用多个属性才能肯定一个惟一的节点。
这时,咱们可使用运算符来进行处理。
仍是这个示例,咱们获取
这个节点,若是只是使用 class 属性来进行获取,会得到不少个节点:
result_4 = html.xpath('//img[@class="img-ajax"]')
print(result_4)复制代码
结果以下:
[<Element img at 0x1984505a788>, <Element img at 0x1984505a7c8>, <Element img at 0x1984505a808>, <Element img at 0x1984505a848>, <Element img at 0x1984505a888>, <Element img at 0x1984505a908>, <Element img at 0x1984505a948>, <Element img at 0x1984505a988>, <Element img at 0x1984505a9c8>, <Element img at 0x1984505a8c8>, <Element img at 0x1984505aa08>, <Element img at 0x1984505aa48>]复制代码
若是咱们加上 alt 属性一块儿进行匹配的话,就能够得到惟一的节点:
result_4 = html.xpath('//img[@class="img-ajax" and @alt="小白学 Python 爬虫(18):Requests 进阶操做"]')
print(result_4)复制代码
结果以下:
[<Element img at 0x299905e6a48>]复制代码
Xpath 支持不少的运算符,详细见下表(来源:https://www.w3school.com.cn/xpath/xpath_operators.asp)
有些时候,咱们匹配出来不少的节点,可是咱们只想获取其中的某一个节点,好比第一个或者最后一个,这时可使用中括号传入索引的方法获取特定次序的节点。
咱们仍是文章的题目为例,咱们先获取全部的文章题目,再进行选择,示例代码以下:
result_5 = html.xpath('//article/div/div/h3[@class="post-title"]/a/text()')
print(result_5)
result_6 = html.xpath('//article[1]/div/div/h3[@class="post-title"]/a/text()')
print(result_6)
result_7 = html.xpath('//article[last()]/div/div/h3[@class="post-title"]/a/text()')
print(result_7)
result_8 = html.xpath('//article[position() < 5]/div/div/h3[@class="post-title"]/a/text()')
print(result_8)复制代码
结果以下:
['小白学 Python 爬虫(18):Requests 进阶操做', '小白学 Python 爬虫(17):Requests 基础使用', '小白学 Python 爬虫(16):urllib 实战之爬取妹子图', '如何用 Python 写一个简易的抽奖程序', '小白学 Python 爬虫(15):urllib 基础使用(五)', '咱们真的在被 APP “窃听” 么?', '小白学 Python 爬虫(14):urllib 基础使用(四)', '小白学 Python 爬虫(13):urllib 基础使用(三)', '小白学 Python 爬虫(12):urllib 基础使用(二)', '小白学 Python 爬虫(11):urllib 基础使用(一)', '老司机大型车祸现场', '小白学 Python 爬虫(10):Session 和 Cookies']
['小白学 Python 爬虫(18):Requests 进阶操做']
['小白学 Python 爬虫(10):Session 和 Cookies']
['小白学 Python 爬虫(18):Requests 进阶操做', '小白学 Python 爬虫(17):Requests 基础使用', '小白学 Python 爬虫(16):urllib 实战之爬取妹子图', '如何用 Python 写一个简易的抽奖程序']复制代码
第一次,咱们选取了当前页面全部的文章的题目。
第二次,咱们选择了当前页面第一篇文章的题目,这里注意下,中括号中传入数字1便可,这里的开始是以 1 为第一个的,不是程序中的 0 为第一个。
第三次,咱们使用 last()
函数,获取了最后一篇文章的题目。
第四次,咱们选择了位置小于 5 的文章题目。
轴可定义相对于当前节点的节点集。
轴名称 |
结果 |
---|---|
ancestor |
选取当前节点的全部先辈(父、祖父等)。 |
ancestor-or-self |
选取当前节点的全部先辈(父、祖父等)以及当前节点自己。 |
attribute |
选取当前节点的全部属性。 |
child |
选取当前节点的全部子元素。 |
descendant |
选取当前节点的全部后代元素(子、孙等)。 |
descendant-or-self | 选取当前节点的全部后代元素(子、孙等)以及当前节点自己。 |
following |
选取文档中当前节点的结束标签以后的全部节点。 |
namespace |
选取当前节点的全部命名空间节点。 |
parent |
选取当前节点的父节点。 |
preceding |
选取文档中当前节点的开始标签以前的全部节点。 |
preceding-sibling |
选取当前节点以前的全部同级节点。 |
self |
选取当前节点。 |
咱们以 ancestor
轴来作示例:
# 节点轴示例
# 获取全部祖先节点
result_9 = html.xpath('//article/ancestor::*')
print(result_9)
# 获取祖先节点 main 节点
result_10 = html.xpath('//article/ancestor::main')
print(result_10)复制代码
结果以下:
[<Element html at 0x2a266171208>, <Element body at 0x2a266171248>, <Element section at 0x2a266171288>, <Element div at 0x2a2661712c8>, <Element div at 0x2a266171308>, <Element main at 0x2a266171388>]
[<Element main at 0x2a266171388>]复制代码
关于节点轴就先介绍到这里,更多的轴的用法能够参考:https://www.w3school.com.cn/xpath/xpath_axes.asp 。
本系列的全部代码小编都会放在代码管理仓库 Github 和 Gitee 上,方便你们取用。