Python Scrapy在发布请求后覆盖内容类型为multipart / form-data

时间:2014-11-15 14:57:50

标签: python scrapy multipart

尝试使用scrapy来抓取一个网站,该网站将其帖子请求编码为" multipart / form-data"出于某种原因。

有没有办法使用" application / x-www-form-urlencoded"来覆盖scrapy的默认发布行为?

看起来该网站没有响应蜘蛛,因为它希望使用" multipart / form-data"来发布请求。

尝试过对表单变量进行多部分编码,但是看过使用wireshark,scrapy仍然错误地设置了标头,无论这种编码如何。

3 个答案:

答案 0 :(得分:0)

只需使用scrapy.http.FormRequest代替scrapy.Request,在formdata参数中传递参数。

示例代码:

import scrapy
from scrapy.http import FormRequest

class MySpider(scrapy.Spider):
    # ...
    def start_requests(self):
        yield FormRequest(some_post_url,
                          formdata=dict(param1='value1', param2='value2'))

了解更多:

答案 1 :(得分:0)

您可以使用此MultipartRequest:

代码:

from scrapy import Request
from StringIO import StringIO

import mimetypes
import random


class MultipartRequest(Request):

    def __init__(self, *args, **kwargs):
        formdata = kwargs.pop('formdata', None)
        files = kwargs.pop('files', None)

        kwargs['method'] = 'POST'

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

        self._boundary = '-----------------------------{0}'.format(random.random() * 1e10)

        if formdata or files:
            buffer = StringIO()

            if formdata:
                self._write_formdata(formdata, buffer)

            if files:
                self._write_files(files, buffer)

            self.headers['Content-Type'] = 'multipart/form-data; boundary={0}'.format(self._boundary)
            self._set_body(buffer.getvalue())


    def _write_formdata(self, formdata, buffer):
        for key, value in formdata.iteritems():
            buffer.write('--{0}\r\n'.format(self._boundary))
            buffer.write('Content-Disposition: form-data; name="{0}"\r\n'.format(key))
            buffer.write('\r\n')
            buffer.write('{0}\r\n'.format(str(value).encode('utf-8')))

    def _write_files(self, files, buffer):
        for key, filedesc, fd in files:
            buffer.write('--{0}\r\n'.format(self._boundary))
            buffer.write('Content-Disposition: form-data; name="{0}"; filename="{1}"\r\n'.format(key, filedesc))
            buffer.write('Content-Type: {0}\r\n'.format(self.get_content_type(filedesc)))
            buffer.write('\r\n')

            if isinstance(fd, basestring):
                buffer.write(fd)
            else:
                buffer.write(fd.getvalue())

            buffer.write('\r\n')
            buffer.write('--{0}--\r\n'.format(self._boundary))
            buffer.write('\r\n')


    def get_content_type(self, filepath):
        return mimetypes.guess_type(filepath)[0] or 'application/octet-stream'

答案 2 :(得分:0)

我花了更多的时间在这上面比我想要的更多,所以这里是scrapy中的情况的概述。

背景

multipart/form-data内容类型具有您需要遵循的特定编码类型。您可以在发送此类请求时查看此示例,检查任何主要浏览器的Network中的Developer tools标签。以下是multipart/form-data请求正文/有效负载

的示例
-----------------------------9128252932315252835063017
Content-Disposition: form-data; name="username"

tibor.udvari
-----------------------------9128252932315252835063017
Content-Disposition: form-data; name="passwd"

secret
-----------------------------9128252932315252835063017
Content-Disposition: form-data; name="_mode"

edit
-----------------------------9128252932315252835063017--

您还必须在标头中设置相应的Content-TypeContent-Length

实施

在撰写本文时,Scrapy 1.4没有便利的方式来发送multipart/form-data个请求。您必须自己构建一个帖子请求。

首先,您需要从数据中构建请求,我使用MultipartEncoder requests-toolbelt类来执行此操作。

formdata = {'username': 'example', 'password': 'example'}
me = MultipartEncoder(fields=formdata)
me_boundary = me.boundary[2:]  #need this in headers
me_length = me.len             #need this in headers
me_body = me.to_string()       #contains the request body 

下一步是创建带有有效标头的请求

headers = {
        'Content-Type': 'multipart/form-data; charset=utf-8; boundary=' + me_boundary,
        'Content-Length': me_length
}
r = scrapy.Request(url='https://example.com', method='POST', body=me_body, headers=headers)

发送此请求应该产生有效的响应,如果它以某种方式格式错误,您应该得到服务器响应,例如"上传错误"。

假设您正在使用scrapy shell,您现在可以发送请求

fetch(r)

限制

我只使用文本输入进行了测试,处理文件可能需要更多步骤。