2012-09-12 11 views
104

Jak wysłać multipart/form-data z zapytaniami w pythonie? Jak wysłać plik, rozumiem, ale nie można zrozumieć, jak wysłać dane formularza za pomocą tej metody.Jak wysłać "wieloczęściowy/formularz danych" z zapytaniami w python?

+0

twoje pytanie nie jest jasne. Co chcesz osiągnąć? Czy chcesz wysłać "wieloczęściowy/formularz danych" bez przesyłania pliku w formularzu? –

+2

Fakt, że parametr 'files' jest używany w obu przypadkach, to bardzo zły interfejs API. Zrobiłem problem pod tytułem [Wysyłanie wieloczęściowych danych - potrzebujemy lepszego interfejsu API] (https://github.com/kennethreitz/requests/issues/935), aby to naprawić. Jeśli zgadzasz się, że użycie parametru 'files' do wysyłania danych mulitpart jest w najlepszym przypadku mylące, poproś o zmianę interfejsu API w powyższym wydaniu. –

+0

@PiotrDobrogost, że problem został zamknięty. Nie zachęcaj ludzi do komentowania zamkniętych spraw, istotnych lub nie. –

Odpowiedz

81

Zasadniczo, jeśli podasz files parametr (słownika), a następnie requests wyśle ​​multipart/form-data POST zamiast application/x-www-form-urlencoded POST. Nie ograniczasz się jednak do używania rzeczywistych plików w tym słowniku, jednak:

>>> import requests 
>>> response = requests.post('http://httpbin.org/post', files=dict(foo='bar')) 
>>> response.status_code 
200 

i httpbin.org pozwala Ci dowiedzieć się, jakie nagłówki zamieściłeś; w response.json() mamy:

>>> from pprint import pprint 
>>> pprint(response.json()['headers']) 
{u'Accept': u'*/*', 
u'Accept-Encoding': u'gzip, deflate, compress', 
u'Connection': u'close', 
u'Content-Length': u'141', 
u'Content-Type': u'multipart/form-data; boundary=33b4531a79be4b278de5f5688fab7701', 
u'Host': u'httpbin.org', 
u'User-Agent': u'python-requests/2.2.1 CPython/2.7.6 Darwin/13.2.0', 
u'X-Request-Id': u'eaf6baf8-fc3d-456b-b17d-e8219ccef1b1'} 

files może być również lista krotek dwie wartości, jeśli potrzeba uporządkowania i/lub wiele pól o tej samej nazwie:

requests.post('http://requestb.in/xucj9exu', files=(('foo', 'bar'), ('spam', 'eggs'))) 

Jeśli podasz zarówno files i data, to zależy od wartości z data co zostanie użyte do utworzenia treści POST. Jeśli data jest łańcuchem, tylko będzie on użyty; w przeciwnym razie stosowane są oba typy: data i , z pierwszymi wymienionymi elementami w data.

+4

Spowoduje to zakodowanie dowolnej rzeczy wysłanej do 'files' jako rzeczywistego parametru pliku w kodowaniu wieloczęściowym. To nie stworzy ścisłej postaci, ale zamiast formularza z wszystkimi parametrami pliku. Zobacz [to] (https://github.com/kennethreitz/requests/issues/1081) w celach informacyjnych. –

+0

@ sigmavirus24: Interfejs API zapytań ewoluował odkąd to opublikowałem; pozwól mi zbadać, czy to wymaga teraz aktualizacji. W każdym razie ten zakątek interfejsu API [od jakiegoś czasu wymaga przeglądu] (https://github.com/kennethreitz/requests/issues/935). –

+0

przeprosiny. StackOverflow umieścił to na górze i zapomniałem reorganizacji pytań i muszę spojrzeć na daty odpowiedzi/zapytał. –

68

Ponieważ poprzednie odpowiedzi zostały napisane, prośby się zmieniły. Spójrz na numer bug thread at Github, aby uzyskać więcej szczegółów i na przykład this comment.

W skrócie, parametr plików zajmuje dict z kluczem to nazwa pola formularza i wartość jest albo ciąg lub 2, 3 lub 4-length krotki, jak opisano w sekcji POST a Multipart-Encoded File we wnioskach Szybki start:

>>> url = 'http://httpbin.org/post' 
>>> files = {'file': ('report.xls', open('report.xls', 'rb'), 'application/vnd.ms-excel', {'Expires': '0'})} 

w powyższym krotka składa się w następujący sposób:

(filename, data, content_type, headers) 

Jeśli wartość jest po prostu ciągiem znaków, nazwa pliku będzie taka sama jak klucz, jak w poniższym przykładzie:

>>> files = {'obvius_session_id': '72c2b6f406cdabd578c5fd7598557c52'} 

Content-Disposition: form-data; name="obvius_session_id"; filename="obvius_session_id" 
Content-Type: application/octet-stream 

72c2b6f406cdabd578c5fd7598557c52 

Jeśli wartość jest krotka i pierwszy wpis jest None własność pliku nie zostaną uwzględnione:

>>> files = {'obvius_session_id': (None, '72c2b6f406cdabd578c5fd7598557c52')} 

Content-Disposition: form-data; name="obvius_session_id" 
Content-Type: application/octet-stream 

72c2b6f406cdabd578c5fd7598557c52 
+1

Co zrobić, jeśli musisz odróżnić 'name' i' filename', ale także mieć wiele pól o tej samej nazwie? – Michael

+1

Mam problem z symulacją jako @Michael. Czy możesz rzucić okiem na to pytanie i coś zasugerować? [link] (http://stackoverflow.com/questions/30683352/how-to-upload-multipart-encode-file-and-form-data-as-a-payload) – Shaardool

+0

czy ktoś rozwiązał ten problem z wieloma polami o tej samej nazwie? – user3131037

27

Trzeba użyć parametru files wysłać wieloczęściowy żądanie formularz POST, nawet jeśli nie trzeba przesyłać żadnych plików.

Z pierwotnego requests źródła:

def request(method, url, **kwargs): 
    """Constructs and sends a :class:`Request <Request>`. 

    ... 
    :param files: (optional) Dictionary of ``'name': file-like-objects`` 
     (or ``{'name': file-tuple}``) for multipart encoding upload. 
     ``file-tuple`` can be a 2-tuple ``('filename', fileobj)``, 
     3-tuple ``('filename', fileobj, 'content_type')`` 
     or a 4-tuple ``('filename', fileobj, 'content_type', custom_headers)``, 
     where ``'content-type'`` is a string 
     defining the content type of the given file 
     and ``custom_headers`` a dict-like object 
     containing additional headers to add for the file. 

Najprostszym wieloczęściowy żądanie formularz, który zawiera oba pliki do przesłania i pól formularza będzie wyglądać następująco:

multipart_form_data = { 
    'file1': open('myfile.zip', 'rb'), 
    'file2': ('custom_file_name.zip', open('myfile.zip', 'rb')), 
    'action': ('', 'store'), 
    'path': ('', '/path1') 
} 

response = requests.post('https://httpbin.org/post', files=multipart_form_data) 

print(response.content) 

Uwaga pusty łańcuch jako pierwszy element krotki dla pól zwykłego tekstu - jest to element zastępczy dla pola nazwy pliku, który jest używany tylko do przesyłania plików, ale dla pola tekstowego pusty symbol zastępczy musi być obecny w kolejności dla danych, które należy złożyć.


Jeśli ten API nie jest wystarczająco pythonic dla Ciebie, lub jeśli trzeba pisać wiele pól o tej samej nazwie, a następnie rozważyć użycie requests toolbelt ( pip install requests_toolbelt), który stanowi rozszerzenie modułu core requests, który zapewnia wsparcie dla pliku przesyłaj strumieniowo, a także MultipartEncoder, który może być używany zamiast files i który przyjmuje parametry zarówno jako słowniki, jak i krotki.

MultipartEncoder może być używany zarówno w przypadku żądań wieloczęściowych z faktycznymi polami przesyłania, jak i bez nich. Musi być przypisany do parametru data.

import requests 
from requests_toolbelt.multipart.encoder import MultipartEncoder 

multipart_data = MultipartEncoder(
    fields={ 
      # a file upload field 
      'file': ('file.py', open('file.py', 'rb'), 'text/plain') 
      # plain text fields 
      'field0': 'value0', 
      'field1': 'value1', 
      } 
    ) 

response = requests.post('http://httpbin.org/post', data=multipart_data, 
        headers={'Content-Type': multipart_data.content_type}) 

Jeśli chcesz wysłać wiele pól o tej samej nazwie, lub jeśli kolejność pól formularza jest ważne, to krotka lub lista może być stosowany zamiast słownika, czyli:

multipart_data = MultipartEncoder(
    fields=(
      ('action', 'store'), 
      ('path', '/path1'), 
      ('path', '/path2'), 
      ('path', '/path3'), 
      ) 
    ) 
+0

Dziękuję za to. Kolejność kluczy była dla mnie ważna, a to bardzo pomogło. – Splendor

+0

Niesamowite. Niewytłumaczalnie, api, z którym pracuję, wymaga dwóch różnych wartości dla tego samego klucza. To jest niesamowite. Dziękuję Ci. – ajon

+0

@ccpizza, co właściwie oznacza ta linia? > "(" file.py ", otwórz (" file.py "," rb ")," text/plain ")". To nie działa dla mnie :( –

Powiązane problemy