python爬虫实现POST request payload形式的请求

时间:2021-05-23

1. 背景

最近在爬取某个站点时,发现在POST数据时,使用的数据格式是request payload,有别于之前常见的 POST数据格式(Form data)。而使用Form data数据的提交方式时,无法提交成功。

1.1. Http请求中Form Data 和 Request Payload的区别

AJAX Post请求中常用的两种传参数的形式:form data 和 request payload

1.1.1. Form data

get请求的时候,我们的参数直接反映在url里面,形式为key1=value1&key2=value2形式,比如:

http://news.baidu.com/ns?word=NBA&tn=news&from=news&cl=2&rn=20&ct=1

而如果是post请求,那么表单参数是在请求体中,也是以key1=value1&key2=value2的形式在请求体中。通过chrome的开发者工具可以看到,如下:

RequestURL:http://127.0.0.1:8080/test/test.doRequest Method:POSTStatus Code:200 OKRequest HeadersAccept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8Accept-Encoding:gzip,deflate,sdchAccept-Language:zh-CN,zh;q=0.8,en;q=0.6AlexaToolbar-ALX_NS_PH:AlexaToolbar/alxg-3.2Cache-Control:max-age=0Connection:keep-aliveContent-Length:25Content-Type:application/x-', 'Content-Type': 'application/json',}# scrapy源码:self.headers.setdefault(b'Content-Type', b'application/x-www-form-urlencoded')print(f"payloadData = {payloadData}")# 这个request并不真正用来调度,去发出请求,因为这种方式构造方式,是无法提交成功的,会返回404错误# 这样构造主要是把查询参数提交出去,在下载中间件部分用request模块下载,用 “payloadFlag” 标记这种requestyield Request(url = postUrl, headers = payloadHeader, meta = {'payloadFlag': True, 'payloadData': payloadData, 'headers': payloadHeader, 'asinInfo': asinInfo}, callback = self.parseAsinSearchFinallyRes, errback = self.error, dont_filter = True )

第二步:在中间件中,用requests模块处理这个请求

# 文件:middlewares.pyclass PayLoadRequestMiddleware: def process_request(self, request, spider): # 如果有的请求是带有payload请求的,在这个里面处理掉 if request.meta.get('payloadFlag', False): print(f"PayLoadRequestMiddleware enter") postUrl = request.url headers = request.meta.get('headers', {}) payloadData = request.meta.get('payloadData', {}) proxy = request.meta['proxy'] proxies = { "http": proxy, "https": proxy, } timeOut = request.meta.get('download_timeout', 25) allow_redirects = request.meta.get('dont_redirect', False) dumpJsonData = json.dumps(payloadData) print(f"dumpJsonData = {dumpJsonData}") # 发现这个居然是个同步 阻塞的过程,太过影响速度了 res = requests.post(postUrl, data=dumpJsonData, headers=headers, timeout=timeOut, proxies=proxies, allow_redirects=allow_redirects) # res = requests.post(postUrl, json=payloadData, headers=header) print(f"responseTime = {datetime.datetime.now()}, res text = {res.text}, statusCode = {res.status_code}") if res.status_code > 199 and res.status_code < 300: # 返回Response,就进入callback函数处理,不会再去下载这个请求 return HtmlResponse(url=request.url, body=res.content, request=request, # 最好根据网页的具体编码而定 encoding='utf-8', status=200) else: print(f"request mode getting page error, Exception = {e}") return HtmlResponse(url=request.url, status=500, request=request)

4.3. 遗留下的问题

scrapy之所以强大,就是因为并发度高。大家都知道,由于Python GIL的原因,导致python无法通过多线程来提高性能。但是至少可以做到下载与解析同步的过程,在下载空档的时候,进行数据的解析,调度等等,这都归功于scrapy采用的异步结构。

但是,我们在中间件中使用requests模块进行网页下载,因为这是个同步过程,所以会阻塞在这个地方,拉低了整个爬虫的效率。

所以,需要根据项目具体的情况,来决定合适的方案。当然这里又涉及到一个新的话题,就是scrapy提供的两种爬取模式:深度优先模式和广度优先模式。如何尽可能最大限度的利用scrapy的并发?在环境不稳定的情形下如何保证尽可能稳定的拿到数据?

深度优先模式和广度优先模式是在settings中设置的。

# 文件: settings.py# DEPTH_PRIORITY(默认值为0)设置为一个正值后,Scrapy的调度器的队列就会从LIFO变成FIFO,因此抓取规则就由DFO(深度优先)变成了BFO(广度优先)DEPTH_PRIORITY = 1, # 广度优先(肯呢个会累积大量的request,累计占有大量的内存,最终数据也在最后一批爬取)

深度优先:DEPTH_PRIORITY = 0

广度优先:DEPTH_PRIORITY = 1

想将这个过程做成异步的,一直没有思路,欢迎大神提出好的想法

以上这篇python爬虫实现POST request payload形式的请求就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章