使用scrapy中xpath选择器的一个坑点
情景如下:
一个网页下有一个ul,这个ur下有125个li标签,每个li标签下有我们想要的 url 字段(每个 url 是唯一的)和 price 字段,我们现在要访问每个li下的url并在生成的请求中携带该请求的price字段
毫无疑问,这里是要用到scrapy项目内meta传参的,那么我们思路可能是这样:
1)start_requests访问初始网页
2)定义一个 parse 方法,通过xpath选择器获取所有的li标签,遍历每个 li 标签,获取 url 和 price 字段,生成目标地址为 url 的 scrapy.Request对 象,将 price 打包到 Request 对象的 meta 中,分别yield新地址为 url 的 scrapy.Request 对象
3)对新的 response 进行处理
现在问题出在第2)步骤中:
我们可能发现遍历 li 标签获取的 url 和 price 对象都是一样的,如
In [20]: url_item = response.xpath('//ul[contains(@class, "house-list")]/li')
In [21]: for item in url_item:
...: url = item.xpath('//h2[@class="title"]/a/@href').extract_first()
...: print(url)
...:
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
......省略122个相同url
可以猜想scrapy中scrapy.selector.unified.SelectorList对象在进行遍历时对子元素操作时,事实上并不是对子元素的操作,而是仍然在对这个SelectorList对象进行操作
In [24]: for item in url_item:
...: url = url_item.xpath('//h2[@class="title"]/a/@href').extract_first()
...: print(url)
...:
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37822311633030x.shtml
......省略122个相同url
以上两种结果完全一致,为了证明我的猜想,我这次在遍历时不用extract_first(),而使用extract(),结果如下:
In [34]: for item in url_item:
...: urls = url_item[2].xpath('//h2[@class="title"]/a/@href').extract()
...: print(urls)
...:
['https://bj.58.com/ershoufang/37822311633030x.shtml', 'https://bj.58.com/ershoufang/37834554715403x.shtml', 'https://bj.58.com/ershoufang/37769196098828x.shtml',
.....省略121个不同url
'https://bj.58.com/ershoufang/37398992001320x.shtml']
......省略和上面相同的123个列表
['https://bj.58.com/ershoufang/37822311633030x.shtml', 'https://bj.58.com/ershoufang/37834554715403x.shtml', 'https://bj.58.com/ershoufang/37769196098828x.shtml',
.....
'https://bj.58.com/ershoufang/37398992001320x.shtml']
分析:遍历的每个item里面只有自己唯一的url,即使extract(),打印的也应该是含自己唯一的url的列表,并且每个item打印的url列表各不相同,
但实际每个item打印的列表包含了所有的url,且每个item打印的url列表完全一致,并且每个item中这个一致的url列表与item的父元素url_item的url列表一致:
In [37]: response.xpath('//ul[contains(@class, "house-list")]/li//h2[@class="title"]/a/@href').extract()
Out[37]:
['https://bj.58.com/ershoufang/37822311633030x.shtml',
'https://bj.58.com/ershoufang/37834554715403x.shtml',
'https://bj.58.com/ershoufang/37769196098828x.shtml',
......省略121个不同url
'https://bj.58.com/ershoufang/37398992001320x.shtml']
结果证实了我的猜想,这也就是我说的scapy中xpath选择器的坑,那么还是面对我最开始提出的情景,该如何解决呢?
在这里提供两种思路:
1)不要使用scrapy中xpath选择器的链式解析,在拿到scrapy.selector.unified.SelectorList对象后,不要通过遍历直接链式解析,直接提取出html文本列表,并对这个列表进行遍历,对每个子元素再生成 scrapy.selector.unified.Selector 对象,然后通过 xpath 提取数据,如下
In [52]: url_item = response.xpath('//ul[contains(@class, "house-list")]/li')
In [53]: items = url_item.extract()
In [55]: for item in items:
...: sele_obj = scrapy.Selector(text=item)
...: url = sele_obj.xpath('//h2[@class="title"]/a/@href').extract_first()
...: print(url)
...:
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37834554715403x.shtml
https://bj.58.com/ershoufang/37769196098828x.shtml
......省略121个不同url
'https://bj.58.com/ershoufang/37398992001320x.shtml'
方法一
成功拿到每个 li 下的url
2)使用scrapy中xpath选择器的链式解析,在拿到scrapy.selector.unified.SelectorList对象后,开始改用 css 选择器解析:
In [60]: url_item = response.css('ul[class *= "house-list"]>li')
In [61]: for item in url_item:
...: url = item.css('h2.title>a::attr(href)').extract_first()
...: print(url)
...:
https://bj.58.com/ershoufang/37822311633030x.shtml
https://bj.58.com/ershoufang/37834554715403x.shtml
https://bj.58.com/ershoufang/37769196098828x.shtml
......省略121个不同url
https://bj.58.com/ershoufang/37398992001320x.shtml
方法二
使用scrapy中xpath选择器的一个坑点的更多相关文章
- 爬虫(十一):scrapy中的选择器
Scrapy提取数据有自己的一套机制,被称作选择器(selectors),通过特定的Xpath或者CSS表达式来选择HTML文件的某个部分Xpath是专门在XML文件中选择节点的语言,也可以用在HTM ...
- JavaScript中sort方法的一个坑(leetcode 179. Largest Number)
在做 Largest Number 这道题之前,我对 sort 方法的用法是非常自信的.我很清楚不传比较因子的排序会根据元素字典序(字符串的UNICODE码位点)来排,如果要根据大小排序,需要传入一个 ...
- [Scrapy-6] XPath使用的一个坑
先上代码: import scrapy from scrapy.selector import Selector class QuoteSpider(scrapy.Spider): name = &q ...
- 记自己在mybatis中设置jdbcType的一个坑
项目是用ssm搭建的.主要是为app数据接口.其中有一个需求就app想要查询一段时间内某个用户的测量信息,所以app给我后端传递了3个参数,分别是appuserId(String),startDate ...
- UC浏览器中Ajax请求中传递数据的一个坑
今天突然收到一个bug,有用户在其浏览器环境中一直无法提交内容,使用的是UC浏览器.当换成Chrome时,内容能够正常提交.鉴于本地没有一直使用Firefox 以及Chrome,于是去下载了一个UC ...
- 说说PHP中foreach引用的一个坑
From: http://blog.csdn.net/yipiankongbai/article/details/45307767 先来看看下面这段代码: <?php $arr = array( ...
- 问题记录 | 记录PIL中Image.save的一个坑
Image.save然后open数值是会变的 我找了一个下午终于找出问题所在,PIL的Image库中把图片resize了之后存在本地然后再读进来,与直接resize后的数值是不一样的. data_va ...
- 关于使用layui中的tree的一个坑
最近几天,因为项目需要,所以自学了下layui,在使用之前就对其比较感兴趣,毕竟封装的东西也不错(个人见解),在接触到layui之后,现在有个需要就是将部门做成tree的样子,开始觉得不怎么难,毕竟都 ...
- 记录MYSQL中SQL语句的一个坑.
MYSQL5.7 假设我们有一个表 : h_member_cards_my (ID, WXOPEN_ID) 表中有一条记录如下: 理论上第二个SQL应当是可以查询得到一条数据的, 结果却为 Empt ...
随机推荐
- photo型的object转byte[]
IEnumerable en = (IEnumerable) myObject; byte[] myBytes = en.OfType<byte>().ToArray(); TypeCon ...
- ECMAScript 6 新特性-set。const
一.let命令是es6新增的特性,作用与var命令类似,声明变量,不同之处在于声明的变量的作用域为块级作用域.引入let后带来了很多新的特性. 1作用域,es5之前之后函数作用域和全局作用域,let的 ...
- html 常用button事件
<input onclick="document.all.WebBrowser.ExecWB(1,1)" type="button" value=&quo ...
- SQL多表操作
1.多表之间的建表原则 一对多:商品和分类 建表原则:在多的一方添加一个外键,指向一的一方的主键 多对多:老师和学生,学生和课程 建表原则:建立一张中间表,将多对多的关系,拆分成一对多的关系,中间表至 ...
- 【学习】数据的加载、存储与文件格式【pandas】
输入输出通常可以划分为几个大类:读取文本文件和其他更高效的磁盘存储格式,加载数据库中的数据,利用web API操作网络资源 1.读写文本格式的数据 pandas提供了一些用于将表格型数据读取为Data ...
- (7/24) 插件配置之html文件的打包发布
从前面几节到现在,其实我们的项目结构是有问题的,因为我们直接把index.html文件放到了dist文件夹目录下.这肯定是不正确的,应该放到我们src目录下,然后打包到dist目录下,前面为了学习,才 ...
- java学习--修饰符
Java语言提供了很多修饰符,主要分为以下两类: 访问修饰符 非访问修饰符 访问控制修饰符 访问控制修饰符用来修饰类和类内部的成员变量和成员方法,来确定其访问权限 类的访问控制修饰符只有两种 defa ...
- ThinkPHP 整合微信支付 扫码支付 模式二 图文教程
这篇文章主要介绍扫码支付场景二. 目前有两种模式,模式一比模式二稍微复杂点,至于模式一与模式二的具体内容,流程,微信开发文档都有详细介绍,这里就不多说废话,接下来赶紧上教程! [title]下载SDK ...
- Mysql千万级大表优化
Mysql的单张表的最大数据存储量尚没有定论,一般情况下mysql单表记录超过千万以后性能会变得很差.因此,总结一些相关的Mysql千万级大表的优化策略. 1.优化sql以及索引 1.1优化sql 1 ...
- PeopleSoft JobSet Schedule Table
PS_SCHDLDEFNPS_SCHDLITEMPS_SCHDLNODEPARMPS_SCHDLNOTIFYPS_SCHDLMESSAGE 其中,PS_SCHDLDEFN中SCHEDULESTATUS ...