python使用scrapy发送post请求的坑


Posted in Python onSeptember 04, 2018

使用requests发送post请求

先来看看使用requests来发送post请求是多少好用,发送请求

Requests 简便的 API 意味着所有 HTTP 请求类型都是显而易见的。例如,你可以这样发送一个 HTTP POST 请求:

>>>r = requests.post('http://httpbin.org/post', data = {'key':'value'})

使用data可以传递字典作为参数,同时也可以传递元祖

>>>payload = (('key1', 'value1'), ('key1', 'value2'))
>>>r = requests.post('http://httpbin.org/post', data=payload)
>>>print(r.text)
{
 ...
 "form": {
  "key1": [
   "value1",
   "value2"
  ]
 },
 ...
}

传递json是这样

>>>import json

>>>url = 'https://api.github.com/some/endpoint'
>>>payload = {'some': 'data'}

>>>r = requests.post(url, data=json.dumps(payload))

2.4.2 版的新加功能:

>>>url = 'https://api.github.com/some/endpoint'
>>>payload = {'some': 'data'}

>>>r = requests.post(url, json=payload)

也就是说,你不需要对参数做什么变化,只需要关注使用data=还是json=,其余的requests都已经帮你做好了。

使用scrapy发送post请求

通过源码可知scrapy默认发送的get请求,当我们需要发送携带参数的请求或登录时,是需要post、请求的,以下面为例

from scrapy.spider import CrawlSpider
from scrapy.selector import Selector
import scrapy
import json
class LaGou(CrawlSpider):
  name = 'myspider'
  def start_requests(self):
    yield scrapy.FormRequest(
      url='https://www.******.com/jobs/positionAjax.json?city=%E5%B9%BF%E5%B7%9E&needAddtionalResult=false',
      formdata={
        'first': 'true',#这里不能给bool类型的True,requests模块中可以
        'pn': '1',#这里不能给int类型的1,requests模块中可以
        'kd': 'python'
      },这里的formdata相当于requ模块中的data,key和value只能是键值对形式
      callback=self.parse
    )
  def parse(self, response):
    datas=json.loads(response.body.decode())['content']['positionResult']['result']
    for data in datas:
      print(data['companyFullName'] + str(data['positionId']))

官方推荐的 Using FormRequest to send data via HTTP POST

return [FormRequest(url="http://www.example.com/post/action",
          formdata={'name': 'John Doe', 'age': '27'},
          callback=self.after_post)]

这里使用的是FormRequest,并使用formdata传递参数,看到这里也是一个字典。

但是,超级坑的一点来了,今天折腾了一下午,使用这种方法发送请求,怎么发都会出问题,返回的数据一直都不是我想要的

return scrapy.FormRequest(url, formdata=(payload))

在网上找了很久,最终找到一种方法,使用scrapy.Request发送请求,就可以正常的获取数据。

return scrapy.Request(url, body=json.dumps(payload), method='POST', headers={'Content-Type': 'application/json'},)

参考:Send Post Request in Scrapy

my_data = {'field1': 'value1', 'field2': 'value2'}
request = scrapy.Request( url, method='POST', 
             body=json.dumps(my_data), 
             headers={'Content-Type':'application/json'} )

FormRequest 与 Request 区别

在文档中,几乎看不到差别,

The FormRequest class adds a new argument to the constructor. The remaining arguments are the same as for the Request class and are not documented here.
Parameters: formdata (dict or iterable of tuples) ? is a dictionary (or iterable of (key, value) tuples) containing HTML Form data which will be url-encoded and assigned to the body of the request.

说FormRequest新增加了一个参数formdata,接受包含表单数据的字典或者可迭代的元组,并将其转化为请求的body。并且FormRequest是继承Request的

class FormRequest(Request):

  def __init__(self, *args, **kwargs):
    formdata = kwargs.pop('formdata', None)
    if formdata and kwargs.get('method') is None:
      kwargs['method'] = 'POST'

    super(FormRequest, self).__init__(*args, **kwargs)

    if formdata:
      items = formdata.items() if isinstance(formdata, dict) else formdata
      querystr = _urlencode(items, self.encoding)
      if self.method == 'POST':
        self.headers.setdefault(b'Content-Type', b'application/x-www-form-urlencoded')
        self._set_body(querystr)
      else:
        self._set_url(self.url + ('&' if '?' in self.url else '?') + querystr)
      ###


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)

最终我们传递的{‘key': ‘value', ‘k': ‘v'}会被转化为'key=value&k=v' 并且默认的method是POST,再来看看Request

class Request(object_ref):

  def __init__(self, url, callback=None, method='GET', headers=None, body=None,
         cookies=None, meta=None, encoding='utf-8', priority=0,
         dont_filter=False, errback=None, flags=None):

    self._encoding = encoding # this one has to be set first
    self.method = str(method).upper()

默认的方法是GET,其实并不影响。仍然可以发送post请求。这让我想起来requests中的request用法,这是定义请求的基础方法。

def request(method, url, **kwargs):
  """Constructs and sends a :class:`Request <Request>`.

  :param method: method for the new :class:`Request` object.
  :param url: URL for the new :class:`Request` object.
  :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`.
  :param data: (optional) Dictionary or list of tuples ``[(key, value)]`` (will be form-encoded), bytes, or file-like object to send in the body of the :class:`Request`.
  :param json: (optional) json data to send in the body of the :class:`Request`.
  :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`.
  :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`.
  :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': file-tuple}``) for multipart encoding upload.
    ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 3-tuple ``('filename', fileobj, 'content_type')``
    or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, where ``'content-type'`` is a string
    defining the content type of the given file and ``custom_headers`` a dict-like object containing additional headers
    to add for the file.
  :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth.
  :param timeout: (optional) How many seconds to wait for the server to send data
    before giving up, as a float, or a :ref:`(connect timeout, read
    timeout) <timeouts>` tuple.
  :type timeout: float or tuple
  :param allow_redirects: (optional) Boolean. Enable/disable GET/OPTIONS/POST/PUT/PATCH/DELETE/HEAD redirection. Defaults to ``True``.
  :type allow_redirects: bool
  :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy.
  :param verify: (optional) Either a boolean, in which case it controls whether we verify
      the server's TLS certificate, or a string, in which case it must be a path
      to a CA bundle to use. Defaults to ``True``.
  :param stream: (optional) if ``False``, the response content will be immediately downloaded.
  :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
  :return: :class:`Response <Response>` object
  :rtype: requests.Response

  Usage::

   >>> import requests
   >>> req = requests.request('GET', 'http://httpbin.org/get')
   <Response [200]>
  """

  # By using the 'with' statement we are sure the session is closed, thus we
  # avoid leaving sockets open which can trigger a ResourceWarning in some
  # cases, and look like a memory leak in others.
  with sessions.Session() as session:
    return session.request(method=method, url=url, **kwargs)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
使用Python的urllib2模块处理url和图片的技巧两则
Feb 18 Python
解析Mac OS下部署Pyhton的Django框架项目的过程
May 03 Python
利用python模拟实现POST请求提交图片的方法
Jul 25 Python
详解如何利用Cython为Python代码加速
Jan 27 Python
Python爬虫实现全国失信被执行人名单查询功能示例
May 03 Python
python使用matplotlib画饼状图
Sep 25 Python
在pandas多重索引multiIndex中选定指定索引的行方法
Nov 16 Python
pandas使用apply多列生成一列数据的实例
Nov 28 Python
Python使用pandas和xlsxwriter读写xlsx文件的方法示例
Apr 09 Python
一篇文章弄懂Python中的可迭代对象、迭代器和生成器
Aug 12 Python
Django自带的加密算法及加密模块详解
Dec 03 Python
python3爬虫中多线程的优势总结
Nov 24 Python
解决win64 Python下安装PIL出错问题(图解)
Sep 03 #Python
Python全局变量与局部变量区别及用法分析
Sep 03 #Python
Python wxPython库Core组件BoxSizer用法示例
Sep 03 #Python
深入浅析Python中list的复制及深拷贝与浅拷贝
Sep 03 #Python
Python wxPython库使用wx.ListBox创建列表框示例
Sep 03 #Python
Python wxPython库消息对话框MessageDialog用法示例
Sep 03 #Python
Python中关键字global和nonlocal的区别详解
Sep 03 #Python
You might like
基于PHP生成静态页的实现方法
2013/05/10 PHP
laravel框架中表单请求类型和CSRF防护实例分析
2019/11/23 PHP
JavaScript 数组运用实现代码
2010/04/13 Javascript
jquery中常用的SET和GET$(”#msg”).html循环介绍
2013/10/09 Javascript
使用javaScript动态加载Js文件和Css文件
2015/10/24 Javascript
基于jquery实现的仿优酷图片轮播特效代码
2016/01/13 Javascript
JavaScript类型系统之布尔Boolean类型详解
2016/06/26 Javascript
vue router2.0二级路由的简单使用
2017/07/05 Javascript
浅谈es6语法 (Proxy和Reflect的对比)
2017/10/24 Javascript
JS 实现缓存算法的示例(FIFO/LRU)
2018/03/20 Javascript
angular4应用中输入的最小值和最大值的方法
2019/05/17 Javascript
微信小程序实现写入读取缓存详解
2019/08/30 Javascript
浅谈layer的Icon样式以及一些常用的layer窗口使用方法
2019/09/11 Javascript
微信小程序使用蓝牙小插件
2019/09/23 Javascript
vue倒计时刷新页面不会从头开始的解决方法
2020/03/03 Javascript
[04:22]DSPL第二期精彩集锦:残血反杀!
2014/12/10 DOTA
python提取内容关键词的方法
2015/03/16 Python
使用Turtle画正螺旋线的方法
2017/09/22 Python
python修改txt文件中的某一项方法
2018/12/29 Python
Python3中urlencode和urldecode的用法详解
2019/07/23 Python
用Python徒手撸一个股票回测框架搭建【推荐】
2019/08/05 Python
python内存管理机制原理详解
2019/08/12 Python
django框架cookie和session用法实例详解
2019/12/10 Python
Python连接Hadoop数据中遇到的各种坑(汇总)
2020/04/14 Python
python3代码输出嵌套式对象实例详解
2020/12/03 Python
Python环境搭建过程从安装到Hello World
2021/02/05 Python
使用CSS3编写灰阶滤镜来制作黑白照片效果的方法
2016/05/09 HTML / CSS
h5封装下拉刷新
2020/08/25 HTML / CSS
美国杰西潘尼官网:JCPenney
2019/06/12 全球购物
迪士尼西班牙官方网上商店:ShopDisney西班牙
2020/02/02 全球购物
J2EE模式面试题
2016/10/11 面试题
校庆团日活动总结
2014/08/28 职场文书
企业开业庆典答谢词
2015/01/20 职场文书
邀请书模板
2015/02/02 职场文书
分析设计模式之模板方法Java实现
2021/06/23 Java/Android
python自动化八大定位元素讲解
2021/07/09 Python