使用 urllib3 进行多部分表单编码和发布

2024-02-24

我正在尝试上传csv文件至。不过,我遇到了一些问题,我think它源于不正确的mimetype(或许)。

我正在尝试通过以下方式手动发布文件urllib2,所以我的代码如下所示:

import urllib
import urllib2
import mimetools, mimetypes
import os, stat
from cStringIO import StringIO

#============================
# Note: I found this recipe online. I can't remember where exactly though.. 
#=============================

class Callable:
    def __init__(self, anycallable):
        self.__call__ = anycallable

# Controls how sequences are uncoded. If true, elements may be given multiple values by
#  assigning a sequence.
doseq = 1

class MultipartPostHandler(urllib2.BaseHandler):
    handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first

    def http_request(self, request):
        data = request.get_data()
        if data is not None and type(data) != str:
            v_files = []
            v_vars = []
            try:
                 for(key, value) in data.items():
                     if type(value) == file:
                         v_files.append((key, value))
                     else:
                         v_vars.append((key, value))
            except TypeError:
                systype, value, traceback = sys.exc_info()
                raise TypeError, "not a valid non-string sequence or mapping object", traceback

            if len(v_files) == 0:
                data = urllib.urlencode(v_vars, doseq)
            else:
                boundary, data = self.multipart_encode(v_vars, v_files)

                contenttype = 'multipart/form-data; boundary=%s' % boundary
                if(request.has_header('Content-Type')
                   and request.get_header('Content-Type').find('multipart/form-data') != 0):
                    print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
                request.add_unredirected_header('Content-Type', contenttype)

            request.add_data(data)

        return request

    def multipart_encode(vars, files, boundary = None, buf = None):
        if boundary is None:
            boundary = mimetools.choose_boundary()
        if buf is None:
            buf = StringIO()
        for(key, value) in vars:
            buf.write('--%s\r\n' % boundary)
            buf.write('Content-Disposition: form-data; name="%s"' % key)
            buf.write('\r\n\r\n' + value + '\r\n')
        for(key, fd) in files:
            file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
            filename = fd.name.split('/')[-1]
            contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
            buf.write('--%s\r\n' % boundary)
            buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
            buf.write('Content-Type: %s\r\n' % contenttype)
            # buffer += 'Content-Length: %s\r\n' % file_size
            fd.seek(0)
            buf.write('\r\n' + fd.read() + '\r\n')
        buf.write('--' + boundary + '--\r\n\r\n')
        buf = buf.getvalue()
        return boundary, buf
    multipart_encode = Callable(multipart_encode)

    https_request = http_request

    import cookielib
    cookies = cookielib.CookieJar()
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
            MultipartPostHandler)

    opener.addheaders = [(
            'User-agent', 
            'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6'
        )]


    params = {"FILENAME" : open("weather_scrape.csv", 'rb'),
            'CGIREF' : '/calludt.cgi/DDFILE1',
            'USE':'MODEL',
            'MODEL':'CM',
            'CROP':'APPLES',
            'METHOD': 'SS',
            'UNITS' : 'E',
            'LOWTHRESHOLD': '50',
            'UPTHRESHOLD': '88',
            'CUTOFF':'H',
            'COUNTY':'AL',
            'ACTIVE':'Y',
            'FROMMONTH':'3',
            'FROMDAY':'15',
            'FROMYEAR': '2013',
            'THRUMONTH':'5',
            'THRUDAY':'13',
            'THRUYEAR':'2013',
            'DATASOURCE' : 'FILE'
            }

    response = opener.open("http://www.ipm.ucdavis.edu/WEATHER/textupload.cgi", params)

现在,当我发布此内容时,一切似乎都很好,直到我单击第一个网页上的提交按钮POST返回。然后我收到此错误消息:

ERROR (bad data) in file 'weather.csv' at line 135.

Data record = [--192.168.117.2.1.4404.1368589639.796.1--]

Too few values found. Check delimiter specification.

现在,在调查我在浏览器中执行操作时发出的发布请求时,我注意到content-type非常具体,即:

------WebKitFormBoundaryfBp6Jfhv7LlPZLKd
Content-Disposition: form-data; name="FILENAME"; filename="weather.csv"
Content-Type: application/vnd.ms-excel

我不完全确定内容类型是导致错误的原因,但是..这是我目前排除的(因为我不知道实际上出了什么问题。)我没有看到任何方法通过 urllib2 设置内容类型,所以经过一番谷歌搜索后,我偶然发现urllib3.

Urllib3具有内置文件发布功能,但我不完全确定如何使用它。

Filepost

urllib3.filepost.encode_multipart_formdata(fields, boundary=None)
Encode a dictionary of fields using the multipart/form-data MIME format.

Parameters: 
fields –
Dictionary of fields or list of (key, value) or (key, value, MIME type) field tuples. The key is treated as the field name, and the value as the body of the form-data bytes. If the value is a tuple of two elements, then the first element is treated as the filename of the form-data section and a suitable MIME type is guessed based on the filename. If the value is a tuple of three elements, then the third element is treated as an explicit MIME type of the form-data section.
Field names and filenames must be unicode.
boundary – If not specified, then a random boundary will be generated using mimetools.choose_boundary().
urllib3.filepost.iter_fields(fields)
Iterate over fields.

Supports list of (k, v) tuples and dicts.

使用这个库,我尝试将值编码为文档中的描述,但出现错误。

我最初尝试过,只是为了测试一下,作为一个dict.

params = {"FILENAME" : open("weather.csv", 'rb'),
            'CGIREF' : '/calludt.cgi/DDFILE1',
            'USE':'MODEL',
            'MODEL':'CM',
            'CROP':'APPLES',
            'METHOD': 'SS',
            'UNITS' : 'E',
            'LOWTHRESHOLD': '50',
            'UPTHRESHOLD': '88',
            'CUTOFF':'H',
            'COUNTY':'AL',
            'ACTIVE':'Y',
            'FROMMONTH':'3',
            'FROMDAY':'15',
            'FROMYEAR': '2013',
            'THRUMONTH':'5',
            'THRUDAY':'13',
            'THRUYEAR':'2013',
            'DATASOURCE' : 'FILE'
            }

    values = urllib3.filepost.encode_multipart_formdata(params)

但是,这会引发以下错误:

    values = urllib3.filepost.encode_multipart_formdata(params)
  File "c:\python27\lib\site-packages\urllib3-dev-py2.7.egg\urllib3\filepost.py", line 90, in encode_multipart_formdata
    body.write(data)
TypeError: 'file' does not have the buffer interface

不确定是什么原因导致的,我尝试传递一个元组列表(键、值、mimetype),但这也会引发错误:

params = [
        ("FILENAME" , open("weather_scrape.csv"), 'application/vnd.ms-excel'),
        ('CGIREF' , '/calludt.cgi/DDFILE1'),
        ('USE','MODEL'),
        ('MODEL','CM'),
        ('CROP','APPLES'),
        ('METHOD', 'SS'),
        ('UNITS' , 'E'),
        ('LOWTHRESHOLD', '50'),
        ('UPTHRESHOLD', '88'),
        ('CUTOFF','H'),
        ('COUNTY','AL'),
        ('ACTIVE','Y'),
        ('FROMMONTH','3'),
        ('FROMDAY','15'),
        ('FROMYEAR', '2013'),
        ('THRUMONTH','5'),
        ('THRUDAY','13'),
        ('THRUYEAR','2013'),
        ('DATASOURCE' , 'FILE)')
        ]

    values = urllib3.filepost.encode_multipart_formdata(params)



>>ValueError: too many values to unpack

如果你想为此使用 urllib3,它看起来像这样:

import urllib3

http = urllib3.PoolManager()

headers = urllib3.make_headers(user_agent='Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6')
url = "http://www.ipm.ucdavis.edu/WEATHER/textupload.cgi"
csv_data = open("weather_scrape.csv").read()

params = {
    "FILENAME": csv_data,
    'CGIREF': '/calludt.cgi/DDFILE1',
    'USE': 'MODEL',
    'MODEL': 'CM',
    'CROP': 'APPLES',
    'METHOD': 'SS',
    'UNITS' : 'E',
    'LOWTHRESHOLD': '50',
    'UPTHRESHOLD': '88',
    'CUTOFF': 'H',
    'COUNTY': 'AL',
    'ACTIVE': 'Y',
    'FROMMONTH': '3',
    'FROMDAY': '15',
    'FROMYEAR': '2013',
    'THRUMONTH': '5',
    'THRUDAY': '13',
    'THRUYEAR': '2013',
    'DATASOURCE' : 'FILE',
}

response = http.request('POST', url, params, headers)

我无法使用您的目标 url 和 csv 数据集对此进行测试,因此它可能存在一些小错误。但这是一般的想法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 urllib3 进行多部分表单编码和发布 的相关文章

随机推荐

  • Direct2D 和 DXGI(D3D 互操作)多线程的最佳实践是什么?

    理想情况下 我希望有多个工作线程能够渲染到屏幕外渲染目标 然后将渲染的内容 传输 到屏幕上目标 对于 hwnd 渲染目标 这似乎不是问题 msdn 有一个例子 当屏幕渲染目标基于 DXGI 交换链时 我不太确定该怎么做 据我所知 每个窗口只
  • 为什么在基于范围的初始化程序中使用临时对象会导致崩溃?

    为什么以下代码在 Visual Studio 和 GCC 上都会崩溃 为了使其崩溃 需要基于范围的 for 循环 std map std string 并引用字符串 如果我删除其中任何一个 它就会起作用 include
  • 如何获取Angular2中ag网格中选定行的数据?

    我在 angular2 中设置了 ag grid 它工作正常 但我无法获取所选行的值 我的控制台窗口中没有错误 这就是我初始化网格的方式 import Component from angular2 core Component selec
  • 如何在 MVC-gui 中使用 JUNG2?

    我正在玩 JUNG2 想要实现一个小型 GUI 允许我显示和更改图表 遵循 JUNG 库中的示例效果很好 但它们没有分离模型 视图和控制器 所以我开始以干净的分离方式构建 GUI 我的第一个 GUI 版本应该是简单地显示初始图形 视图是模型
  • 是否可以在 sass 中重载 mixins ?

    假设你有一个像这样的阴影混合 mixin box shadow offset blur color moz box shadow offset offset blur color webkit box shadow offset offse
  • MySql 中的 DELIMITER 错误

    我正在使用以下sql DELIMITER DROP PROCEDURE IF EXISTS get auto increment settings CREATE PROCEDURE get auto increment settings B
  • Rails 不转换时区 (PostgreSQL)

    我对时区和 postgresql 数据库 Rails 3 0 4 PostgreSQL 9 0 有问题 我正在使用自定义范围 在其中附加一些条件 执行连接等 问题是 Rails 不会将时间转换为我的本地时区 这是范围的代码 scope wi
  • VBscript删除子文件夹

    我对 vb 脚本非常陌生 我需要一个脚本来根据起始名称 SA 和 2 天前删除几个三级子文件夹 example C abc user1 temp SA123 c abc user2 temp SA2345 c abc user3 temp
  • 如何在 VS Code 编辑器中按标题级别更改 Markdown 标题颜色?

    我的问题类似于但那里给出的答案是针对 Vim 的 我需要一个针对 VS Code 的答案 我是一个真正的新手 我尝试自己解决这个问题 但这些尝试失败了 Markdown 预览 GitHub 样式 https github com mjbvz
  • 如何复制克隆 UIElement 并保留布局/渲染信息?

    我想复制一个复杂的数据绑定UIElement但保留原始 UIElement 中的绑定 布局和渲染信息 创建一个新的UIElement似乎效率低下 因为我必须执行另一个完整的绑定 测量 排列 渲染过程 到目前为止我最接近的是创建一个新的Dra
  • 对于上下文无关语法,如何将其转换为等效的下推自动机?

    对于 0 1 2 上的上下文无关文法 G 起始变量为 S S 0S0 1S1 2S2 是是 22 我如何将其变成等效的下推自动机 下推自动机可以将符号推入堆栈顶部并将其弹出 它还可以将其转换基于最顶层的堆栈符号 我们需要考虑一种机制 允许我
  • 通过 USB 模拟 UART

    有谁知道是否可以通过 USB 模拟 UART 简单串行发送和接收 这将如何实现 我在 Microchip 网站上找到了这个链接 但不是很容易找到 http www microchip com forums m522571 print asp
  • PassportJS 自定义身份验证回调未调用

    更新 以下错误已修复a commit https github com jaredhanson passport issues 186 我已将第一个答案标记为 正确 尽管该提交在其中一条评论中引起了我的注意 我希望利用自定义回调来处理 Pa
  • 使用 .htaccess 删除 .php 扩展名

    是的 我已经阅读了 Apache 手册并在这里进行了搜索 由于某种原因 我根本无法让它发挥作用 我最接近的是让它删除扩展名 但它指向根目录 我希望它只在包含以下内容的目录中工作 htaccess file 我需要做三件事 htaccess
  • javax.net.ssl.SSLHandshakeException:在 bigquery 中插入行时,远程主机在握手期间关闭了连接

    您好 我正在开发 Android 应用程序 其中集成了 bigquery 我发现有时我们在将数据插入大型查询表时会遇到很多 SSL 异常 我不知道该如何处理这件事 请帮忙看看这个问题的具体原因是什么 这是相同的主题 但没有答案进行流式插入
  • 如何简洁地创建一个以字母为键的字典?

    我创建了一个包含 26 个字母的字典 如下所示 aDict a 1 b 2 c 3 d 4 etc 我正在努力让我的代码变得更好 我的问题是 有没有更短的方法可以做到这一点而无需输入所有这些数字 您可以使用string ascii lowe
  • AngularJS 和 Angular-UI Bootstrap 选项卡范围

    我正在使用 AngularJS 和 Angular UI Bootstrap 选项卡 这是我的控制器 app controller SettingsCtrl scope SettingsFactory stateParams functio
  • 在 pandas 的数据框中创建一个组

    我有一个清单 例如 groups Group1 A B Group2 C D 和一个数据框 例如 A 100 B 200 C 300 D 400 我想将上面列表中的一组总和变为 Group 1 300 Group 2 700 我如何使用 p
  • 列表视图项目点击不起作用

    我的 ListView 中有一个 ListView 显示 ImageButton 我将 focusalble false 和 focusableInTouchMode false 设置为 ImageButton 我设置了ListView O
  • 使用 urllib3 进行多部分表单编码和发布

    我正在尝试上传csv文件至 不过 我遇到了一些问题 我think它源于不正确的mimetype 或许 我正在尝试通过以下方式手动发布文件urllib2 所以我的代码如下所示 import urllib import urllib2 impo