web-dev-qa-db-ja.com

http POST python urllib / urllib2を使用してファイルのアップロード要求を行う

Pythonを使用してPOST Webサービスにファイルをアップロードする(および応答を取得する)リクエストを作成します。たとえば、次のようにすることができますPOST = curlを含むリクエスト:

curl -F "[email protected]" -F output=json http://jigsaw.w3.org/css-validator/validator

python urllib/urllib2を使用して同じ要求を行うにはどうすればよいですか?これまでで最も近いものは次のとおりです。

with open("style.css", 'r') as f:
    content = f.read()
post_data = {"file": content, "output": "json"}
request = urllib2.Request("http://jigsaw.w3.org/css-validator/validator", \
                          data=urllib.urlencode(post_data))
response = urllib2.urlopen(request)

上記のコードからHTTPエラー500を受け取りました。しかし、私のcurlコマンドは成功するため、python request?

私はこのトピックにまったく新しいので、新人の質問に非常に簡単な答えや間違いがある場合はご容赦ください。すべてのあなたの助けに感謝します!

12
Ying Xiong

少し掘り下げた後、 この投稿 が私の問題を解決したようです。マルチパートエンコーダーを適切にセットアップする必要があることがわかりました。

from poster.encode import multipart_encode
from poster.streaminghttp import register_openers
import urllib2

register_openers()

with open("style.css", 'r') as f:
    datagen, headers = multipart_encode({"file": f})
    request = urllib2.Request("http://jigsaw.w3.org/css-validator/validator", \
                              datagen, headers)
    response = urllib2.urlopen(request)
10
Ying Xiong

個人的には、ファイルを投稿するには requests ライブラリを検討すべきだと思います。

url = 'http://jigsaw.w3.org/css-validator/validator'
files = {'file': open('style.css')}
response = requests.post(url, files=files)

urllib2を使用してファイルをアップロードすることは不可能ではありませんが、非常に複雑なタスクです: http://pymotw.com/2/urllib2/#uploading-files

10
Wolph

まあ、それを行うには複数の方法があります。上記のように、「multipart/form-data」でファイルを送信できます。ただし、ターゲットサービスはこのタイプを予期していない可能性があり、その場合は、さらにいくつかのアプローチを試すことができます。

ファイルオブジェクトを渡す

urllib2は、ファイルオブジェクトをdataとして受け入れることができます。このタイプを渡すと、ライブラリはファイルをバイナリストリームとして読み取り、送信します。ただし、適切なContent-Typeヘッダーをnot設定します。さらに、Content-Lengthヘッダーが欠落している場合、ファイルには存在しないオブジェクトのlenプロパティにアクセスしようとします。ただし、メソッドを機能させるには、Content-TypeヘッダーとContent-Lengthヘッダーの両方を提供する必要があります。

import os
import urllib2

filename = '/var/tmp/myfile.Zip'
headers = {
    'Content-Type': 'application/Zip',
    'Content-Length': os.stat(filename).st_size,
}
request = urllib2.Request('http://localhost', open(filename, 'rb'),
                          headers=headers)
response = urllib2.urlopen(request)

ファイルオブジェクトをラップする

長さを処理しないために、単純なラッパーオブジェクトを作成できます。少し変更するだけで、メモリにファイルがロードされている場合、文字列からコンテンツを取得するように変更できます。

class BinaryFileObject:
  """Simple wrapper for a binary file for urllib2."""

  def __init__(self, filename):
    self.__size = int(os.stat(filename).st_size)
    self.__f = open(filename, 'rb')

  def read(self, blocksize):
    return self.__f.read(blocksize)

  def __len__(self):
    return self.__size

base64としてコンテンツをエンコード

別の方法は、base64.b64encodeを介してdataをエンコードし、Content-Transfer-Type: base64ヘッダーを提供することです。ただし、この方法ではサーバー側のサポートが必要です。実装に応じて、サービスはファイルを受け入れて誤って保存するか、HTTP 400を返します。例えば。 GitHub APIはエラーをスローしませんが、アップロードされたファイルは破損します。

2
real4x