2013-04-01 12 views
8

Ich versuche, eine mehrteilige/verwandte Nachricht mit Anfragen in Python zu senden. Das Skript scheint einfach genug zu sein, außer dass Anfragen nur das Senden von mehrteiligen/Formulardaten erlauben, obwohl ihre Dokumentation dies nicht eindeutig auf die eine oder andere Art angibt.Wie senden Sie ein "multipart/related" mit Anfragen in Python?

Mein Anwendungsfall sendet Seife mit Anhängen. Ich kann ein Wörterbuch mit den zwei Dateien bereitstellen, deren Inhalt eine Test-Soap-Nachricht ist, und ein Testdokument, das ich zu senden versuche. Die erste enthält die Soap-Nachricht mit allen Anweisungen, die zweite ist das eigentliche Dokument.

Wenn ich jedoch keinen Header-Wert angeben, scheint nur Anfragen multipart/form-data zu verwenden, wenn Sie die Option files verwenden. Wenn ich jedoch Header für den Versuch vorschlage, einen anderen Multipart-Typ anzugeben, scheinen die Requests die Mime-Randinformationen nicht hinzuzufügen.

url = 'http://10.10.10.90:8020/foo' 
headers = {'content-type': 'multipart/related'} 
files = {'submission': open('submission_set.xml', 'rb'), 'document': open('document.txt', 'rb')} 
response = requests.post(url, data=data, headers=headers) 
print response.text 

Gibt es eine Möglichkeit, dies mit Anfragen zu erledigen? Oder gibt es ein anderes Werkzeug, das ich betrachten sollte?

+0

Haben Sie diese 22 Fragen überprüft, die als Ergebnis der Suche nach "[Python] [Python-Anfragen] + Multipart" kommen? –

+3

@PiotrDobrogost: Es handelt sich dabei um 'multipart/form-data', die für Sie' requests' behandeln. Dies ist * 'multipart/related' *, was keine übliche Kodierung für' POST' ist und 'requests' behandelt das nicht automatisch. –

Antwort

19

Sie müssen die MIME-Codierung selbst erstellen. Sie können mit dem email.mime Paket tun so:

import requests 
from email.mime.multipart import MIMEMultipart 
from email.mime.text import MIMEText 

related = MIMEMultipart('related') 

submission = MIMEText('text', 'xml', 'utf8') 
submission.set_payload(open('submission_set.xml', 'rb').read()) 
related.attach(submission) 

document = MIMEText('text', 'plain') 
document.set_payload(open('document.txt', 'rb').read()) 
related.attach(document) 

body = related.as_string().split('\n\n', 1)[1] 
headers = dict(related.items()) 

r = requests.post(url, data=body, headers=headers) 

ich die XML-Datei vermutet verwendet UTF-8, werden Sie wahrscheinlich festlegen möchten auch einen Zeichensatz für die document Eintrag.

requests kann nur multipart/form-data Post-Körper erstellen; Die multipart/related wird nicht häufig verwendet.

+0

Danke! Das ist sehr hilfreich. Ich habe früher versucht, das Paket 'email.mime' zu ​​verwenden, konnte aber nicht herausfinden, wie man die beiden Dienste zusammenbringt. Du hast mir geholfen, meine beiden alternativen Skripte zu einem zusammenzufügen! Allerdings scheint es ein kleines Problem zu geben, und ich kann nicht sagen, ob es die Kombination von Dienstleistungen oder ein anderer Fehler ist.Ich bekomme ein 'Unerwartetes EOF in Prolog bei [Zeile, Spalte {unbekannte Quelle}]: [1,0] 'was es so aussieht, als ob das erste Zeichen, das gesendet wird, als ein EOF interoperiert ist. Könnte das an der Werkzeugkette/Codierungen liegen? –

+0

Ich habe absolut keine Ahnung; klingt wie ein XML-Analyseproblem, aber der Fehler ist nicht vertraut. –

+0

Das sieht fantastisch aus, aber meine Anfrage scheint für immer zu hängen. Irgendwelche Hinweise? – zapatilla

0

Ich arbeite mit requests und der Google Drive API "Multipart" hochladen.

Die email.mime Lösung funktionierte nicht mit Googles API, also grub ich in den requests Quellcode, um zu sehen, wie es multipart/form-data Körper implementiert.

requests verwendet die urllib3.filepost.encode_multipart_formdata() Helfer, die multipart/related bereitzustellen gewickelt kann:

from urllib3.filepost import encode_multipart_formdata, choose_boundary 

def encode_multipart_related(fields, boundary=None): 
    if boundary is None: 
     boundary = choose_boundary() 

    body, _ = encode_multipart_formdata(fields, boundary) 
    content_type = str('multipart/related; boundary=%s' % boundary) 

    return body, content_type 

Jetzt können wir encode_multipart_related() verwenden, um ein (body, content_type) Tupel zu erstellen, die Anforderungen von Google passt:

import json 
from urllib3.fields import RequestField 

def encode_media_related(metadata, media, media_content_type): 
    rf1 = RequestField(
     name='placeholder', 
     data=json.dumps(metadata), 
     headers={'Content-Type': 'application/json; charset=UTF-8'}, 
    ) 
    rf2 = RequestField(
     name='placeholder2', 
     data=media, 
     headers={'Content-Type': media_content_type}, 
    ) 
    return encode_multipart_related([rf1, rf2]) 

Hier ist ein vollständiges Beispiel, das unsere encode_media_related() verwendet, um eine Hallo-Welt-Datei auf Google Drive hochzuladen, unter Verwendung der google_auth-Bibliothek.

from google.oauth2 import service_account 
import google.auth.transport.requests 

credentials = service_account.Credentials.from_service_account_file(
    PATH_TO_SERVICE_FILE, 
    scopes=['https://www.googleapis.com/auth/drive.file'], 
) 
session = google.auth.transport.requests.AuthorizedSession(credentials) 

metadata = { 
    'mimeType': 'application/vnd.google-apps.document', 
    'name': 'Test Upload', 
} 
body, content_type = encode_media_related(
    metadata, 
    '<html><body><p>Hello World!</body></html>', 
    'text/html; charset=UTF-8', 
) 
resp = session.post(
    'https://www.googleapis.com/upload/drive/v3/files', 
    data=body, 
    params={'uploadType': 'multipart'}, 
    headers={'Content-Type': content_type}, 
) 

print 'Uploaded to file with id: %s' % resp.json()['id']