Python equivalente a los rizos –form: Cree una solicitud de publicación de datos de formulario de varias partes con datos en el parámetro “formulario”

Estoy buscando un python equivalente a este comando curl:

curl --referer "https://myreferer" --insecure --form "myparam=1234" https://myurl 

que resulta en la siguiente solicitud (tomada de httpbin.org/post):

 { "args": {}, "data": "", "files": {}, "form": { "myparam": "1234" }, "headers": { "Accept": "*/*", "Connection": "close", "Content-Length": "142", "Content-Type": "multipart/form-data; boundary=----------------------------29a1ce32cc53", "Host": "httpbin.org", "Referer": "https://speedport.ip/hcti_start_passwort.stm", "User-Agent": "curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3", "X-Request-Id": "c67c4461-89d2-4c9f-a9f4-ebfe312c026c" }, ... 

Como puede ver, los datos “myparam” se entregan en un parámetro de “formulario”.

Intenté construir una solicitud de este tipo a través del módulo de requests pythons y me acerqué con este código:

 import requests payload={'myparam':'1234'} url="http://httpbin.org/post" headers={'User-Agent': 'Mozilla 5.0','referer':'https://myreferer'} r = requests.post(url, files=payload, headers=headers,verify=False) 

Pero la biblioteca de solicitudes coloca los datos en el parámetro “archivos”. Así que la solicitud resultante se ve así:

 { "args": {}, "data": "", "files": { "pws": "1234" }, "form": {}, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "143", "Content-Type": "multipart/form-data; boundary=a878ad29e28d47ffb00e0631319ed0e2", "Host": "httpbin.org", "Referer": "https://myreferer", "User-Agent": "Mozilla 5.0", "X-Request-Id": "60f5d65e-789a-47fe-bba3-dab88f9bbb65" ... 

Así que los datos se entregan en el lugar equivocado, es decir, dentro del parámetro “archivos”, lo que hace que Apache se ahogue con una respuesta “501 No implementado”.

¿Alguien puede sugerir cómo hacer una solicitud de este tipo en Python? (Sé que podría llamar a curl como un subproceso, pero como quiero hacer muchas de estas solicitudes, me gustaría tener una versión de solo python (que posiblemente sea más eficaz)).

Y, como habrás notado, también debo aceptar un certificado autofirmado y enviar un encabezado de referencia.

Me alegraría si alguien pudiera sugerir una manera fácil de resolver esto.

¡Gracias!

Edición : ya probé con el comando “data” -param del requests.post pero esto da como resultado un encabezado de tipo de contenido diferente (application / x-www-form-urlencoded). Tenga en cuenta el tipo de contenido encabezado de la solicitud de curl.

Edición : Lo que probablemente necesite es simplemente enviar el encabezado de Contenido-Tipo correcto, multipart / form-data, a través del parámetro de encabezados del comando requests.post. Pero también tendría que calcular la parte “límite” de la cadena de encabezado multipart / form-data. Supongo que debe haber una forma más fácil que construir manualmente el encabezado y calcular los límites.

El uso file-like object a un files para resultados de files en multipart/form-data tipo de contenido de multipart/form-data

Preparemos todo lo que necesitamos para la llamada, comenzando con lo “habitual”:

 >>> import requests >>> data = {"myparam": "1234"} >>> headers = {'User-Agent': 'Mozilla 5.0','referer':'https://myreferer'} 

El truco para forzar requests para usar “multipart / form-data” es darle al menos un objeto similar a un archivo.

 >>> from StringIO import StringIO >>> buff = StringIO("") 

buff es ahora el objeto tipo archivo que podemos pasar como argumento de valor de files .

 >>> req = requests.post(url, data=data, headers=headers, stream=True, files=buff) >>> print req.text { "args": {}, "data": "", "files": {}, "form": { "myparam": "1234" }, "headers": { "Accept": "*/*", "Accept-Encoding": "gzip, deflate", "Connection": "close", "Content-Length": "130", "Content-Type": "multipart/form-data; boundary=0b3bbec1f5c844a1b7377aacfe701f02", "Host": "httpbin.org", "Referer": "https://myreferer", "User-Agent": "Mozilla 5.0", "X-Request-Id": "988a0467-1c32-45aa-a75c-fba5aa8d632e" }, "json": null, "origin": "85.160.45.204", "url": "http://httpbin.org/post" } 

Si se comunicaría con https utilizando un certificado autofirmado, use verify=False :

 >>> req = requests.post(url, data=data, headers=headers, stream=True, files=buff, verify=False) 

La ayuda para requests.request también señala que el valor de verify podría ser “Una ruta CA_BUNDLE”, por lo que puede asegurarse explícitamente de que el servidor está utilizando el certificado autofirmado que espera. Pero con esto nunca lo he experimentado.

Desafortunadamente, si no desea enviar los datos como un archivo, tiene que usar una biblioteca de terceros: request_toolbelt . Una vez que pides pip install requests-toolbelt , puedes hacer

 from requests_toolbelt import MultipartEncoder import requests payload = MultipartEncoder({'myparam': '1234'}) r = requests.post(url, data=payload, headers={'Content-Type': payload.content_type}) 

Naturalmente, también puede configurar los otros encabezados, esto es solo un ejemplo rápido del uso del cinturón de herramientas según sus necesidades.

Si desea validar el certificado, puede pasar una cadena con la ruta completa al archivo PEM, por ejemplo,

 r = requests.get('https://somesite.com', verify='/Users/mhelwig/certificate.pem')