由于某种原因,尝试使用scrapy来抓取一个网站,该网站将其帖子请求编码为“multipart / form-data”。
有没有办法覆盖scrapy使用“application / x-www-form-urlencoded”发布的默认行为?
看起来网站没有响应蜘蛛,因为它希望使用“multipart / form-data”发布请求。
尝试过对表单变量进行多部分编码,但是看过使用wireshark,scrapy仍然错误地设置了标题,而不管这种编码如何。
只需使用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'))
您可以使用此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'
我花了更多的时间在这上面比我想要的更多,所以这里是我在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-Type
和Content-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)
我只用文本输入测试了这个,处理文件可能需要更多步骤。