====== Python: Web Scraping ======
import webbrowser
# Abre el navegador en cierta página:
webbrowser.open("https://example.org")
En el web scraping, normalmente se siguen unos pasos:
- Definir la URL de base
- Hacer petición a esa URL
- Obtener respuesta HTML
- Extraer datos del HTML
- Volver al paso 2 con nuevas URLs.
===== XPath =====
Lenguaje para el procesado de documentos XML.
* [[http://xpather.com/|XPath online real-time tester and generator for XML & HTML]]
* [[https://devhints.io/xpath|Xpath cheatsheet]]
Buscar en cualquier parte del documento:
//
Para buscar cualquier etiqueta ''h1'':
//h1
Después de definir el //step//, añadimos un **predicado** al //step// para afinar más la búsqueda:
//h1[@class="title"]
Con la expresión anterior estaríamos buscando etiquetas ''h1'' por el atributo ''class'' con el valor ''title''.
Podemos usar operadores lógicos para añadir expresiones:
//h1[@class="title" or @class="subtitle"]
Una vez encontremos un elemento de partida, podemos hacer búsquedas dentro de él añadiéndolo a la expresión:
//div[@class="container container-uno"]//li
En los predicados podemos indicar la posición de los elementos que queremos buscar:
//div[@class="container container-uno"]//li[1]
Solo nos quedaríamos con el primer ''li''
Si solo nos queremos quedar con el texto que contiene cierto elemento:
//h1[contains(text(), "This is")]/text()
Si solo queremos obtener el valor de un atributo:
//h1[contains(text(), "This is")]/@class
En los navegadores Google Chrome y Mozilla Firefox se pueden probar expresiones XPath desde la consola de las herramientas de desarrolladores introduciendo las expresiones en ''$x()'': ''$x(%%"//h2[1]"%%)''
==== Funciones ====
* ''position''
* ''contains''
* ''starts-with''
* ''not''
* ''text''
//div[@class="container container-uno"]//li[starts-with(@id, "elem")]
Obtiene todos los ''li'' cuyo atributo ''id'' comience por ''elem''.
Si queremos buscar por un texto:
//h1[contains(text(), "Hola, mundo")]
Nos buscará todos los elementos ''h1'' que contengan el texto ''Hola, mundo''.
===== Descargar ficheros =====
El módulo no viene incluido por defecto en la instalación de Python así que hay que instalarlo con pip: ''pip install requests''
* [[https://requests.readthedocs.io|Requests: HTTP for Humans™]]
import requests
# Con la petición vamos a personalizar la cabecera HTTP para
# que el servidor "crea" que estamos utilizando un navegador web
headers = {
"user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36"
}
url = "https://example.org/page"
res = requests.get(url, headers=headers)
# 'res' es el objeto con la respuesta
# Resultado de la petición
res.status_code
# Resultado
# 200 -> Si ha ido bien
# El contenido de la descarga está en:
res.text
===== Procesar HTML =====
Tenemos dos posibilidades interesantes con los siguientes módulos de Python:
* [[https://lxml.de/|lxml]]
* [[https://www.crummy.com/software/BeautifulSoup/|Beautiful Soup]]
Hay una tercera posibilidad que es **Scrapy**, que es ya todo un framework para navegar por páginas, extraer información y moverse por las diferentes páginas de un mismo sitio.
La ventaja de Beautiful Soup frente a lxml es poder movernos por elementos sin conocer su id o clase, solo con tener un punto de partida. Ejemplos de esto es el método ''find_next_sibling()''
Es necesario instalar BeautifulSoup: ''pip install beautifulsoup4''
* [[https://www.crummy.com/software/BeautifulSoup/|Beautiful Soup: We called him Tortoise because he taught us]]
import bs4
import requests # es el módulo que hará la descarga
# Para las webs que no permiten scraping, le hacemos creer que
# nos estamos conectando con un navegador
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36'}
res = requests.get("https://www.amazon.es/dp/B07DLRWFF8/", headers = headers)
# Nos aseguramos de que no ha habido errores al descargar la web anterior
res.raise_for_status()
soup = bs4.BeautifulSoup(res.text, "html.parser")
# Se crea un objeto 'BeautifulSoup' donde se podrán hacer búsquedas
# utilizando selectores CSS, por ejemplo:
elementos = soup.select('')
# Eliminamos el HTML quedándonos solo con el texto:
elementos[0].text
# Quitamos también saltos de línea y espacios:
elementos[0].text.strip()
===== Interacción automática con webs =====
Usar el navegador sin intervención del usuario, de forma automática.
Es necesario instalar Selenium: ''pip install selenium''
* [[https://selenium-python.readthedocs.io/|Selenium with Python]]
from selenium import webdriver
# Para lanzar el Firefox
browser = webdriver.Firefox()
# Para abrir una web en esa instancia de Firefox
browser.get("http://example.org")
Si queremos simular un click en cierto elemento:
# Buscamos el elemento mediante selector CSS:
elem = browser.find_element_by_css_selector('')
elem.click()
Si sabemos que hay más de un elemento con el mismo selector CSS, el método cambia:
# Buscamos los elementos mediante selector CSS:
elem = browser.find_elements_by_css_selector('')
^ Método ^ Objeto / lista devuelto ^
| ''browser.find_element_by_class_name(name)'' | Elementos que tengan la clase ''name'' |
| ''browser.find_elements_by_class_name(name)'' | Elementos que tengan la clase ''name'' |
| ''browser.find_element_by_css_selector(selector)'' | Elemento que tengan el ''selector'' CSS |
| ''browser.find_elements_by_css_selector(selector)'' | Elementos que tengan el ''selector'' CSS |
| ''browser.find_element_by_id(id)'' | Elemento con el id igual a ''id'' |
| ''browser.find_elements_by_id(id)'' | Elementos con el id igual a ''id'' |
| ''browser.find_element_by_link_text(texto)'' | Enlace con el texto ''texto'' |
| ''browser.find_elements_by_link_text(texto)'' | Enlaces con el texto ''texto'' |
| ''browser.find_element_by_partial_link_text(texto)'' | Enlaces que contengan el texto ''texto'' |
| ''browser.find_elements_by_partial_link_text(texto)'' | Enlaces que contengan el texto ''texto'' |
| ''browser.find_element_by_name(name)'' | Elemento con un atributo ''name'' |
| ''browser.find_elements_by_name(name)'' | Elementos con un atributo ''name'' |
| ''browser.find_element_by_tag_name(name)'' | Elemento con la etiqueta ''name'' |
| ''browser.find_elements_by_tag_name(name)'' | Elementos con la etiqueta ''name'' |
==== Enviar texto ====
Por ejemplo para formularios:
# Buscamos el elemento
elem = browser.find_element_by_css_selector('ruta CSS al elemento')
elem.send_keys('texto')
# Enviamos el formulario
elem.submit()
==== Navegación ====
* ''browser.back()'': página anterior
* ''browser.forward()'': página siguiente
* ''browser.refresh()'': actualizar página
* ''browser.quit()'': cerrar el navegador
==== Scraping ====
Al seleccionar un elemento, podemos acceder al texto con el atributo ''text'':
elem = browser.find_element_by_css_selector('')
elem.text
Si quisiésemos todo el código HTML de la página:
elem = find_element_by_css_selector('html')
===== Recursos =====
* [[https://towardsdatascience.com/everything-you-need-to-know-about-web-scraping-6541b241f27e|Everything you Need to Know About Web Scraping]]
* [[https://automatetheboringstuff.com/2e/chapter12/|Automate the Boring Stuff with Python: WEB SCRAPING]]
* [[https://toscrape.com/|Web Scraping Sandbox]]