scrapy提取数据之:xpath选择器
css选择器;虽然一招半式足以称霸江湖,但这里老夫还想再传授诸位一招:xpath提取!好好学,技多不压身。还是从几个方面说:一、属性提取;二、内容提取;三、标签内包含标签又包含标签的最外层标签里的所有内容提取;会了这些基本也就会了,反正scrapy用到的就这么几个知识,少之又少,不要想着除了这些还有神马冰上之下,绝对木有,请放心,如果真的有,我会再末尾给诸君写一些例子,依葫芦画瓢就行,那接下来就开始装13;
一、scrapy xpath 属性提取
这里先给大家列出xpath的选择器类型,如下表:XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。 下面列出了最有用的路径表达式:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
上面神马意思或许你还不清楚,没关系,1+1为神马等于2我们也无法证明,会用即可;可以看到上面选取属性用的是:@符号,那我们还是来试试提取最常见的属性:href、src,首先我们试着来提取href,还是用:http://lab.scrapy.cn开刀,就提取首页分页的href,如下图:
调试的话我们还是在命令行使用下面命令:
scrapy shell lab.scrapyd.cn
这样的话就能成功打开lab.scrapyd.cn这个页面,那我们要如何使用xpath表达式呢?用到了这么一个函数:response.xpath("表达式"),提取属性的话既然使用:@,那我们要提取href就是:@href,试一下:
In [1]: response.xpath("@href")
Out[1]: []
可以看到神马都木有,why?因为我们木有限定从哪里提取,一般我们都需要加个://,再来试试
In [2]: response.xpath("//@href")
Out[2]:
[<Selector xpath='//@href' data='//cdnjscn.b0.upaiyun.com/libs/normalize/'>,
<Selector xpath='//@href' data='http://lab.scrapyd.cn/usr/themes/default'>,
……
<Selector xpath='//@href' data='http://lab.scrapyd.cn/tag/%E7%8E%8B%E5%B'>,
<Selector xpath='//@href' data='http://lab.scrapyd.cn/tag/%E6%99%BA%E6%8'>,
<Selector xpath='//@href' data='http://lab.scrapyd.cn/tag/%E6%B3%B0%E6%8'>,
……
<Selector xpath='//@href' data='http://lab.scrapyd.cn/tag/%E8%AF%8D/'>,
<Selector xpath='//@href' data='http://lab.scrapyd.cn'>,
<Selector xpath='//@href' data='http://bbs.scrapyd.cn'>,
<Selector xpath='//@href' data='http://www.scrapyd.cn'>,
<Selector xpath='//@href' data='http://lab.scrapyd.cn/'>]
可以看到已经提取到了,只是这是这个页面所有的href,并非是我们想要的,那和css选择一样我们需要加以限制,如何限制呢?我们可以看到分页href是在一个<ol></ol>标签里面,那我们就可以这样来写:
In [9]: response.xpath("//ol//@href")
Out[9]:
[<Selector xpath='//ol//@href' data='http://lab.scrapyd.cn/page/1/'>,
<Selector xpath='//ol//@href' data='http://lab.scrapyd.cn/page/2/'>,
<Selector xpath='//ol//@href' data='http://lab.scrapyd.cn/page/3/'>,
<Selector xpath='//ol//@href' data='http://lab.scrapyd.cn/page/4/'>,
<Selector xpath='//ol//@href' data='http://lab.scrapyd.cn/page/6/'>,
<Selector xpath='//ol//@href' data='http://lab.scrapyd.cn/page/2/'>]
"//ol//@href",这个表达式表示:ol标签下所有的href属性值,可以看到我们这里限定了html的<ol>标签,这里的话页面只有一个<ol>,不会出错,如果页面中有多个<ol>,那就不一定能得到我们想要的结果,那如何是好?这里我们还能限定我们的属性,使用的是:标签[@属性名='属性值']; 例如,我们分页标签:
<ol class="page-navigator">
……
</ol>
里面有个:class=“page-navigator”,那我们就可以这样限制://ol[@class="page-navigator"]//@href 好了,这样的话就能让<ol>尽量缩小范围,我们再来试试这个代码效果:
In [10]: response.xpath("//ol[@class='page-navigator']//@href")
Out[10]:
[<Selector xpath="//ol[@class='page-navigator']//@href" data='http://lab.scrapyd
.cn/page/1/'>,
<Selector xpath="//ol[@class='page-navigator']//@href" data='http://lab.scrapyd
.cn/page/2/'>,
<Selector xpath="//ol[@class='page-navigator']//@href" data='http://lab.scrapyd
.cn/page/3/'>,
<Selector xpath="//ol[@class='page-navigator']//@href" data='http://lab.scrapyd
.cn/page/4/'>,
<Selector xpath="//ol[@class='page-navigator']//@href" data='http://lab.scrapyd
.cn/page/6/'>,
<Selector xpath="//ol[@class='page-navigator']//@href" data='http://lab.scrapyd
.cn/page/2/'>]
可以看到也是同样得到想要的效果,以此类推,诸位根据自己的情况来缩小范围,如果这里的属性是id那就:ol[@id='page-navigator'],灵活点!那这里我们还没有得出href,前后都有些括号,和css选择器一样我们还是用到了:extract()、extract_first()这两个函数,加上就可以提取纯粹的href值了,如下:
In [11]: response.xpath("//ol[@class='page-navigator']//@href").extract()
Out[11]:
['http://lab.scrapyd.cn/page/1/',
'http://lab.scrapyd.cn/page/2/',
'http://lab.scrapyd.cn/page/3/',
'http://lab.scrapyd.cn/page/4/',
'http://lab.scrapyd.cn/page/6/',
'http://lab.scrapyd.cn/page/2/']
好了,这就是属性的提取,表达式就是://@属性名 这里的话我们还给诸君说了另外一些思路,也就是:如何缩小范围?我们使用的是:缩小标签范围、限定属性的方式,希望诸君学到的是思路!
二、接下来我们提取标签里面的内容,表达式: //text()
这个没多少好讲的,关键是以上的思路,我们需要层次限制,把范围缩小到最小,这样提取便很准确了,诸位需要在实践中多练习,我们来提取一下:lab.scrapy.cn 的标题,看用法:
In [4]: response.xpath("//title//text()").extract()
Out[4]: ['SCRAPY爬虫实验室 - SCRAPY中文网提供']
可以看到,表达式为://title//text() ,就是用到了://text() 这个表达式,前面的://title是限定你要提取的范围,同理如果你要提取首页右侧的标签,如下:
<ul class="tags-list">
<li><a style="color:rgb(101,86,
72)" href="http://lab.scrapyd.cn/tag/%E4%BA%BA%E7%94%9F/">
人生</a></li>
<li><a style="color:rgb(214,236,
5)" href="http://lab.scrapyd.cn/tag/%E5%8A%B1%E5%BF%97/">
励志</a></li>
……
<li><a style="color:rgb(4,4,
41)" href="http://lab.scrapyd.cn/tag/%E7%BB%9D%E4%B8%96%E5%A5%BD%E8%AF%8D/">
绝世好词</a></li>
<li><a style="color:rgb(204,12,
225)" href="http://lab.scrapyd.cn/tag/%E6%9C%A8%E5%BF%83/">
木心</a></li>
……
<li><a href="http://lab.scrapyd.cn">返回首页</a></li>
<li><a href="http://bbs.scrapyd.cn" target="_blank">SCRAPY中文社区</a></li>
<li><a href="http://www.scrapyd.cn" target="_blank">SCRAPY中文网</a></li>
</ul>
可以看到标签文字是在 "class=tags-list" 的<ul>里面,那我们就可以这样写表达式://ul[@class='tags-list']//a//text(),具体代码如下:
In [5]: response.xpath("//ul[@class='tags-list']//a//text()").extract()
Out[5]:
['\r\n 人生',
'\r\n 励志',
'\r\n 爱情',
'\r\n 王尔德',
'\r\n 智慧',
'\r\n 泰戈尔',
'\r\n 绝世好词',
'\r\n 木心',
'\r\n 艺术',
'\r\n 名画',
'\r\n 生活',
'\r\n 词',
'返回首页',
'SCRAPY中文社区',
'SCRAPY中文网']
可以看到经过这样的限制,我们就定位到了;
三、包含HTML标签的所有文字内容提取:string()
这种用法主要是提取一些内容页,里面标签夹杂着文字,但我们只要文字!比如我们随便点击进:http://lab.scrapyd.cn/archives/28.html 这个详情页,我们要提取div里面的所有文字:
<div class="post-content" itemprop="articleBody">
<p>如果你因失去了太阳而流泪,那么你也将失去群星了。
<br>If you shed tears when you miss the sun, you also miss the stars.
</p>
<p><a href="http://www.scrapyd.cn">scrapy中文网(</a><a href="http://www.scrapyd.cn">http://www.scrapyd.cn</a>)整理</p>
</div>
如果我们用表达式://div[@class='post-content']//text(),你会发现虽然能提取但是一个列表,不是整段文字:
In [4]: response.xpath("//div[@class='post-content']//text()").extract()
Out[4]:
['\n ',
'如果你因失去了太阳而流泪,那么你也将失去群星了。 ',
'If you shed tears when you miss the sun, you also miss the stars. ',
'scrapy中文网(',
'http://www.scrapyd.cn',
')整理',
' ']
那这里我们就用到一个xpath函数:string(),我们可以把表达式这样写:response.xpath("string(//div[@class='post-content'])").extract(),可看到我们没有使用:text(),而是用:string(要提取内容的标签),这样的话就能把数据都提取出来了,而且都合成为一条,并非一个列表,如下:
In [5]: response.xpath("string(//div[@class='post-content'])").extract()
Out[5]: ['\n 如果你因失去了太阳而流泪,那么你也将失去群星了。 If you
shed tears when you miss the sun, you also miss the stars. scrapy中文网(http://
www.scrapyd.cn)整理 ']
这一种用法在我们提取商品详情、小说内容的时候经常用到,望诸君好好学习、天天赚钱!
四、xpath实例:
路径表达式 | 结果 |
---|---|
/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。 |
路径表达式 | 结果 |
---|---|
bookstore | 选取 bookstore 元素的所有子节点。 |
/bookstore |
选取根元素 bookstore。 注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径! |
bookstore/book | 选取属于 bookstore 的子元素的所有 book 元素。 |
//book | 选取所有 book 子元素,而不管它们在文档中的位置。 |
bookstore//book | 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。 |
//@lang | 选取名为 lang 的所有属性。 |
XPath 通配符可用来选取未知的 HTML元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素节点。 |
@* | 匹配任何属性节点。 |
node() | 匹配任何类型的节点。 |
在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:
路径表达式 | 结果 |
---|---|
/bookstore/* | 选取 bookstore 元素的所有子元素。 |
//* | 选取文档中的所有元素。 |
//title[@*] | 选取所有带有属性的 title 元素。 |
差不多就这些,如果诸君还想深入,百度xpath有很多教程,我也懒得复制粘贴了,以上内容scrapy够用即可!