use*_*419 4 python multipartform-data python-requests gmail-api
我正在与gmail api交谈,并希望批量处理请求.他们有一个友好的指南,https://developers.google.com/gmail/api/guides/batch,这表明我应该能够使用multipart/mixed并包含不同的网址.
我正在使用Python和Requests库,但我不确定如何发布不同的URL.像这样的答案如何在python中发送带有请求的"multipart/form-data"?不要提及更改该部分的选项.
我该怎么做呢?
Tim*_*o D 10
不幸的是,请求不支持在其API中使用multipart/mixed.这已在几个GitHub问题(#935和#1081)中提出,但目前尚无此更新.如果您在请求源中搜索"混合" 并获得零结果,这也变得非常清楚:(
现在,您有多种选择,具体取决于您希望使用多少Python和第三方库.
现在,这个问题最明显的答案是使用谷歌在这里提供的官方Python API .它附带了一个HttpBatchRequest类,可以处理您需要的批处理请求.本指南中详细介绍了这一点.
基本上,您创建一个HttpBatchRequest对象并将所有请求添加到它.然后图书馆将所有内容放在一起(取自上面的指南):
batch = BatchHttpRequest()
batch.add(service.animals().list(), callback=list_animals)
batch.add(service.farmers().list(), callback=list_farmers)
batch.execute(http=http)
Run Code Online (Sandbox Code Playgroud)
现在,如果由于某种原因你不能或不会使用官方谷歌库,你将不得不自己构建请求体的一部分.
正如我已经提到的,请求没有正式支持multipart/mixed.但这并不意味着我们不能"强迫"它.创建Request对象时,我们可以使用该files参数来提供多部分数据.
files是一个接受此格式的4元组值的字典:(filename,file_object,content_type,headers).文件名可以为空.现在我们需要将Request对象转换为文件(类似)对象.我写了一个小方法,涵盖了Google示例中的基本示例.它部分受到Google在其Python库中使用的内部方法的启发:
import requests
from email.mime.multipart import MIMEMultipart
from email.mime.nonmultipart import MIMENonMultipart
BASE_URL = 'http://www.googleapis.com/batch'
def serialize_request(request):
'''Returns the string representation of the request'''
mime_body = ''
prepared = request.prepare()
# write first line (method + uri)
if request.url.startswith(BASE_URL):
mime_body = '%s %s\r\n' % (request.method, request.url[len(BASE_URL):])
else:
mime_body = '%s %s\r\n' % (request.method, request.url)
part = MIMENonMultipart('application', 'http')
# write headers (if possible)
for key, value in prepared.headers.iteritems():
mime_body += '%s: %s\r\n' % (key, value)
if getattr(prepared, 'body', None) is not None:
mime_body += '\r\n' + prepared.body + '\r\n'
return mime_body.encode('utf-8').lstrip()
Run Code Online (Sandbox Code Playgroud)
此方法将requests.Request对象转换为UTF-8编码的字符串,以后可以将其用作MIMENonMultipart对象的有效负载,即不同的多部分.
现在,为了生成实际的批处理请求,我们首先需要将(Google API)请求列表压缩到请求 lib 的files字典中.以下方法将获取对象列表,将每个对象转换为MIMENonMultipart,然后返回符合字典结构的字典:requests.Requestfiles
import uuid
def prepare_requests(request_list):
message = MIMEMultipart('mixed')
output = {}
# thanks, Google. (Prevents the writing of MIME headers we dont need)
setattr(message, '_write_headers', lambda self: None)
for request in request_list:
message_id = new_id()
sub_message = MIMENonMultipart('application', 'http')
sub_message['Content-ID'] = message_id
del sub_message['MIME-Version']
sub_message.set_payload(serialize_request(request))
# remove first line (from ...)
sub_message = str(sub_message)
sub_message = sub_message[sub_message.find('\n'):]
output[message_id] = ('', str(sub_message), 'application/http', {})
return output
def new_id():
# I am not sure how these work exactly, so you will have to adapt this code
return '<item%s:12930812@barnyard.example.com>' % str(uuid.uuid4())[-4:]
Run Code Online (Sandbox Code Playgroud)
最后,我们需要将Content-Type从multipart/form-data更改为multipart/mixed,并从每个请求部分中删除Content-Disposition和Content-Type标头.这些是我们通过请求生成的,不能被files字典覆盖.
import re
def finalize_request(prepared):
# change to multipart/mixed
old = prepared.headers['Content-Type']
prepared.headers['Content-Type'] = old.replace('multipart/form-data', 'multipart/mixed')
# remove headers at the start of each boundary
prepared.body = re.sub(r'\r\nContent-Disposition: form-data; name=.+\r\nContent-Type: application/http\r\n', '', prepared.body)
Run Code Online (Sandbox Code Playgroud)
我已尽力使用批处理指南中的Google示例对此进行测试:
sheep = {
"animalName": "sheep",
"animalAge": "5",
"peltColor": "green"
}
commands = []
commands.append(requests.Request('GET', 'http://www.googleapis.com/batch/farm/v1/animals/pony'))
commands.append(requests.Request('PUT', 'http://www.googleapis.com/batch/farm/v1/animals/sheep', json=sheep, headers={'If-Match': '"etag/sheep"'}))
commands.append(requests.Request('GET', 'http://www.googleapis.com/batch/farm/v1/animals', headers={'If-None-Match': '"etag/animals"'}))
files = prepare_requests(commands)
r = requests.Request('POST', 'http://www.googleapis.com/batch', files=files)
prepared = r.prepare()
finalize_request(prepared)
s = requests.Session()
s.send(prepared)
Run Code Online (Sandbox Code Playgroud)
最终的请求应该与Google在其"配料"指南中提供的内容足够接近:
POST http://www.googleapis.com/batch
Content-Length: 1006
Content-Type: multipart/mixed; boundary=a21beebd15b74be89539b137bbbc7293
--a21beebd15b74be89539b137bbbc7293
Content-Type: application/http
Content-ID: <item8065:12930812@barnyard.example.com>
GET /farm/v1/animals
If-None-Match: "etag/animals"
--a21beebd15b74be89539b137bbbc7293
Content-Type: application/http
Content-ID: <item5158:12930812@barnyard.example.com>
GET /farm/v1/animals/pony
--a21beebd15b74be89539b137bbbc7293
Content-Type: application/http
Content-ID: <item0ec9:12930812@barnyard.example.com>
PUT /farm/v1/animals/sheep
Content-Length: 63
Content-Type: application/json
If-Match: "etag/sheep"
{"animalAge": "5", "animalName": "sheep", "peltColor": "green"}
--a21beebd15b74be89539b137bbbc7293--
Run Code Online (Sandbox Code Playgroud)
最后,我强烈推荐官方谷歌库,但如果你不能使用它,你将不得不即兴发挥:)
免责声明:我实际上并没有尝试将此请求发送到Google API端点,因为身份验证过程太麻烦了.我只是想尽可能接近批处理指南中描述的HTTP请求.\ r和\n行结尾可能存在一些问题,具体取决于Google终端的严格程度.
资料来源:
| 归档时间: |
|
| 查看次数: |
2596 次 |
| 最近记录: |