Mostrando entradas con la etiqueta request. Mostrar todas las entradas
Mostrando entradas con la etiqueta request. Mostrar todas las entradas

sábado, 25 de febrero de 2023

Web Scraping

 ¿Que es el Web Scraping?

Es usar, en este caso, Python para extraer contenido  y datos de un sitio web. Para poder usarlo es necesario tener unos conocimientos mínimos de como se estructura el lenguaje HTML. Este se basa en etiquetas. Y precisamente esas etiquetas son las que vamos a utilizar para extraer esos datos. 

Vamos a verlo con un ejemplo.  Supongamos que nos interesa extraer el valor liquidativo de un fondo de inversión. Ese valor liquidativo se publica por ejemplo en la siguiente página web.


https://www.quefondos.com/es/fondos/ficha/index.html?isin=ES0164853014


pagina web donde se recoge el valor del fondo

Para poder extraer el valor liquidativo tenemos que instalar las librerías - BeautifulSoup - y - Request -. 

La librería BeautifulSoup nos sirve para extraer información de contenido en formato HTML o XML.

La librería Requests se utiliza en Python para hacer peticiones HTTP.

Usaremos también la librería webbrowser que ya está instalada por defecto en la biblioteca estándar de Python y que usaremos al final del programa para abrir el navegador y ver la página web de donde hemos extraído los datos. Si no tienes instaladas las librerías lo puedes hacer con:

$ pip install beautifulsoup4
$ pip install requests

 El programa empezará importando las librerías y estableciendo en una variable la página web desde donde vamos a descargar los datos.

main.py

#!/usr/bin/env python3

# Si no los tenemos, instalamos BeautifulSoup y requests
# pip install beautifulsoup4
# pip install requests

from bs4 import BeautifulSoup
import requests
import webbrowser # esta instalado por defecto, nos lleva a una pagina web desde python.

URL = "https://www.quefondos.com/es/fondos/ficha/index.html?isin=ES0164853014"

Vamos a utilizar requests para obtener un objeto donde se encuentre la página que queremos con todos sus elementos.

...
URL = "https://www.quefondos.com/es/fondos/ficha/index.html?isin=ES0164853014"
page = requests.get(URL)

Si ejecutáramos este programa y el servidor nos proporcionase la página de forma correcta, obtendríamos la siguiente respuesta.

<Response [200]>

Sin embargo lo que a nosotros nos interesa es el contenido de esa página, lo que conseguiremos usando page.content, y luego extraeremos el código HTML para poder buscar el dato que queremos con el argumento "html.parser".

...
URL = "https://www.quefondos.com/es/fondos/ficha/index.html?isin=ES0164853014"
page = requests.get(URL)
# Lo que nos interesa de la pagina es el contenido por lo que usamos page.content
# y luego que lo pase en formato html
soup = BeautifulSoup(page.content, 'html.parser')

Si imprimimos esta variable "soup" obtendremos el código HTML de la página.

Código HTML de la página. "soup" 

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html lang="es-ES">
<head>
<title>CAIXABANK SELECCION TENDENCIAS, FI PLUS (ES0164853014) · CAIXABANK ASSET MGMT</title>
<meta content="Fondo de inversión, Ficha, Informe, CAIXABANK SELECCION TENDENCIAS, FI PLUS, ISIN, ES0164853014, Gestora, CAIXABANK ASSET MGMT, Política de inversión, Valor liquidativo, Rentabilidad, Ranking, Riesgo, Rating VDOS, Comisiones, Gráfico, Evolución, Histórico" name="keywords"/>
<meta content="Consulta el informe de CAIXABANK SELECCION TENDENCIAS, FI PLUS (ISIN:ES0164853014). Gestionado por
CAIXABANK ASSET MGMT. Categoría VDOS: MIXTO FLEXIBLE" name="description"/>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
<meta content="no-cache" http-equiv="cache-control">
<meta content="no-cache" http-equiv="pragma">
<meta content="index, follow" name="robots">
...
<div class="w100">
<h4 id="bodytitle_h3_3">Última valoración</h4>
<p>
<span class="floatleft">Valor liquidativo: </span><span class="floatright">14,677700 EUR</span>
</p>
<p>
<span class="floatleft">Patrimonio (miles de euros):</span><span class="floatright">1.338.861,29</span>
</p>
<p>
<span class="floatleft">Fecha: </span><span class="floatright">16/02/2023</span>
</p>
</div>
...


Ahora viene la parte manual del tema y lo más importante. Lo que necesitamos es encontrar dentro de que etiquetas está el valor que queremos localizar. Dentro de todo el código HTML de la página el valor liquidativo está dentro de la etiqueta <div class="w100">. Sin embargo al buscar en la página por esta etiqueta van a aparecer muchos elementos, más de los que nos gustaría, pero por lo menos ya hemos hecho una primera acotación.

Aquí os muestro el código que queremos buscar dentro de la página. Para analizar una página web una buena herramienta que se puede utilizar, es dentro del propio navegador buscar en herramientas, "herramientas para desarrolladores". Eso nos facilitará ver el código de la parte que nos interesa capturar. En nuestro caso es el siguiente:

<div class="w100">
<h4 id="bodytitle_h3_3">Última valoración</h4>
<p>
<span class="floatleft">Valor liquidativo: </span><span class="floatright">14,574500 EUR</span>
</p>
<p>
<span class="floatleft">Patrimonio (miles de euros):</span><span class="floatright">1.328.576,22</span>
</p>
<p>
<span class="floatleft">Fecha: </span><span class="floatright">19/02/2023</span>
</p>
<p>
<span class="floatleft">1 día: </span><span class="floatright"><span class="igual">0,00%</span></span>
</p>
</div>

 Para ello usaremos:

result = soup.find_all('div', class_="w100")

En la variable result se almacenará en forma de lista cada una de las etiquetas <div> que a la vez contengan la clase con el nombre "w100". Una de ellas contendrá el valor liquidativo que estamos buscando. Pero como en el código hay varias de ellas tenemos que buscar algo que diferencie la que nosotros buscamos del resto. En este caso, dentro de la etiqueta <div class="w100"> la que contiene el valor liquidativo es la que dentro tiene esta etiqueta <h4 id="body_title_h3_3>. Lo que haremos será iterar sobre la lista result buscando con i.find() aquella que contenga esta ultima etiqueta. Cuando la encontremos, usaremos el mismo método para localizar la etiqueta que contiene el valor liquidativo. El código completo de la aplicación sería el siguiente:

main.py

#!/usr/bin/env python3

# Si no los tenemos, instalamos BeautifulSoup y requests
# pip install beautifulsoup4
# pip install requests

from bs4 import BeautifulSoup
import requests
import sys
import webbrowser  # esta instalado por defecto, nos lleva a una pagina web desde python.

URL = "https://www.quefondos.com/es/fondos/ficha/index.html?isin=ES0164853014"
page = requests.get(URL)
# Lo que nos interesa de la pagina es el contenido por lo que usamos page.content
# y luego que lo pase en formato html
soup = BeautifulSoup(page.content, 'html.parser')
# Para la busqueda se pasa la etiqueta html en la que esta contenido el dato
# result = soup.find_all('span', class_="floatright")
result = soup.find_all('div', class_="w100")

valores = {}
for i in result:
    # getText() - elimina todas las etiquetas html y nos deja solo el contenido de texto.
    # find() - busca la primera conformidad con el parámetro buscado.
    # print(i.getText())
    key = i.find('h4', id="bodytitle_h3_3")
    if key is not None:
        titulo = i.find('h4', id="bodytitle_h3_3")
        valor = i.find('span', class_='floatright')
        print(titulo)
        print(valor)
        print(f"{titulo.getText()}: {valor.getText()}")
    print("------------------------------------------------------")

# Nos muestra la página web que le pasemos la url.
webbrowser.open(URL)
Finalmente, la salida que buscamos sería la siguiente:

------------------------------------------------------
<h4 id="bodytitle_h3_3">Última valoración</h4>
<span class="floatright">14,574500 EUR</span>
Última valoración: 14,574500 EUR
------------------------------------------------------
Puedes encontrar el código completo del programa en el siguiente enlace.


sábado, 1 de agosto de 2020

Flask 11. Formularios en Flask


¿Cómo exportar el informe de SAP a Excel en 3 sencillos pasos? : SAP exportar a Excel o imprimir a archivo


Trabajando con formularios en Flask.



Ejemplo de Formulario usando el método POST.


    La manera habitual en la que enviaremos información a las páginas de nuestra aplicación web será usando los formularios definidos en Html5. Usaremos los métodos GET y POST de los que ya hablamos anteriormente en Flask 8. Peticiones Http y métodos GET y POST . Vamos a repasar estos conceptos.

En este ejemplo práctico construiremos una vista que funcione como calculadora. Usaremos una plantilla en la que pediremos dos números y un operador (suma, resta, multiplicación y división). Según el operador que se elija se mostrará el resultado correspondiente. Usaremos primero el método POST para enviar el formulario y posteriormente haremos lo mismo con el método GET.

El primer paso será construir el directorio de trabajo de la manera habitual:


Luego crearemos una plantilla base que llamaremos base.html que usaremos para definir nuestra plantilla, la cual contendrá el código de la calculadora. Estará situada en la carpeta "templates". En este ejemplo no habrá ningún archivo css con los que el directorio static estará vacío.

base.html


Creamos dentro del directorio "templates" un archivo llamado "formulario_post.html" que contendrá el código html del ejemplo. Voy a poner el código y luego comentamos el funcionamiento.

formulario_post.html





Línea 1: importamos el archivo base.html que es la plantilla padre la cual contiene la estructura básica html sobre la que trabajaremos. Es una especie de copia y pega del código de esta plantilla padre en la hija.

línea 3: usamos el bloque que definirá el titulo de la plantilla con la que estamos trabajando.

Línea 5-32: Aquí va la parte principal del programa. Como antes, utilizaremos un bloque de contenido que insertará todo el código dentro del contenido que hemos especificado en la plantilla base.html.

Lo primero definimos un formulario. Este se enviará con el método "POST" a la página principal de nuestro proyecto. (127.0.0.1:5000/). Esta URL se obtiene con el método url_for de la función "calculadora_post" que definiremos luego en el archivo "inicio.py".

En el formulario primeramente aparecen dos campos de texto donde se introducirán los números que participarán en la operación. Después va el código de una lista desplegable donde irá el tipo de operación que queremos realizar (suma, resta, multiplicación y división). Y para finalizar ponemos un botón que enviará nuestro formulario.

Pasamos a describir inicio.py:



Línea 1-2: Aquí comenzamos importando las diferentes bibliotecas de flask que necesitamos y construimos la aplicación.

Línea 4: Definimos con el decorador la ruta principal de nuestra aplicación. La que se llamará nada mas que ejecutes el programa. (localhost:5000/). También le decimos que a esta URL se le puede llamar tanto con el método "GET" como con el "POST". Cuando se aceda a esta vista por primera vez, nada más que ejecutes el programa se utilizará el método Get (el que llama a las páginas en Flask por defecto).

Construimos la Función calculadora_post y utilizamos el método request.method para saber con que método se está llamando a la vista.

Es este punto te ánimo a que ejecutes la aplicación y vamos viendo como funciona el programa. 

(miEntornoVirtual) user@ubuntu:~/Escritorio/nFlask/11$ python inicio.py
 * Serving Flask app "inicio" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 164-357-938
127.0.0.1 - - [31/Jul/2020 18:35:09] "GET / HTTP/1.1" 200 -


En el momento que la ejecutas, se intenta cargar la página de inicio y como Flask llama a las vistas con el método GET el condicional de la línea 6 se activa y en consecuencia se renderiza la plantilla "formulario_post.html". Deberías ver algo como esto:




Rellenamos el primer número con, por ejemplo, un 3 y el segundo número con, por ejemplo, un 2. Elegimos la operación a realizar, yo escogeré la suma y le damos al botón enviar. En el formulario hemos dicho que cuando se envíe, lo haga con el método "POST" y a la página principal. Esto hará que se ejecute el condicional de la línea 8.



Utilizamos el método request.form.get para obtener el primer y segundo número, así como el tipo de operación. Ten en cuenta que lo que retorna este método es un diccionario:

{"primer_numero": "3", "segundo_numero":"2", "operacion": "+"}

en el que tanto la clave como el valor asociado son de tipo string. Como luego vamos a realizar operaciones con los números necesitamos convertirlo en eso, en números con los que se pueda operar. Es por ello el uso de la instrucción int( ... ), para convertir los números texto en números para operar.

Ahora le decimos al programa que intente (try) ejecutar el siguiente código y si no lo consigue por que haya algún error (except) que muestre el mensaje "no se ha podido realizar la operación". Esto ocurrirá si por ejemplo  intentas dividir algo por cero.

Lo que hay dentro de la instrucción Try sirve para decidir cual es la operación a realizar con los números en base al valor retornado por request.form.get("operación") que puede ser +,-,* o /. Finalmente si se ha podido realizar la operación se envía el resultado mediante una variable y se renderiza la página "formulario_post.html" de nuevo.


Ejemplo de Formulario usando el método GET


No es nada habitual pero vamos a ver el mismo programa, en este caso enviando la información del formulario usando el método GET. En este caso tenemos que modificar ligeramente tanto el archivo inicio.py como la plantilla "formulario_post.html". 

En este último voy a cambiar el nombre para que no haya confusiones a "formulario_get.html", también le cambio el texto del bloque del título a "Calculadora método GET" y el formulario enviara la información a la dirección url de la función calculadora_get utilizando el método GET.


formulario_get.html





En el archivo inicio.py hay que cambiar alguna cosa más. Esto se debe a que ahora al acceder a la ruta de inicio localhost:5000/ siempre lo hacemos con el método GET por lo que no nos vale el condicional que utilizamos antes que diferenciaba cuando accediamos con GET y cuando con POST. En este caso debemos determinar cuando acedemos a la ruta directamente y cuando lo hacemos porque el formulario envía argumentos a esa dirección. 

Al usar el método GET el formulario enviará argumentos a la página de inicio. Por lo tanto vamos a utilizar el condicional junto con el método request.args para saber cuando ocurre una cosa u otra.

Si es la primera vez que hemos accedido, es decir que no acedemos a través del formulario, la URL no tendrá ningún tipo de argumentos, por lo que si utilizamos:

len(request.args) este será igual a cero, no habrá argumentos. Nota: La instrucción len( .. ) nos da la longitud de la cadena que contiene.

Por tanto si accedemos con el método GET y la longitud de request.args es cero entonces mostramos una plantilla con el formulario.

Si por el contrario (... else) es el formulario el que envía los datos o argumentos a la URL esta instrucción nos devolverá una longitud igual a 3 ya que contiene un diccionario con los elementos enviados por el formulario (numero uno, dos y operación). Por tanto el programa realizará los cálculos con los número enviados, en base a la operación seleccionada y volverá a llamar a la plantilla "formulario_get.html" para mostrar el resultado. Para recuperar los elementos de ese diccionario que contiene los valores que hemos enviado usamos la instrucción:

request.args.get( "...elemento a recuperar del formulario (name)...")

inicio.py



Al ejecutar el programa fíjate como en la URL aparece ahora, junto con la dirección, los datos enviados lo que es propio del método GET.



No obstante cuando enviemos información con formularios debemos siempre usar el método POST. Si no es así, y optamos por el método GET y por tanto enviamos información en la URL, deberíamos usar al menos RUTAS DINAMICAS para enviar la información del formulario:

@app.route("/calculadora/<num1>/<num2>/<operacion>", methods=["get"])
def calculadora_var(operador,num1,num2):

y no de la forma que hemos visto en este ejemplo.


Puede obtener más información sobre como definir formularios en html5 en https://developer.mozilla.org/es/docs/Learn/HTML/Forms