Skip to content

Instantly share code, notes, and snippets.

@hlecuanda
Created October 20, 2020 21:33
Show Gist options
  • Save hlecuanda/d497b9927291d431417fb90f9d7df5cb to your computer and use it in GitHub Desktop.
Save hlecuanda/d497b9927291d431417fb90f9d7df5cb to your computer and use it in GitHub Desktop.
Python Requests Rapidamente.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "Python Requests Rapidamente.ipynb",
"provenance": [],
"collapsed_sections": [],
"toc_visible": true,
"authorship_tag": "ABX9TyM5JSeAO7Z/Dy5SeOGVhPFd",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/hlecuanda/d497b9927291d431417fb90f9d7df5cb/python-requests-rapidamente.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "kRW1R5IsbLgY"
},
"source": [
"# Introducción rápida al uso de la librería `requests`\n",
"\n",
"Aquí veremos de manera concisa la forma correcta de invocar un request simple a un api HTTP, limpiando nuestras conecciones, y validando haber recibido una respuesta correcta del servidor antes de procesarla. "
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Qm3Rgrh_3OEC"
},
"source": [
"## setup basico. \n",
"\n",
"Importamos algunas librerias y la linida impresora para ver nuestros objetos"
]
},
{
"cell_type": "code",
"metadata": {
"id": "Rd3LrQgibJyx",
"outputId": "87aaf1de-bafe-44c4-e91b-8ebe096ce5a7",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"import requests\n",
"import json\n",
"import pprint\n",
"\n",
"\n",
"# una linda impresora con indentacion, maxima profundiad 3 \n",
"# no-compacta\n",
"pp = pprint.PrettyPrinter(indent=2,depth=3,compact=False)\n"
],
"execution_count": 48,
"outputs": [
{
"output_type": "stream",
"text": [
"<Response [200]>\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Iaa1gZ0j3jwA"
},
"source": [
"## El ejemplo mas básico de uso de la libreria requests\n",
"\n",
"Hacemos un `get request` a un api que solo responde con tu propio IP en un objeto json simple (el codigo del servidor lo puedes ver al final)"
]
},
{
"cell_type": "code",
"metadata": {
"id": "BtBFkvKa3hWK",
"outputId": "3782ccc4-0f97-4c54-ee7b-7d3b279f7fde",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"\n",
"# un api estupido que responde con tu propio IP en un mensaje json\n",
"url = 'https://mezaops.appspot.com/knock/'\n",
"r = requests.get(url)\n",
"\n",
"pp.pprint(r)"
],
"execution_count": 90,
"outputs": [
{
"output_type": "stream",
"text": [
"<Response [200]>\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "fgnMuiCVdZXT",
"outputId": "b0a3f82c-f9a4-4fc9-e4da-ad33f814965f",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"pp.pprint(r.content) "
],
"execution_count": 49,
"outputs": [
{
"output_type": "stream",
"text": [
"b'{\\n \"ip\": \"35.225.45.29\"\\n}\\n'\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "eq2nwHFWepsW"
},
"source": [
"aqui `r.content` es una propiedad del objeto `r` definido por `requests` y contiene un `bytearray` con la respuesta del servidor, que en este caso es un objeto json, con una sola propiedad, \"ip\" cuyo valor es tu propio IP\n",
"\n",
"el `bytearray` lo debemos convertir a un objeto python para poder usarlo. requests usa `bytearray` para el contenido de la respuesta porque es el tipo mas generico en el que cabe cualquier respuesa, ya sea texto puro, o bits brutos binarios de una imagen. solo tu sabes que es lo que esperas de el servidor. \n",
"\n",
"para poder usarlo, lo pasamos por `json.loads` que automaticamente detecta el tipo texto y el encoding `utf8` que es el default para el tipo mime `application/json`"
]
},
{
"cell_type": "code",
"metadata": {
"id": "dVQWGw5VdyXT",
"outputId": "471917de-3198-40ff-df29-5232b3533107",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 122
}
},
"source": [
"# eso es json en bruto, para poderlo usar, pasalo por json.loads() \n",
"# loads quiere decir \"load string\"\n",
"\n",
"response_content_object = json.loads(r.content)\n",
"\n",
"print('el objeto diccionario completo:')\n",
"pp.pprint(response_content_object) # response_obect es un diccionario con un solo elemento: 'ip'.\n",
"\n",
"print('\\n')\n",
"print('el elemento \"ip\" en el diccionario:')\n",
"pp.pprint(response_content_object['ip'])\n"
],
"execution_count": 50,
"outputs": [
{
"output_type": "stream",
"text": [
"el objeto diccionario completo:\n",
"{'ip': '35.225.45.29'}\n",
"\n",
"\n",
"el elemento \"ip\" en el diccionario:\n",
"'35.225.45.29'\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xL2zWHEk4BEg"
},
"source": [
"## Otras propiedades de los objetos `request`"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "H05KcZ5cgbRV"
},
"source": [
"El objeto respuesta `r` contiene otras propiedades que puedes usar en el codigo para validad si has recibido una respuesta valida del servidor, notablemente el codigo de respuesta en los encabezados:"
]
},
{
"cell_type": "code",
"metadata": {
"id": "MAIIs-pQgom3",
"outputId": "0fc857ee-d381-46ea-86c4-a3f2b97c1efe",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 34
}
},
"source": [
"pp.pprint(r.status_code)"
],
"execution_count": 51,
"outputs": [
{
"output_type": "stream",
"text": [
"200\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4AhyyjMVpjmp"
},
"source": [
"Una lista de todas las propiedades contenidas en el objeto `r`"
]
},
{
"cell_type": "code",
"metadata": {
"id": "RhXx2jU6hS53",
"outputId": "05c95a8f-0637-4889-f1b5-80a47338410b",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 316
}
},
"source": [
"properties = [ prop for prop in dir(r) if '_' not in prop ]\n",
"\n",
"pp.pprint(properties)"
],
"execution_count": 52,
"outputs": [
{
"output_type": "stream",
"text": [
"[ 'close',\n",
" 'connection',\n",
" 'content',\n",
" 'cookies',\n",
" 'elapsed',\n",
" 'encoding',\n",
" 'headers',\n",
" 'history',\n",
" 'json',\n",
" 'links',\n",
" 'next',\n",
" 'ok',\n",
" 'raw',\n",
" 'reason',\n",
" 'request',\n",
" 'text',\n",
" 'url']\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ZHrzpUsrsAPQ"
},
"source": [
"Veamos que es lo que hay en todo el objeto 'r' que nos pueda servir para validar la respuesta antes de procesarla, evitando un error o excepcion en caso de no recibir respuesta adecuada\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "o01wXo7nsjQy",
"outputId": "2c38508b-15f4-4bcb-e465-6712c1d6023a",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
}
},
"source": [
"for p in properties:\n",
" print('Property:>>{}<<'.format(p))\n",
" print('With Value:{}'.format(r.__getattribute__(p)))\n",
" print('-----------------------\\n')"
],
"execution_count": 78,
"outputs": [
{
"output_type": "stream",
"text": [
"Property:>>close<<\n",
"With Value:<bound method Response.close of <Response [200]>>\n",
"-----------------------\n",
"\n",
"Property:>>connection<<\n",
"With Value:<requests.adapters.HTTPAdapter object at 0x7f20537aa9e8>\n",
"-----------------------\n",
"\n",
"Property:>>content<<\n",
"With Value:b'{\\n \"ip\": \"35.225.45.29\"\\n}\\n'\n",
"-----------------------\n",
"\n",
"Property:>>cookies<<\n",
"With Value:<RequestsCookieJar[]>\n",
"-----------------------\n",
"\n",
"Property:>>elapsed<<\n",
"With Value:0:00:00.035224\n",
"-----------------------\n",
"\n",
"Property:>>encoding<<\n",
"With Value:None\n",
"-----------------------\n",
"\n",
"Property:>>headers<<\n",
"With Value:{'Content-Type': 'application/json', 'X-Cloud-Trace-Context': 'f673bb912acce55214567f4485fdda2e;o=1', 'Date': 'Tue, 20 Oct 2020 20:20:40 GMT', 'Server': 'Google Frontend', 'Content-Length': '29'}\n",
"-----------------------\n",
"\n",
"Property:>>history<<\n",
"With Value:[]\n",
"-----------------------\n",
"\n",
"Property:>>json<<\n",
"With Value:<bound method Response.json of <Response [200]>>\n",
"-----------------------\n",
"\n",
"Property:>>links<<\n",
"With Value:{}\n",
"-----------------------\n",
"\n",
"Property:>>next<<\n",
"With Value:None\n",
"-----------------------\n",
"\n",
"Property:>>ok<<\n",
"With Value:True\n",
"-----------------------\n",
"\n",
"Property:>>raw<<\n",
"With Value:<urllib3.response.HTTPResponse object at 0x7f20537aaf60>\n",
"-----------------------\n",
"\n",
"Property:>>reason<<\n",
"With Value:OK\n",
"-----------------------\n",
"\n",
"Property:>>request<<\n",
"With Value:<PreparedRequest [GET]>\n",
"-----------------------\n",
"\n",
"Property:>>text<<\n",
"With Value:{\n",
" \"ip\": \"35.225.45.29\"\n",
"}\n",
"\n",
"-----------------------\n",
"\n",
"Property:>>url<<\n",
"With Value:https://mezaops.appspot.com/knock/\n",
"-----------------------\n",
"\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Qa3zNuHYuzhv"
},
"source": [
"podemos ver que `requests` amablemente provee un objeto `json` ya procesado que podemos usar directamente.\n",
"\n",
"tambien nos provee un objeto `ok` con valor `True` que podemos usar para determinar si el request fue exitoso con un bonito if asi:\n",
"\n",
"```\n",
"if r.ok:\n",
" # hacemos algo c on r\n",
"else:\n",
" # procesams el error\n",
"```\n",
"\n",
"tenemos tambien `r.reason` entonces, muy utilmente podemos decir\n",
"```\n",
"if r.ok:\n",
" # hacemos algo c on r\n",
"else:\n",
" print('request fallo porque {}'.format(r.reason))\n",
"```\n",
"\n",
"nota tambien que tenemos un metodo `r.close()` lo que nos dice que el request deja abierta una coneccion http al servidor que podemos reusar, y que debemos cerrar al terminar, \n",
"\n",
"y que tenemos un metodo `r.next()` que hace de nuestro objeto `r`` lindo iterable!! omg, que util! \n",
"\n",
"podriamos usar algo asi:\n",
"\n",
"```\n",
"for x in r:\n",
" print(x.content)\n",
"```\n",
"\n",
"esto es para inspecionar conecciones redireccionadas (algunos apis aprovechan ese tipo de conecciones para generar listas de resultaos)\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "gqDsD6Ty4Zny"
},
"source": [
"## Codigo generico para un request simple, con verificacion de status y manejo de errores en la coneccion"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pXAaIP1X4VZd"
},
"source": [
"Podemos ver que el objeto respuesta que nos ofrece la libreria `requests` contiene una multitud de objetos utiles para la validacion de una respuesta del servidor. \n",
"\n",
"Hay que notar tambien, dada la presencia de un metodo `r.close()` es que la conección esta abierta, y debemos cerrarla cuando ya no sea necesaria. (es el caso análogo a cerrar un archivo despues de escribir en el) para esto usamos un administrador de contextos, de la misma manera que se hace al abrir y cerrar archivos.\n",
"\n",
"Finalmente, nuestro código lo podemos escribir de la siguiente manera:\n",
"\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "4bMYRD7muy9C",
"outputId": "958eec5d-4baf-4634-c855-bacdd2e61894",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 87
}
},
"source": [
"from contextlib import closing # contextos para cerrar conecciones http\n",
"\n",
"url = 'https://mezaops.appspot.com/knock/'\n",
"\n",
"with closing(requests.get(url)) as newreq: # al terminar de usar newreq, la coneccion se cerrara\n",
" if newreq.ok: # tenemos una respuesta valida\n",
" print('Request exitoso') # se lo decimos al usuario\n",
" print('Respuesta:')\n",
" pp.pprint(newreq.json()) # ya tenemos un objeto json procesado \n",
" print('http status code: {}'.format(newreq.status_code))\n",
"\n",
" else: # Algo sucedio mal. probablemente en el servidor\n",
" print('Request a {} fallo por esta razon; {}'.format(\n",
" newreq.url,newreq.reason)) # le avisamos al usuario que ocurrio mal\n",
" print('http status code: {}'.format(newreq.status_code))\n",
"\n"
],
"execution_count": 88,
"outputs": [
{
"output_type": "stream",
"text": [
"Request exitoso\n",
"Respuesta:\n",
"{'ip': '35.225.45.29'}\n",
"http status code: 200\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Za8nY39Dz-la"
},
"source": [
"El servidor responde con error 404 cuando pides un url que no es `/knock/`, veamos como se comporta: "
]
},
{
"cell_type": "code",
"metadata": {
"id": "uCTps3Co0enP",
"outputId": "dcb79ef7-1165-42cb-cb65-e10e05c5b160",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 52
}
},
"source": [
"url = 'https://mezaops.appspot.com/knick/' # \"knick\", no \"knock\"\n",
"\n",
"with closing(requests.get(url)) as newreq: # al terminar de usar newreq, la coneccion se cerrara\n",
" if newreq.ok: # tenemos una respuesta valida\n",
" print('Request exitoso') # se lo decimos al usuario\n",
" print('Respuesta:')\n",
" pp.pprint(newreq.json()) # ya tenemos un objeto json procesado \n",
" print('http status code: {}'.format(newreq.status_code))\n",
"\n",
" else: # Algo sucedio mal. probablemente en el servidor\n",
" print('Request a {} fallo por esta razon; \"{}\"'.format(\n",
" newreq.url,newreq.reason)) # le avisamos al usuario que ocurrio mal\n",
" print('http status code: {}'.format(newreq.status_code))"
],
"execution_count": 86,
"outputs": [
{
"output_type": "stream",
"text": [
"Request a https://mezaops.appspot.com/knick/ fallo por esta razon; \"Not Found\"\n",
"http status code: 404\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "neU5uiT_1wjM"
},
"source": [
"# Código fuente del servidor\n",
"\n",
"\n",
"El siguiente es el codigo corriendo en el servidor (Google app engine con python 2.7, usando Flask) (lleva como 4 años ahi jajaj ya tengo que actualizarlo porque py2 ya no fifa)\n",
"\n",
"\n",
"```python\n",
"\n",
"\"\"\" Knocker - knock to trigger an open firewall port\n",
"\n",
" %his module implements a simple service infroming the\n",
" caller of his public IP address as seen from the cloud\n",
"\n",
"\"\"\"\n",
"from flask import Flask\n",
"from werkzeug.wrappers import Response\n",
"\n",
"app = Flask(__name__)\n",
"\n",
"from flask import request\n",
"\n",
"@app.route('/knock/', methods=['GET', 'POST'])\n",
"def whatismyip():\n",
" \"\"\"Return the callers IP plain and simple\"\"\"\n",
" return Response('Data: %s' % request.remote.addr)\n",
"\n",
"@app.errorhandler(404)\n",
"def page_not_found(e):\n",
" \"\"\"Return a custom 404 error.\"\"\"\n",
" return 'Sorry, nothing at this URL.', 404\n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "NgCHGxpl7F3N"
},
"source": [
"-Hector Lecuanda ([email protected])"
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment