====== 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]]