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?
私はこのトピックにまったく新しいので、新人の質問に非常に簡単な答えや間違いがある場合はご容赦ください。すべてのあなたの助けに感謝します!
少し掘り下げた後、 この投稿 が私の問題を解決したようです。マルチパートエンコーダーを適切にセットアップする必要があることがわかりました。
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)
個人的には、ファイルを投稿するには 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
まあ、それを行うには複数の方法があります。上記のように、「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はエラーをスローしませんが、アップロードされたファイルは破損します。