redis的scan命令的使用(scrapy-redis源码分析之发送POST请求详解)
redis的scan命令的使用
scrapy-redis源码分析之发送POST请求详解1 引言
这段时间在研究美团爬虫,用的是scrapy-redis分布式爬虫框架,奈何scrapy-redis与scrapy框架不同,默认只发送get请求,换句话说,不能直接发送post请求,而美团的数据请求方式是post,网上找了一圈,发现关于scrapy-redis发送post的资料寥寥无几,只能自己刚源码了。
2 美团post需求说明
先来说一说需求,也就是说美团post请求形式。我们以获取某个地理坐标下,所有店铺类别列表请求为例。获取所有店铺类别列表时,我们需要构造一个包含位置坐标经纬度等信息的表单数据,以及为了向下一层parse方法传递的一些必要数据,即meta,然后发起一个post请求。
url:
请求地址,即url是固定的,如下所示:
|
url = 'http://i.waimai.meituan.com/openh5/poi/filterconditions?_=1557367197922' |
url最后面的13位数字是时间戳,实际应用时用time模块生成一下就好了。
表单数据:
|
form_data = { 'initiallat' : '25.618626' , 'initiallng' : '105.644569' , 'actuallat' : '25.618626' , 'actuallng' : '105.644569' , 'geotype' : '2' , 'wm_latitude' : '25618626' , 'wm_longitude' : '105644569' , 'wm_actual_latitude' : '25618626' , 'wm_actual_longitude' : '105644569' } |
meta数据:
meta数据不是必须的,但是,如果你在发送请求时,有一些数据需要向下一层parse方法(解析爬虫返回的response的方法)中传递的话,就可以构造这一数据,然后作为参数传递进request中。
|
meta = { 'lat' : form_data.get( 'initiallat' ), 'lng' : form_data.get( 'initiallng' ), 'lat2' : form_data.get( 'wm_latitude' ), 'lng2' : form_data.get( 'wm_longitude' ), 'province' : '**省' , 'city' : '**市' , 'area' : '**区' } |
3 源码分析
采集店铺类别列表时需要发送怎样一个post请求在上面已经说明了,那么,在scrapy-redis框架中,这个post该如何来发送呢?我相信,打开我这篇博文的读者都是用过scrapy的,用scrapy发送post肯定没问题(重写start_requests方法即可),但scrapy-redis不同,scrapy-redis框架只会从配置好的redis数据库中读取起始url,所以,在scrapy-redis中,就算重写start_requests方法也没用。怎么办呢?我们看看源码。
我们知道,scrapy-redis与scrapy的一个很大区别就是,scrapy-redis不再继承spider类,而是继承redisspider类的,所以,redisspider类源码将是我们分析的重点,我们打开redisspider类,看看有没有类似于scrapy框架中的start_requests、make_requests_from_url这样的方法。redisspider源码如下:
|
class redisspider(redismixin, spider): @classmethod def from_crawler( self , crawler, * args, * * kwargs): obj = super (redisspider, self ).from_crawler(crawler, * args, * * kwargs) obj.setup_redis(crawler) return obj |
很遗憾,在redisspider类中没有找到类似start_requests、make_requests_from_url这样的方法,而且,redisspider的源码也太少了吧,不过,从第一行我们可以发现redisspider继承了redisminxin这个类,所以我猜redisspider的很多功能是从父类继承而来的(拼爹的redisspider)。继续查看redisminxin类源码。redisminxin类源码太多,这里就不将所有源码贴出来了,不过,惊喜的是,在redisminxin中,真找到了类似于start_requests、make_requests_from_url这样的方法,如:start_requests、next_requests、make_request_from_data等。有过scrapy使用经验的童鞋应该都知道,start_requests方法可以说是构造一切请求的起源,没分析scrapy-redis源码之前,谁也不知道scrapy-redis是不是和scrapy一样(后面打断点的方式验证过,确实一样,话说这个验证有点多余,因为源码注释就是这么说的),不过,还是从start_requests开始分析吧。start_requests源码如下:
|
def start_requests( self ): return self .next_requests() |
呵,真简洁,直接把所有任务丢给next_requests方法,继续:
|
def next_requests( self ): """returns a request to be scheduled or none.""" use_set = self .settings.getbool( 'redis_start_urls_as_set' , defaults.start_urls_as_set) fetch_one = self .server.spop if use_set else self .server.lpop # xxx: do we need to use a timeout here? found = 0 # todo: use redis pipeline execution. while found < self .redis_batch_size: # 每次读取的量 data = fetch_one( self .redis_key) # 从redis中读取一条记录 if not data: # queue empty. break req = self .make_request_from_data(data) # 根据从redis中读取的记录,实例化一个request if req: yield req found + = 1 else : self .logger.debug( "request not made from data: %r" , data) if found: self .logger.debug( "read %s requests from '%s'" , found, self .redis_key) |
上面next_requests方法中,关键的就是那个while循环,每一次循环都调用了一个make_request_from_data方法,从函数名可以函数,这个方法就是根据从redis中读取从来的数据,实例化一个request,那不就是我们要找的方法吗?进入make_request_from_data方法一探究竟:
|
def make_request_from_data( self , data): url = bytes_to_str(data, self .redis_encoding) return self .make_requests_from_url(url) # 这是重点,圈起来,要考 |
因为scrapy-redis默认值发送get请求,所以,在这个make_request_from_data方法中认为data只包含一个url,但如果我们要发送post请求,这个data包含的东西可就多了,我们上面美团post请求说明中就说到,至少要包含url、form_data。所以,如果我们要发送post请求,这里必须改,make_request_from_data方法最后调用的make_requests_from_url是scrapy中的spider中的方法,不过,我们也不需要继续往下看下去了,我想诸位都也清楚了,要发送post请求,重写这个make_request_from_data方法,根据传入的data,实例化一个request返回就好了。
4 代码实例
明白上面这些东西后,就可以开始写代码了。修改源码吗?不,不存在的,改源码可不是好习惯。我们直接在我们自己的spider类中重写make_request_from_data方法就好了:
|
from scrapy import formrequest from scrapy_redis.spiders import redisspider class meituanspider(redisspider): """ 此处省略若干行 """ def make_request_from_data( self , data): """ 重写make_request_from_data方法,data是scrapy-redis读取redis中的[url,form_data,meta],然后发送post请求 :param data: redis中都去的请求数据,是一个list :return: 一个formrequest对象 """ data = json.loads(data) url = data.get( 'url' ) form_data = data.get( 'form_data' ) meta = data.get( 'meta' ) return formrequest(url = url, formdata = form_data, meta = meta, callback = self .parse) def parse( self , response): pass |
搞清楚原理之后,就是这么简单。万事俱备,只欠东风——将url,form_data,meta存储到redis中。另外新建一个模块实现这一部分功能:
|
def push_start_url_data(request_data): """ 将一个完整的request_data推送到redis的start_url列表中 :param request_data: {'url':url, 'form_data':form_data, 'meta':meta} :return: """ r.lpush( 'meituan:start_urls' , request_data) if __name__ = = '__main__' : url = 'http://i.waimai.meituan.com/openh5/poi/filterconditions?_=1557367197922' form_data = { 'initiallat' : '25.618626' , 'initiallng' : '105.644569' , 'actuallat' : '25.618626' , 'actuallng' : '105.644569' , 'geotype' : '2' , 'wm_latitude' : '25618626' , 'wm_longitude' : '105644569' , 'wm_actual_latitude' : '25618626' , 'wm_actual_longitude' : '105644569' } meta = { 'lat' : form_data.get( 'initiallat' ), 'lng' : form_data.get( 'initiallng' ), 'lat2' : form_data.get( 'wm_latitude' ), 'lng2' : form_data.get( 'wm_longitude' ), 'province' : '**省' , 'city' : '*市' , 'area' : '**区' } request_data = { 'url' : url, 'form_data' : form_data, 'meta' : meta } push_start_url_data(json.dumps(request_data)) |
在启动scrapy-redis之前,运行一下这一模块即可。如果有很多poi(地理位置兴趣点),循环遍历每一个poi,生成request_data,push到redis中。这一循环功能就你自己写吧。
5 总结
没有什么是撸一遍源码解决不了的,如果有,就再撸一遍!
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对开心学习网的支持。
原文链接:http://www.cnblogs.com/chenhuabin/p/10867285.html
- postgresql docker 宿主机(本地Docker安装Postgres 12 + pgadmin的方法 支持Apple M1)
- python响应处理post请求(Python3模拟curl发送post请求操作示例)
- iframe跨域原理(详解使用postMessage解决iframe跨域通信问题)
- redis的scan命令的使用(scrapy-redis源码分析之发送POST请求详解)
- react和antd项目教程(React引入antd-mobile+postcss搭建移动端)
- mysql所有用户命令(MySQL用户管理与PostgreSQL用户管理的区别说明)
- iframe向子页面发送消息(使用postMessage让 iframe自适应高度的方法示例)
- nginx日志请求状态(Nginx设置日志打印post请求参数的方法)
- h5 video支持格式(H5 video poster属性设置视频封面的方法)
- html5 postMessage(html5关于外链嵌入页面通信问题postMessage解决跨域通信)
- docker的postgres镜像包(Docker中运行PostgreSQL并推荐几款连接工具)
- php获取post请求来源域名(在PHP中实现使用Guzzle执行POST和GET请求)
- redis新增数据分页(Scrapy-Redis结合POST请求获取数据的方法示例)
- docker启动sqlserver(用docker运行postgreSQL的方法步骤)
- yii2支持php7.2吗(Yii框架通过请求组件处理get,post请求的方法分析)
- 如何用postman做接口测试(基于postman实现http接口测试过程解析)
- 得这个 难治病 的人太多了,300个人赶到杭州商量怎么办(得这个难治病的人太多了)
- 经度,世界时间腕表的灵魂(世界时间腕表的灵魂)
- 阿里最新财报公布 三季度营收增长3 ,将增加150亿美元回购额度 在美股价小涨(阿里最新财报公布)
- 赵薇时胖时瘦 最近变美少女 原因在这里 躺着就变瘦(赵薇时胖时瘦最近变美)
- 学会这26种姿势,你就可以和兵哥哥切磋了(你就可以和兵哥哥切磋了)
- 吴彦祖陈冠希 恩怨 ,失去曾让他流泪的女友,终遇走过18年真爱(吴彦祖陈冠希恩怨)
热门推荐
- 内网nginx配置https详解(Nginx如何配置Http、Https、WS、WSS的方法步骤)
- vue插槽实例(Vue中插槽slot的使用方法与应用场景详析)
- SQL Server将数据导出到SQL脚本文件
- sqlserver设置自动备份的注意事项(SQL server 定时自动备份数据库的图文方法)
- Ext.query与Ext.select 的用法
- oracle中decode函数
- sql server表字段数据类型(SQL Server数据库中伪列及伪列的含义详解)
- 当前云服务器设置方法(如何使用云服务器?四招教你玩转)
- css样式中实现3d效果(利用纯CSS实现动态的文字效果实例)
- mongodb索引的优化
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9