如何批量发送包含多个 url 的多部分 html 帖子?

2023-11-21

我正在与 gmail api 交谈,并希望对请求进行批处理。他们在这里有一个友好的指南,https://developers.google.com/gmail/api/guides/batch,这表明我应该能够使用多部分/混合并包含不同的网址。

我正在使用 Python 和 Requests 库,但不确定如何发出不同的 url。类似这样的回答如何在python中发送带有请求的“multipart/form-data”?不要提及更改该部分的选项。

我该怎么做呢?


很遗憾,requests他们的 API 不支持 multipart/mixed。这已经在几个 GitHub 问题中提出了建议(#935 and #1081),但目前还没有任何更新。如果您在搜索中搜索“混合”,这一点也会变得非常清楚requests来源并得到零结果:(

现在您有多种选择,具体取决于您想要使用 Python 和第 3 方库的程度。

谷歌API客户端

现在,这个问题最明显的答案是使用 Google 提供的官方 Python APIhere。它带有一个HttpBatchRequest可以处理您需要的批量请求的类。这详细记录在本指南.

本质上,您创建了一个HttpBatchRequest对象并将您的所有请求添加到其中。然后,图书馆会将所有内容放在一起(取自上面的指南):

batch = BatchHttpRequest()
batch.add(service.animals().list(), callback=list_animals)
batch.add(service.farmers().list(), callback=list_farmers)
batch.execute(http=http)

现在,如果出于某种原因您不能或不会使用官方 Google 库,您将必须自己构建请求主体的部分内容。

请求+电子邮件.mime

正如我已经提到的,requests不正式支持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()

该方法将改变一个requests.Request对象转换为 UTF-8 编码的字符串,稍后可以将其用作负载MIMENonMultipart对象,即不同的多部分。

现在,为了生成实际的批量请求,我们首先需要将(Google API)请求列表压缩到files词典为requests库。以下方法将获取一个列表requests.Request对象,将每个对象转换为 MIMENonMultipart,然后返回符合该结构的字典files字典:

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:[email protected]>' % str(uuid.uuid4())[-4:]

最后,我们需要将 Content-Type 更改为多部分/表单数据 to 多部分/混合并从每个请求部分中删除 Content-Disposition 和 Content-Type 标头。这些我们生成的requests并且不能被覆盖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)

我已尽力使用批处理指南中的 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)

由此产生的请求应该足够接近 Google 在其批处理指南中提供的内容:

POST http://www.googleapis.com/batch
Content-Length: 1006
Content-Type: multipart/mixed; boundary=a21beebd15b74be89539b137bbbc7293

--a21beebd15b74be89539b137bbbc7293

Content-Type: application/http
Content-ID: <item8065:[email protected]>

GET /farm/v1/animals
If-None-Match: "etag/animals"

--a21beebd15b74be89539b137bbbc7293

Content-Type: application/http
Content-ID: <item5158:[email protected]>

GET /farm/v1/animals/pony

--a21beebd15b74be89539b137bbbc7293

Content-Type: application/http
Content-ID: <item0ec9:[email protected]>

PUT /farm/v1/animals/sheep
Content-Length: 63
Content-Type: application/json
If-Match: "etag/sheep"

{"animalAge": "5", "animalName": "sheep", "peltColor": "green"}

--a21beebd15b74be89539b137bbbc7293--

最后,我强烈推荐官方的 Google 库,但如果你不能使用它,你将不得不临时凑合:)

免责声明:我实际上并没有尝试将此请求发送到 Google API 端点,因为身份验证过程太麻烦了。我只是想尽可能接近批处理指南中描述的 HTTP 请求。 \r 和 \n 行结尾可能存在一些问题,具体取决于 Google 端点的严格程度。

Sources:

  • 请求 github(特别是问题 #935 和 #1081)
  • 请求API文档
  • 适用于 Python 的 Google API
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何批量发送包含多个 url 的多部分 html 帖子? 的相关文章

随机推荐

  • globalPackagesFolder存储库路径差异

    根据globalPackagesFolderNuGet 文档 它允许您更改默认全局包文件夹的位置 而不是 Users username nuget packages 所以 我发现这是存储包裹的地方 另一方面 repositoryPathNu
  • 使 FetchContent 与 find_package 兼容

    我尝试添加我的项目所需的所有依赖项以通过 CMake 进行编译 这应该会减少其他人第一次编译项目时的开销 为了实现这一点 我尝试使用 FetchContent 到目前为止一切顺利 当我链接生成的目标时 这根本不是问题 但现在我有一个库依赖于
  • 您需要在 GCD 的块内创建 NSAutoreleasePool 吗?

    通常 如果您生成后台线程或在 NSOperationQueue 上运行 NSOperation 则需要为该线程或操作创建 NSAutoreleasePool 因为默认情况下不存在 同样的规则是否适用于放置在 Grand Central Di
  • Jquery 验证插件 - 您可以从选项中启用“热切”验证吗?

    我在项目中使用 Jquery 验证插件 默认情况下 该插件会在单击提交按钮时验证输入 该行为是 惰性的 以便不打扰用户 如果发现错误 验证就会变得 急切 并在用户更正有问题的条目时验证输入 有没有办法通过选项覆盖最初的 惰性 行为 我在文档
  • 如何从时间(小时)中删除前导零

    我想要从 1 到 9 的小时不带前导零 但分钟带零 同时还要在时间上添加 15 分钟 现在 当我输入 1 和 46 时 我得到 02 01 我想得到 2 01 Scanner scan new Scanner System in int h
  • Request.Browser.IsMobileDevice 不适用于 iPadAir2 和 iOS 13.0.1

    I am able to detect iPadAir2 device running on iOS 11 4 using Request Browser IsMobileDevice and it gives me UserAgent i
  • 颁发者证书的过期状态是否会影响主体的过期?

    如果证书颁发者颁发的证书的过期时间发生在颁发者自己的证书过期之后 那么颁发者的证书过期后 颁发的证书是否仍然有效 为了更清楚 让我举个例子 I 发行人 C 颁发的证书 如果我在 2007 年创建了 C 到期日期为 2017 年 我的证书20
  • Leopard 终端(和 iTerm)忽略控制组合键

    I am very used to using Ctrl A Ctrl E Ctrl L etc as shortcuts to operations beginning of line end of line clear terminal
  • 找到不在列表中的最小整数

    我的一位同事使用了一个有趣的面试问题 假设给您一个非常长的 未排序的无符号 64 位整数列表 你如何找到最小的非负整数does not出现在列表中 后续 既然已经提出了明显的排序解决方案 你能比 O n log n 更快地完成它吗 后续 您
  • 查找“nan”并将其替换为数字

    我想替换数组中的数字 3 而不是所有 nan 这是我的代码 train train replace nan int 3 但我的数组没有任何变化 你能指导一下吗 您可以使用np isnan import numpy as np train n
  • 从 ExceptionLogger 引用操作参数

    我想利用新方法来全局记录错误 我写了一个继承的类ExceptionLogger并覆盖Log 方法 然后将其注册为替代品 public class TraceExceptionLogger ExceptionLogger public asy
  • 在 .NET 中创建插件环境的最佳方法

    我读了这篇文章如何在 NET中加载插件 我实在看不出微软的System Addin命名空间有什么高明之处 为什么我不能在 bin 目录中有一个插件文件夹 用户可以将程序集放入其中以实现我设计的界面 然后 我可以使用反射来创建插件类的实例 并
  • Phonegap 在应用程序运行时启用 GPS/位置

    我正在使用 jquery ui maps 和 HTML5 地理位置向用户显示位置列表 我需要利用用户的地理位置 所以 这是用例 用户未启用 GPS 定位服务 用户打开应用程序并导航到调用的视图导航器 地理位置获取用户的位置 应用程序出错并通
  • 尝试理解 javascript 中 for 循环内的递归

    我一直盯着这个问题的答案 甚至在每次迭代中写下变量之类的东西 我只是不明白这里的过程 当我输入控制台日志时 我看到 permute 在到达此行之前被调用 input length 1 次 input splice i 0 ch 当我完全迷失
  • 删除 ttk 组合框鼠标滚轮绑定

    我有一个 ttk 组合框 我想从鼠标滚轮解除绑定 以便在组合框处于活动状态时使用滚轮滚动不会更改值 而是滚动框架 我尝试过解除绑定以及绑定到空函数 但都不起作用 见下文 import Tkinter as tk import ttk cla
  • 从 9i 客户端连接到 11g 数据库时,ORA-01017 用户名/密码无效

    我正在尝试从具有 9i v9 2 0 1 客户端的 PC 连接到 11g v11 2 0 1 0 上的架构 它似乎可以很好地连接到某些模式 但不是这个 它返回一个ORA 01017 Invalid Username Password每次都会
  • 区分带有默认值和无值的&可选参数

    根据功能在 GigaMonkeys 上 Common Lisp 通过以下方式支持可选位置参数 optional并且默认值可以任意设置 默认的默认值为nil defun function mandatory argument optional
  • 如何通过 MVVM 为 DataGrid ItemSsource 设置过滤器

    我有一个DataGrid绑定到一个集合视图源在 XAML 中
  • Next.js React 应用程序中未定义窗口

    In my Next js我似乎无法访问该应用程序window 未处理的拒绝 ReferenceError 窗口未定义 componentWillMount console log window innerHeight window inn
  • 如何批量发送包含多个 url 的多部分 html 帖子?

    我正在与 gmail api 交谈 并希望对请求进行批处理 他们在这里有一个友好的指南 https developers google com gmail api guides batch 这表明我应该能够使用多部分 混合并包含不同的网址