【问题标题】:Curl POST request into pycurl code将 POST 请求卷曲成 pycurl 代码
【发布时间】:2015-10-27 21:25:59
【问题描述】:

我正在尝试将以下 curl 请求转换为 pycurl:

curl -v
-H Accept:application/json \
-H Content-Type:application/json \
-d "{
    name: 'abc',
    path: 'def',
    target: [ 'ghi' ]
}" \
-X POST http://some-url

我有以下 python 代码:

import pycurl, json

c = pycurl.Curl()
c.setopt(pycurl.URL, 'http://some-url')
c.setopt(pycurl.HTTPHEADER, ['Accept: application/json'])
data = json.dumps({"name": "abc", "path": "def", "target": "ghi"})
c.setopt(pycurl.POST, 1)
c.setopt(pycurl.POSTFIELDS, data)
c.setopt(pycurl.VERBOSE, 1)
c.perform()
print curl_agent.getinfo(pycurl.RESPONSE_CODE)
c.close()

执行此操作时出现错误 415:不支持的媒体类型,因此我已更改:

c.setopt(pycurl.HTTPHEADER, ['Accept: application/json'])

进入:

c.setopt(pycurl.HTTPHEADER, [ 'Content-Type: application/json' , 'Accept: application/json'])

这次我有 400: Bad request。但是带有 curl 的 bash 代码可以工作。你知道我应该在 python 代码中修复什么吗?

【问题讨论】:

    标签: python rest post curl pycurl


    【解决方案1】:

    在您的 bash 示例中,属性 target 是一个数组,在您的 Python 示例中它是一个字符串。

    试试这个:

    data = json.dumps({"name": "abc", "path": "def", "target": ["ghi"]})
    

    我还强烈建议您查看requests 库,它有更好的 API:

    import requests
    data = {"name": "abc", "path": "def", "target": ["ghi"]}
    response = requests.post('http://some-url', json=data)
    print response.status_code
    

    【讨论】:

    • 请求库在很多方面都被破坏了,以至于对于任何严格或稍微不符合要求的服务器都毫无用处。有些请求在 curl 中可以正常工作,但在使用 requests 库时由于其各种不必要的清理而被拒绝。
    • @Nimrod 感谢您的评论。我没有意识到这一点,我自己也从未对请求有任何问题,但有兴趣了解更多关于您描述的问题。你能告诉我更多信息吗?
    • 不,我强烈反对,python requests 并不总是冠军。如果你想上传一个可读流中的文件,那么 pycurl 的 FORM_FILE 来救援。
    【解决方案2】:

    最好使用 requests 库。 (http://docs.python-requests.org/en/latest)

    我为您的原始 curl 自定义标头附加 python 代码。

    import json
    import requests
    
    url = 'http://some-url'
    headers = {'Content-Type': "application/json; charset=xxxe", 'Accept': "application/json"}
    data = {"name": "abc", "path": "def", "target":  ["ghi"]}
    res = requests.post(url, json=data, headers=headers)
    print (res.status_code)
    print (res.raise_for_status())
    

    【讨论】:

    • 请解释为什么它更好
    【解决方案3】:

    我知道这已经一年多了,但请尝试删除标题值中的空格。

    c.setopt(pycurl.HTTPHEADER, ['Accept:application/json'])
    

    我也更喜欢使用 requests 模块,因为 API/方法干净且易于使用。

    【讨论】:

      【解决方案4】:

      我遇到了类似的问题,我使用了您的代码示例,但更新了 httpheader 部分如下:

      c.setopt(pycurl.HTTPHEADER, ['Content-Type:application/json'])
      

      【讨论】:

        【解决方案5】:

        PycURL 是用 C 语言编写的 libcurl 库的包装器,因此它的 Python API 可能有点令人费解。由于有些人提倡使用 python 请求,我只想指出它不是一个完美的替代品。对我来说,它缺乏 DNS 解析超时是一个交易破坏者。我还发现在我的 Raspberry Pi 上它要慢得多。这种比较可能是相关的: Python Requests vs PyCurl Performance

        所以这里有一些东西不能回避 OP 的问题:

        import pycurl
        import json
        from cStringIO import StringIO
        
        curl = pycurl.Curl()
        curl.setopt(pycurl.URL, 'http://some-url')
        curl.setopt(pycurl.HTTPHEADER, ['Accept: application/json',
                                        'Content-Type: application/json'])
        curl.setopt(pycurl.POST, 1)
        
        # If you want to set a total timeout, say, 3 seconds
        curl.setopt(pycurl.TIMEOUT_MS, 3000)
        
        ## depending on whether you want to print details on stdout, uncomment either
        # curl.setopt(pycurl.VERBOSE, 1) # to print entire request flow
        ## or
        # curl.setopt(pycurl.WRITEFUNCTION, lambda x: None) # to keep stdout clean
        
        # preparing body the way pycurl.READDATA wants it
        # NOTE: you may reuse curl object setup at this point
        #  if sending POST repeatedly to the url. It will reuse
        #  the connection.
        body_as_dict = {"name": "abc", "path": "def", "target": "ghi"}
        body_as_json_string = json.dumps(body_as_dict) # dict to json
        body_as_file_object = StringIO(body_as_json_string)
        
        # prepare and send. See also: pycurl.READFUNCTION to pass function instead
        curl.setopt(pycurl.READDATA, body_as_file_object) 
        curl.setopt(pycurl.POSTFIELDSIZE, len(body_as_json_string))
        curl.perform()
        
        # you may want to check HTTP response code, e.g.
        status_code = curl.getinfo(pycurl.RESPONSE_CODE)
        if status_code != 200:
            print "Aww Snap :( Server returned HTTP status code {}".format(status_code)
        
        # don't forget to release connection when finished
        curl.close()
        

        libcurl curleasy setopts documentation 中有一些更有趣的功能值得一试

        【讨论】:

        • StringIO 和 cStringIO 模块不见了。相反,导入 io 模块并分别对文本和数据使用 io.StringIO 或 io.BytesIO。
        • 也许有用:from io import StringIO
        • @MaxBase 很有趣,但是 cStringIO 有自己的位置,因为它是用 C 语言编写的,与 io.StringIO 或 StringIO.StringIO 不同,可以看出它在 this benchmark 中的表现如何。用 C 编写是为了编写性能更高的代码,所以我认为读者会发现 cStringIO 是一个更好的恭维。
        • 非常感谢,经过几天的调查,这对我帮助很大
        猜你喜欢
        • 1970-01-01
        • 2017-05-10
        • 2018-09-20
        • 2019-03-31
        • 1970-01-01
        • 2020-11-10
        • 1970-01-01
        • 2011-12-08
        • 1970-01-01
        相关资源
        最近更新 更多