spider使用post请求总结

1. 简介

最近有做APP相关的爬虫,在使用scrapy框架调取post请求时遇到不少意外情况,现在做一下总结。

2.问题总结

在HTTP协议中,post提交的数据必须放在消息主体中,但协议中并没有规定必须使用什么编码方式,从而导致提交方式的不同,服务端根据请求头中的Content-Type字段来获取请求中的消息主体是用何种方式进行编码,从而对消息主体进行解析。
具体的编码方式包括如下:

  • application/x-www-form-urlencoded # 以form表单形式提交数据,最常见也是大家最熟悉的
  • application/json # 以json串提交数据
  • multipart/form-data # 上传文件

下面针对不同的POST请求分别用requests和scrapy做发送

1.提交Form表单

Form表单的post请求一般用于网站登录,用来提交用户名和密码,以Form表单形式发送post请求,只需要将请求的参数构造成一个字典,然后传给相应的参数就可以了。

requests模块请求
url = ‘http://baidu/login'
data = {'name': 'zhangsan', 'password': '123456'}
res = requests.post(url, data=data}
print(res.text)
使用scrapy模块请求

Form表单的post请求在scrapy框架中使用scrapy.FormRequest向目标网站提交数据。

# header信息
headers = {
    'Host': 'www.baidu.com',
    'Referer': 'http://www.baidu.com/',
     'User-Agent': 'okhttp/3.12.0',
}

# 表单需要提交的数据
form_data = {'name': 'John Doe', 'age': '27'}

# 自定义信息,向下层响应(response)传递下去
customer_data = {'key1': 'value1', 'key2': 'value2'}
# scrapy post提交代码
yield scrapy.FormRequest(url = "http://www.baidu.com/post/action",
                         headers = headers,
                         method = 'POST',             
                         formdata = form_data,       # 表单提交的数据
                         meta = customer_data,       # 自定义,向response传递数据
                         callback = self.parse,
                         errback = self.error_handle,
                         # 如果需要多次提交表单,且url一样,那么就必须加此参数dont_filter,防止被当成重复网页过滤掉了
                         dont_filter = True     
                         )

上面是正常的post提交,今天遇到的问题是表单提交数据内嵌了字典的形式:
在这里插入图片描述
内嵌字典的形式刚开始的写法是这样的:
在这里插入图片描述
这样写把头部信息写进去,请求的是一个400的信息和一个400的页面,废了好长时间,最终找到了问题所在。
首先在请求头信息里有一个Content-Length的字段,这个字段的长度本来以为是固定不变的,但看返回信息中如果带着这个Content-Length的字段,请求头里就会有两个Content-Length的字段,这样就表明了这个字段是不需要我们填进去的,Content-Length的字段是根据Form表单里的信息会自动生成在请求头里。而且会发现两个Content-Length的字段的长度不一致,继续研究就会发现我们在传From表单数据时,表单信息必须不能自动分行,且在内嵌字典里不能有空格存在。后来传递的头部信息是这样的:
在这里插入图片描述
传递的表单信息是这样的:
在这里插入图片描述
而且我们在传的时候应该讲内嵌字典转换成普通的字符串,这是因为在scrapy源码中有一个对字典的value进行to_bytes 编码的过程,详细如下:

# 第一阶段: 字典分解为items
        if formdata:
            items = formdata.items() if isinstance(formdata, dict) else formdata
            querystr = _urlencode(items, self.encoding)

# 第二阶段: 对value,调用 to_bytes 编码
def _urlencode(seq, enc):
    values = [(to_bytes(k, enc), to_bytes(v, enc))
              for k, vs in seq
              for v in (vs if is_listlike(vs) else [vs])]
    return urlencode(values, doseq=1)

# 第三阶段: 执行 to_bytes ,参数要求是bytes, str
def to_bytes(text, encoding=None, errors='strict'):
    """Return the binary representation of `text`. If `text`
    is already a bytes object, return it as-is."""
    if isinstance(text, bytes):
        return text
    if not isinstance(text, six.string_types):
        raise TypeError('to_bytes must receive a unicode, str or bytes '
                        'object, got %s' % type(text).__name__)

formdata的参数值必须是Unicode、str、bytes object 不能是整数。

2.提交Json串

对于提交json串,主要是发送ajax请求,动态加载数据。

requests模块请求

错误写法

import requests

url = "http://jinbao.pinduoduo.com/network/api/common/goodsList"
data ={"pageSize":60,"pageNumber":1,"withCoupon":0,"sortType":0}
headers = {
    'Content-Type':'application/json; charset=UTF-8',
    'Host':'jinbao.pinduoduo.com',
    'Origin':'http://jinbao.pinduoduo.com',
    'Referer':'http://jinbao.pinduoduo.com/',
    'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Mobile Safari/537.36',
    'Accept': 'application/json, text/javascript, */*; q=0.01',
    }
r = requests.post(url=url,data =data,headers=headers)
print(r.text)

打印的内容如下:

{"success":false,"errorCode":4000000,"errorMsg":"System Error","result":null}

即使写上了 ‘Content-Type’:‘application/json; charset=UTF-8’ ,返回依然出错了,原因就在于 你的请求实体的格式错了,服务端无法解码。

正确写法1:

正确代码是把data进行json编码,再发送。代码如下:

r = requests.post(url=url,data=json.dumps(data),headers=headers)  # 利用 json 对 字典序列化

正确写法2:
requests 提供了一个json参数,自动使用json方式发送,而且在请求头中也不用显示声明’Content-Type’:‘application/json; charset=UTF-8’ ,代码如下:

import requests

url = "http://jinbao.pinduoduo.com/network/api/common/goodsList"
data ={"pageSize":60,"pageNumber":1,"withCoupon":0,"sortType":0}
headers = {
    'Host':'jinbao.pinduoduo.com',
    'Origin':'http://jinbao.pinduoduo.com',
    'Referer':'http://jinbao.pinduoduo.com/',
    'User-Agent':'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Mobile Safari/537.36',
}
r = requests.post(url=url,json =data,headers=headers)  # 直接把字典传给 requests.post() 的 json 参数
print(r.text)
使用scrapy模块请求

在这里插入图片描述

3.上传文件

上传文件在爬虫中使用的不多,Content-Type类型为multipart/form-data,以multipart形式发送post请求,只需将一文件传给 requests.post() 的 files参数 即可。还是以 http://httpbin.org/post 为例,代码如下:

url = 'http://httpbin.org/post'
files = {'file': open('upload.txt', 'rb')}
r = requests.post(url, files=files  # 文件传给 requests.post() 的 files 参数
print(r.text)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值