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

问题描述 投票:0回答:3

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

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

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

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

python scrapy multipart
3个回答
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'))

Read more:


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'

0
投票

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

Background

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

Implementation

在撰写本文时,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)

Limitations

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

© www.soinside.com 2019 - 2024. All rights reserved.