<jupyter_start><jupyter_text>Máster Propio en Data Science y Big Data (IV Edición)  Arquitecturas y Paradigmas para Ciencia del Dato (APCD)  Técnicas de obtención de datos     Instructor                  <NAME>, <EMAIL>, @versae                        Postdoctoral Researcher en el Proyecto Europeo POSTDATA de la UNED            PhD, Estudios Hispánicos y Humanidades Digitales, University of Western Ontario, Canada            Máster en Inteligencia Artificial, Universidad de Sevilla, España                  Ex-Ingeniero de Investigación en la Stanford University, California            Ex-Director Técnico del laboratorio de investigación CulturePlex Lab en la University of Western Ontario, Canada          Índice1. **Introducción**2. Repositorios3. Scraping4. Extracción de información usando expresiones regulares5. Conclusiones6. Bibliografı́a  IntroducciónLa recolección de datos es el proceso de recopilación y medición de la información sobre un conjunto de variables especı́ficas.Aquı́ surge una pregunta, ¿necesitamos definir un modelo de datos para recolectar los datos asociados?El primer paso es saber qué se quiere obtener como resultado y qué variables nos harı́an falta.  IntroducciónExisten infinidad de opciones para recolectar datos dependiendo del proyecto y de la disciplina en la que trabajemos. Hoy en dı́a hay un punto común a todos estos procesos (y cada vez más necesario), esla digitalización de la información.Por dar una definición breve y simple, podrı́amos llamar digitalización al proceso de transformación de información analógica a formato digital. Esto facilita tareas como el almacenamiento, consulta, gestión, etc.  IntroducciónPartiendo del ámbito de la ciencia de datos, vamos a ver cuatro tipos de procedimientos en cuanto a recolección u obtención de datos se refiere:- Repositorios- Scraping- Extracción de información usando expresiones regulares- APIs  Índice1. Introducción2. **Repositorios**3. Scraping4. Extracción de información usando expresiones regulares5. Conclusiones6. Bibliografı́a  RepositoriosUn repositorio o depósito es un sitio centralizado donde se almacena y mantiene información digital, habitualmente bases dedatos o archivos informáticos. Los datos almacenados en un repositorio pueden distribuirse a través de una red informática (Internet) o de un medio fı́sico.Nosotros, principalmente, usaremos los repositorios para dos tareas:- Para almacenar y gestionar el código de nuestro proyecto- Para almacenar, descargar y gestionar los conjuntos de datos con los que trabajamos- Para facilitar el intercambio de datos  Repositorios- Pueden ser de acceso público o estar protegidos y necesitar de una autentificación previa- Los repositorios más conocidos son los de carácter académico (investigación) e institucional (datos abiertos)- Los repositorios suelen contar con sistemas de respaldo y mantenimiento preventivo y correctivo (tolerancia a fallos)  Formatos- Generales: CSV, TXT, WORD, JSON, XML, XLS- Optimizados: HDF5, Parquet, Arrow- Semánticos (LOD, SPARQL): RDF, Turtle, N3, JSON-LD ![Ejemplo de Turtle](https://miro.medium.com/max/573/1*WgqGqn1tcd_aSpQbN45aBw.png)  Repositorios de datos- https://www.kaggle.com/datasets- http://www.kdnuggets.com/datasets/index.html- https://archive.ics.uci.edu/ml/datasets.php- https://github.com/caesar0301/awesome-public-datasets- http://sevilla.idesevilla.opendata.arcgis.com/- http://datosabiertos.sevilla.org/- http://datos.madrid.es/portal/site/egob/  Repositorios de datos abiertos enlazados (LOD)- http://datos.bne.es/inicio.html- https://bnb.data.bl.uk/- https://www.europeana.eu/portal/en- https://wiki.dbpedia.org/- https://m.wikidata.org/wiki/Wikidata:Main_Page  Repositorios de código- https://bitbucket.org/- https://www.github.com/- https://sourceforge.net/- https://launchpad.net/  Índice1. Introducción2. Repositorios3. **Scraping**4. Extracción de información usando expresiones regulares5. Conclusiones6. Bibliografı́a  ScrapingWeb scraping es una técnica que utiliza programas software para extraer información de sitios web.Usualmente, estos programas simulan la navegación de un humano en Internet ya sea utilizando el protocolo HTTP manualmente, o incrustando un navegador en una aplicación.  ScrapingComo curiosidad: un concepto muy relacionado con el web scraping es la indexación que se produce en Internet. La indexación se lleva a cabo por programas (llamados spiders o crawlers) que rastrean y navegan automáticamente por toda la web. Esta técnica es la que adoptan la mayorı́a de los motores de búsqueda.Desde nuestro punto de vista, el web scraping se enfoca más en la transformación de datos sin estructura en la web en datos estructurados que pueden ser almacenados y analizados. Un campo en el que esta técnica es muy utilizada es el periodismo de datos (social media).  ScrapingPara llevar a cabo técnicas de scraping tenemos que tener en cuenta, esencialmente, dos puntos fundamentales:- Acceso al código HTML de la página (hay muchas formas de conseguir esto)- Un lenguaje de programación para obtener los datos que nos interesan- En nuestro caso, usaremos la consola de desarrollo de Firefox/Google Chrome y Python.  Scraping![Diagrama de una petición HTTP](http://www.hermosaprogramacion.com/wp-content/uploads/2015/01/http-protocolo-peticion.png)  Scraping![HTTP](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/images/HTTP.png)  Scraping![HTTP](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/images/HTTP_Steps.png)  Scraping - Uniform Resource Locator (URL)```protocol://hostname:port/path-and-file-name```- Protocol: HTTP, FTP, telnet, etc.- Hostname: DNS domain name (e.g., www.nowhere123.com) o dirección IP (e.g., 172.16.58.3).- Port: Puerto TCP de escucha.- Path: Ruta del recurso en el servidor.Ejemplo: `http://www.nowhere123.com/docs/index.html`  Scraping - Uniform Resource Identifier (URI)Una URI es una forma más general de URL. Añade un par de campos más.```protocol://hostname:port/path-and-file-name?queryfragment```- Fragment: Porción dentro del recurso.- Query: Parámetros de la petición, e.g., `name=Ana&age=30`Ejemplo:```http://www.nowhere123.com/docs/index.html?order=1table1```  Scraping - HTTP Request```GET /docs/index.html HTTP/1.1Host: www.nowhere123.comAccept: image/gif, image/jpeg, */*Accept-Language: en-usAccept-Encoding: gzip, deflateUser-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)```  Scraping - HTTP Request![reques](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/images/HTTP_RequestMessageExample.png)  Scraping - HTTP Request Methods- GET: Un cliente puede utilizar la petición GET para obtener un recurso web del servidor.- HEAD: Un cliente puede utilizar la solicitud HEAD para obtener el encabezado que habría obtenido una solicitud GET. Dado que la cabecera contiene la última fecha modificada de los datos, se puede utilizar para comparar con la copia de la caché local.- POST: Se utiliza para enviar datos al servidor web.- PUT: Pide al servidor que almacene los datos.- DELETE: Pide al servidor que borre los datos.  Scraping - HTTP Response```HTTP/1.1 200 OKDate: Sun, 18 Oct 2009 08:56:53 GMTServer: Apache/2.2.14 (Win32)Last-Modified: Sat, 20 Nov 2004 07:16:26 GMTETag: "10000000565a5-2c-3e94b66c2e680"Accept-Ranges: bytesContent-Length: 44Connection: closeContent-Type: text/html``` ```It works!```  Scraping - HTTP Response![response](https://www.ntu.edu.sg/home/ehchua/programming/webprogramming/images/HTTP_ResponseMessageExample.png)  Scraping - HTTP Response Status Codes- 1xx (Informativo): Solicitud recibida, el servidor continúa el proceso.- 2xx (Éxito): La solicitud fue recibida, comprendida, aceptada y atendida con éxito.- 3xx (Redirección): Se deben tomar más medidas para completar la solicitud.- 4xx (Error de cliente): La petición contiene una sintaxis errónea o no puede ser entendida.- 5xx (Error del servidor): El servidor no cumplió con una petición aparentemente válida.  Scraping - HTTP Response Status CodesAlgunos de los códigos de estado más comunes son:- 100 Continuar: El servidor recibió la solicitud y en el proceso de dar la respuesta.- 200 OK: La solicitud se ha cumplido.- 301 Movido permanentemente.- 302 Encontrado y redirigido (o Movido temporalmente).  Scraping - HTTP Response Status CodesAlgunos de los códigos de estado más comunes son:- 400 Bad Request: El servidor no pudo interpretar o entender la petición, probablemente un error de sintaxis en el mensaje de petición.- 401 Se requiere autenticación.- 403 Prohibido.- 404 No encontrado.  Scraping - HTTP Response Status CodesAlgunos de los códigos de estado más comunes son:- 500 Error interno del servidor: El servidor está confundido, a menudo causado por un error en el programa del lado del servidor que responde a la petición.- 502 Bad Gateway: Proxy o Gateway indica que recibe una mala respuesta del servidor de entrada.- 503 Servicio no disponible: El servidor no puede responder debido a sobrecargas o mantenimiento. El cliente puede volver a intentarlo más tarde.<jupyter_code>### Scrapping - HTTP
!!pip install requests

import requests

response = requests.get("https://httpbin.org/html")
print(response.status_code, response.ok)
print(response.headers)
### Scrapping - HTTP

print(response.text)<jupyter_output><empty_output><jupyter_text>EjercicioEscriba una función, `status(urls)`, que dada una lista de de URLs en `urls`, devuelve un diccionario usando como claves los códigos de estado de las peticiones HTTP y como valores la lista correspondiente de URLs que lo producen.Por ejemplo:urls = [    "https://httpbin.org/",    "https://httpbin.org/404",    "https://www.eltiempo.es/",    "https://www.pixar.com/cer890h76yt89j768y6590g43e9f4efv54er",]status(urls)    Debería producir{200: ['https://httpbin.org/', 'https://www.eltiempo.es/'], 404: ['https://httpbin.org/404', 'https://www.pixar.com/cer890h76yt89j768y6590g43e9f4efv54er']}<jupyter_code>urls = [
    "https://httpbin.org/",
    "https://httpbin.org/404",
    "https://www.eltiempo.es/",
    "https://www.pixar.com/cer890h76yt89j768y6590g43e9f4efv54er",
]

def status(urls):
    # Escriba su código aquí
    pass
        

status(urls)<jupyter_output><empty_output><jupyter_text>```{200: ['https://httpbin.org/', 'https://www.eltiempo.es/'], 404: ['https://httpbin.org/404',  'https://www.pixar.com/cer890h76yt89j768y6590g43e9f4efv54er']}``` EjercicioEscriba el código necesario para descargar el contenido alojado en `https://httpbin.org/html` y devolver las 10 palabras más comunes y su frecuencia absoluta (total de apariciones)<jupyter_code>url = "https://httpbin.org/html"
# Escriba aquí su solución<jupyter_output><empty_output><jupyter_text>```[('', 42), ('the', 34), ('and', 25), ('of', 22), ('to', 17), ('his', 14), ('a', 14), ('in', 11), ('had', 8), ('old', 6)]```  Scraping - HTMLIdealmente, todas las páginas web siguen una estructura bien definida semánticamente de acuerdo al Document Object Model (DOM). Esto nos permitirı́a extraer datos utilizando reglas como encontrar el elemento `` cuyo id es "subject" y devolver el "texto" que contiene.En el mundo real, el código HTML de una web no tiene por qué estar bien formado y anotado. Esto significa que necesitaremos estudiar la estructura de una web antes de extraer información de la misma.  Scraping - HTML```htmlMi webJavier de la RosaCiencia de Datos```  Scraping - HTML```htmlMi webJavier de la RosaCiencia de datosNoviembre 2019```  Scraping - `robots.txt`El archivo robots.txt es un documento que define qué partes de un dominio pueden ser analizadas por los rastreadores de los motores de búsqueda y proporciona un enlace al XML-sitemap (ver [ejemplo de sitemap](https://yoast.com/sitemap_index.xml)).``` robots.txt for http://www.example.com/User-agent: UniversalRobot/1.0User-agent: GoogleBotDisallow: /sources/dtd/User-agent: *Disallow: /nonsense/Disallow: /temp/Disallow: /newsticker.shtml```Ejemplo real: https://www.google.com/robots.txt  Scraping con Python- [BeautifulSoup](http://www.crummy.com/software/BeautifulSoup/bs4/doc/), encargada de crear un árbol con los elementos que componen una web y facilitar su acceso- [requests](https://requests.readthedocs.io/en/master/) para facilitar la ejecución de peticiones a servidores desde código Python. Aunque nativamente se puede usar [urllib3](https://urllib3.readthedocs.io/en/latest/index.html).- [html5lib](https://github.com/html5lib/html5lib-python), para mejorar el tratamiento de los elementos HTML<jupyter_code>!!pip install beautifulsoup4
!!pip install requests requests-html
!!pip install html5lib
### Scraping - requests vs urllib3
import requests

print("requests:",
    requests.get("http://httpbin.org/ip").json()
)


import urllib3
import json

http = urllib3.PoolManager()
response = http.request('GET', 'http://httpbin.org/ip')
print("urllib3: ",
    json.loads(response.data.decode('utf-8'))
)
### Scraping con Python

# elpais.com example - First part
from bs4 import BeautifulSoup
import requests

url = "https://elpais.com/"
html = requests.get(url).text
soup = BeautifulSoup(html, 'html5lib')
links = []
all_news_lines = soup.select('.articulo-titulo')
for line in all_news_lines:
    link = line.find('a')
    links.append(link)
links[:3]
### Scraping con Python

# elpais.com example - Second part
news = []
for link in links:
    new = link.text  # link.get("title")
    news.append(new)
news[:5]<jupyter_output><empty_output><jupyter_text>Selectores- Selector de tipo o etiqueta (todos)```csspp, span```- Selector descendente (dentro)```cssp spanh1 span``````htmlAlgo de texto especial!```  Selectores (cont.)- Selectores de ID```cssdestacado``````htmlSegundo párrafo```- Selector de clase```cssp.destacado``````htmlLorem ipsum dolor sit amet...```  Selectores (cont.)- Combinación de selectores básicos```css.aviso .especial``````htmlAlgo de texto especial!```- Hijos directos```csstd > span``````htmlTexto```  Selectores (cont.)- Precedencia directa (sin parentesco)```cssp + a``````htmlParagraph yenlace```- Posicionales```cssp:first span:last a:n-child(2)``````htmlPara 1 Span 1Span 2 Enlace 1 Enlace 2 Para 2``` EjercicioEscriba las expresiones de selección para los siguientes casos:    - Todos los `div` del documento.- Todos los `p` y `a`.- Todos los `a` descedientes de `p`.- Todos los elementos de las clase `nombreClase`.- Los `div` de las clase `nombreClase`.<jupyter_code># Escriba su código aquí
# Todos los div del documento.

# Todos los p y a.

# Todos los a descedientes de p.

# Todos los elementos de las clase nombreClase.

# Los div de las clase nombreClase.<jupyter_output><empty_output><jupyter_text>Scraping - Ejemplo: 43º Congreso de los EEUU```htmlMember NameBirth-DeathADAMS, <NAME>1837-1920ALBERT, <NAME>1816-1879ALBRIGHT, Charles1830-1880```<jupyter_code>### Scraping - Ejemplo: 43º Congreso de los EEUU
    
html = """<table border="1" cellspacing="2" cellpadding="3">
<tbody>
<tr>
<th>Member Name</th>
<th>Birth-Death</th>
</tr>
<tr>
<td><a href="http://bioguide.congress.gov/scripts/biodisplay.pl?index=A000035">ADAMS, <NAME></a></td>
<td>1837-1920</td>
</tr>
<tr>
<td><a href="http://bioguide.congress.gov/scripts/biodisplay.pl?index=A000074">ALBERT, <NAME></a></td>
<td>1816-1879</td>
</tr>
<tr>
<td><a href="http://bioguide.congress.gov/scripts/biodisplay.pl?index=A000077">ALBRIGHT, Charles</a></td>
<td>1830-1880</td>
</tr>
</tbody>
</table>"""
### Scraping - Ejemplo: 43º Congreso de los EEUU

soup = BeautifulSoup(html)
print(soup.prettify())
### Scraping - Ejemplo: 43º Congreso de los EEUU

links = soup.find_all('a')
for link in links:
    names = link.text
    full_link = link.get('href')
    print([names, full_link])
### Scraping - Ejemplo: 43º Congreso de los EEUU
import csv

with open("congress.csv", "w") as file:
    writer = csv.writer(file)
    writer.writerow(["Name", "Link"])
    links = soup.find_all('a')
    for link in links:
        names = link.text
        full_link = link.get('href')
        writer.writerow([names, full_link])

!cat congress.csv
### Scraping con Python

# Marca example - First Part
from bs4 import BeautifulSoup
import requests

url = "http://www.marca.com/futbol/primera/calendario.html"
soup = BeautifulSoup(requests.get(url).text, "html5lib")
jornadas = soup.find_all("div", "jornada")
### Scraping con Python

# Marca example - Second Part
for jornada in jornadas:
    nombre_jornada = jornada.find("caption").text
    partidos_jornada = jornada.find_all("tr")
nombre_jornada, partidos_jornada[5]
### Scraping con Python

# Marca example - Third Part
for partido_jornada in partidos_jornada[1:]:
    local = visitante = resultado = ""
    try:
        local = partido_jornada.find("td", "local").text.strip()
        visitante = partido_jornada.find("td", "visitante").text.strip()
        resultado = partido_jornada.find("td", "resultado").text.strip()
    except:
        pass
local, resultado, visitante
### Scraping con Python

# Marca example - Fourth Part
resultados = []
if "Villarreal" in [local, visitante]:
    partido = f"{local} vs {visitante}: {resultado} | {nombre_jornada}"
resultados.append(partido)
resultados
### Scraping con Python

resultados = []
url = "http://www.marca.com/futbol/primera/calendario.html"
equipo = "Villarreal"
soup = BeautifulSoup(requests.get(url).text, "html5lib")
jornadas = soup.find_all("div", "jornada")
for jornada in jornadas:
    nombre_jornada = jornada.find("caption").text
    partidos_jornada = jornada.find_all("tr")
    for partido_jornada in partidos_jornada[1:]:
        local = partido_jornada.find("td", "local").text.strip()
        visitante = partido_jornada.find("td", "visitante").text.strip()
        resultado = partido_jornada.find("td", "resultado").text.strip()
        if equipo in [local, visitante]:
            partido = f"{local} vs {visitante}: {resultado} | {nombre_jornada}"
            if partido not in resultados:
                resultados.append(partido)
resultados<jupyter_output><empty_output><jupyter_text>EjercicioDesde hace años, el portal de alexa.com mantiene listados de los sitios más visitados por país. En https://www.alexa.com/topsites/countries/ES se puede ver la lista de los 50 primeros. Usando la librería [`builtwith`](https://pypi.org/project/builtwith/), se pide escrapear los 50 sitios más visitados de España en el ranking de Alexa y devolver los sitios que usen el *framework* Javascript `React` (clave `'javascript-frameworks'` en el resultado devuelto por `builtwith`).(**Pista**: Para poder procesar una URL, debe tener el protocolo primero, `https://`. Adem'as algunas webs no funcionan con `builtwith`, así que hay que capturar las excepciones correspondientes)<jupyter_code>!!pip install builtwith
import builtwith

# Escriba su código aquí
url = ...
html = requests....
soup = BeautifulSoup(...)
selector = ...
sites_using_react = []
for element in soup.select(selector)[:10]:
    element_url = ...
    try:
        builts = builtwith.builtwith(element_url)
        frameworks = builts...
    except:
        frameworks = []
    if "React" in frameworks:
        sites_using_react.append(...)

sites_using_react<jupyter_output><empty_output><jupyter_text>Scrapping - Problemas- Dificultad a la hora de estudiar la estructura de una página web- Las páginas webs pueden cambiar a lo largo del tiempo- Nuestros programas también necesitan cambiar- Necesitamos revisar las leyes y los derechos de uso de las páginas webs para poder aplicar esta técnica- Contenido dinámico (☠️ Javascript ☠️) EjercicioDada la URL https://pythonprogramming.net/parsememcparseface/, se pide scrapearla y obtener el valor textual del id `yesnojs`.<jupyter_code># Escriba su código aquí<jupyter_output><empty_output><jupyter_text>Scraping - Contenido dinámicoEl módulo [`requests_html`](https://github.com/psf/requests-html) permite descargar HTML y ejecutar el contenido dinámico existente.<jupyter_code>%%python
from requests_html import HTMLSession

html = HTMLSession().get('https://pythonprogramming.net/parsememcparseface/').html
html.render()

print(html.find('#yesnojs', first=True).text)<jupyter_output><empty_output><jupyter_text>Scraping - Contenido dinámicoOtra opción, bastante más versátil, es [Selenium](https://selenium.dev/) (y sus [drivers para Python](https://selenium-python.readthedocs.io)), ya que permite controlar programáticamente una instancia real de un navegador.<jupyter_code>### Scraping - Contenido dinámico

# En Anaconda !!conda install -yq python-chromedriver-binary
!!pip install selenium chromedriver_binary
from selenium import webdriver
import chromedriver_binary  # Adds chromedriver binary to path

driver = webdriver.Chrome()
driver.get("http://www.python.org")
print(driver.title, "Python" in driver.title)
driver.quit()
### Scraping - Contenido dinámico
# renfe.es

driver = webdriver.Chrome()
driver.get("http://www.renfe.es")
### Scraping - Contenido dinámico
# renfe.es

origen = driver.find_element_by_id('IdOrigen')
origen.send_keys("SEVILLA-SANTA JUSTA")
destino = driver.find_element_by_id("IdDestino")
destino.send_keys("MADRID (TODAS)")
comprar = driver.find_element_by_css_selector("#datosBusqueda > button")
comprar.click()
### Scraping - Contenido dinámico
# renfe.es

precios = driver.find_elements_by_css_selector("tr.trayectoRow > td:nth-child(6)")
for precio in precios:
    print(precio.text)
driver.quit()<jupyter_output><empty_output><jupyter_text>Índice1. Introducción2. Repositorios3. Scraping4. **Extracción de información usando expresiones regulares**5. Conclusiones6. Bibliografı́a  Extracción de información usando expresiones regularesUna expresión regular (a menudo llamada también regex) es una secuencia de caracteres que forma un patrón de búsqueda, principalmente utilizada para la búsqueda de patrones en textos u operaciones de sustituciones.Haciendo uso de expresiones regulares sobre texto podremos extraer la información que nos interesa almacenar y/o gestionar.La teorı́a tras las expresiones regulares es compleja y muy amplia. Aquı́ sólo veremos unos ejemplos sencillos para entender el funcionamiento de esta técnica para nuestros objetivos.  Expresiones regulares- [`(\d{3}-){2}\d{3}`](https://regexper.com/%28%5Cd%7B3%7D-%29%7B2%7D%5Cd%7B3%7D)- [`(\W|^)(tontería|maldito|caray|caramba|madre\smía|ostras)(\W|$)`](https://regexper.com/%28%5CW%7C%5E%29%28tontería%7Cmaldito%7Ccaray%7Ccaramba%7Cmadre%5Csmía%7Costras%29%28%5CW%7C%24%29)- [`v[i!1][a@]gr[a@]`](https://regexper.com/v%5Bi!1%5D%5Ba%40%5Dgr%5Ba%40%5D)  Expresiones regulares en PythonSeguiremos haciendo uso del lenguaje Python. Estudiaremos las expresiones regulares trabajando sobre una serie de ejemplos.El paquete Python que usaremos es el paquete re. Una búsqueda de una expresión regular tı́pica serı́a:```pythonmatch = re.search(pat, str)```donde `pat` serı́a el patrón de la expresión regular y `str` la cadena de texto donde buscar. La búsqueda del patrón devuelve el resultado si lo encuentra o None en otro caso.<jupyter_code>### Expresiones regulares en Python

import re
text = "nos encanta el:data:science"
match = re.search(r"el:\w\w\w\w", text)
if match:
    print("found:", match.group())
else:
    print("not found")<jupyter_output><empty_output><jupyter_text>Expresiones regulares en PythonPatrones básicos que podemos usar:- `a`, `X`, `9`: Caracteres normales buscan la coincidencia de ellos mismos.- `\w`: Busca la coincidencia de letras, dı́gitos o guión bajo `[a-zA-Z0-9 ]`. `\W` coincide con cualquier carácter que no sea uno de los anteriores.  Expresiones regulares en PythonMás patrones básicos:- `\s`: Busca la coincidencia de un único espacio en blanco (space, newline, return, tab, form `[ \n \r \t \f]`). `\S` coincide con cualquier carácter que no sea uno de los anteriores.- `\d`: Busca coincidencias de dı́gitos decimales `[0-9]`.- `\`: Se usa también para tratar los caracteres especiales como caracteres normales (los escapa).  Expresiones regulares en PythonAdemás de estos patrones, podremos usar una serie de caracteres especiales:- `.`: Coincide con cualquier carácter del texto.- `ˆ`, `$`: Sirven para indicar que la búsqueda del patrón sea al comienzo o al final del texto respectivamente.- `*`, `+`, `?`: Se usan para la repetición de ocurrencias en el texto.- `{}[]()-`: Estos caracteres se usan para la definición de grupos y conjuntos dentro del patrón de búsqueda.<jupyter_code>### Expresiones regulares en Python
text = "www.example.com"
numbers = "12345"
mail = "<EMAIL>"

print(re.search(r"www", text))
print(re.search(r"wwwe", text))
print(re.search(r"\d\d\d", numbers))
print(re.search(r"\w\w@\w\w", mail))<jupyter_output><empty_output><jupyter_text>Expresiones regulares en PythonCaracteres para aplicar repetición de elementos:- `+`, buscarı́a 1 o más ocurrencias del patrón a su izquierda. Por ejemplo, `i+` equivale a uno o más caracteres `i`- `*`, buscarı́a 0 o más ocurrencias del patrón a su izquierda- `?`, buscarı́a 0 o 1 ocurrencia del patrón a su izquierda<jupyter_code>### Expresiones regulares en Python
text = "www.example.com"
numbers_words = "hola12345caracola"
numbers_words_spaces = "hola12345 caracola"
mail = "<EMAIL>"

print(re.search(r"w+", text))
print(re.search(r"w?", text))
print(re.search(r"\w\d*\w", numbers_words))
print(re.search(r"\w\d+\w", numbers_words))
print(re.search(r"\w\d+\s+\w", numbers_words_spaces))
print(re.search(r"^p\w+", mail))
print(re.search(r"p\w+", mail))<jupyter_output><empty_output><jupyter_text>Expresiones regulares en PythonDentro de nuestras expresiones regulares podemos usar corchetes.El uso de corchetes indica un conjunto de caracteres. Por ejemplo, `[abc]` buscarı́a `a`, `b` o `c`.Dentro de los corchetes podemos usar los patrones que hemos visto, como `\w`, `\s`, etc. Sólo hay una excepción, el carácter `.` significa un punto y no cualquier carácter.<jupyter_code>### Expresiones regulares en Python
text = "sevilla <EMAIL> mas pruebas extras"
match = re.search(r"\w+@\w+", text)
if match:
    print(match.group())
match = re.search(r"[\w.-]+@[\w.-]+", text)
if match:
    print(match.group())<jupyter_output><empty_output><jupyter_text>Expresiones regulares en Python - Extracción de gruposUna caracterı́stica muy útil cuando trabajamos con expresiones regulares son los grupos. Usar grupos nos permite separar el patrón por partes que luego podemos elegir individualmente.Para conseguir esto, englobamos las partes que nos interesan con paréntesis. Para obtener los valores de los grupos obtenidos, simplemente usamos la función `match.group()`, pasándole como parámetro el ı́ndice grupo que queremos seleccionar. Si no le pasamos ningún valor obtendremos el resultado de todo el patrón (funcionamiento normal).<jupyter_code>### Expresiones regulares en Python
text = "betis <EMAIL> mas pruebas extras"
match = re.search("([\w.-]+)@([\w.-]+)", text)
if match:
    print(match.group()) ## the whole match
    print(match.group(1)) ## the user
    print(match.group(2)) ## the host<jupyter_output><empty_output><jupyter_text>Expresiones regulares en Python - findallEl método `re.search()` nos devuelve el primer resultado del patrón buscado sobre un texto. Usando el método `findall()` obtenemos todos los resultados del patrón buscado sobre el texto, devueltos en una lista de resultados.Por ejemplo, si queremos encontrar un patrón en un archivo de texto necesitarı́amos iterar sobre todas las lı́neas e ir buscando el patrón en cada una. Si usamos `findall()` podrı́amos pasar como parámetro el propio archivo completo, dejando que el método haga la búsqueda en todo el documento.<jupyter_code>### Expresiones regulares en Python
text = "betis <EMAIL> <EMAIL> mas <EMAIL> pruebas extras"
emails = re.findall(r"[\w\.-]+@[\w\.-]+", text)
for email in emails:
    print(email)<jupyter_output><empty_output><jupyter_text>Expresiones regulares en Python - findallSi unimos la forma de trabajar de los grupos con el método `findall()`, en vez de obtener una lista de resultados obtendremos una lista de tuplas.Cada una de las tuplas contendrá los resultados de los grupos (group(1), group(2), etc.).<jupyter_code>### Expresiones regulares en Python
text = "betis <EMAIL> <EMAIL> mas <EMAIL> pruebas extras"
tuples = re.findall(r"([\w\.-]+)@([\w\.-]+)", text)
print(tuples)
for pair in tuples:
    print(pair[0])
    print(pair[1])<jupyter_output><empty_output><jupyter_text>Expresiones regulares en Python - SubstitutionPor último, la función `re.sub(pat, replacement, str)` busca todas las coincidencias del patrón `pat` en la cadena `str` y los reemplaza con el valor de `replacement`.La cadena replacement puede incluir los valores `\1`, `\2`, etc. en referencia al texto de `group(1)`, `group(2)`, etc.<jupyter_code>### Expresiones regulares en Python
text = "betis <EMAIL> <EMAIL> mas <EMAIL> pruebas extras"
print(re.sub(r"([\w\.-]+)@([\w\.-]+)", r"<EMAIL>", text))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11361271074724208, 'fraction_numerical': 0.017622979254839383, 'mean_word_length': 5.0206766917293235, 'pattern_counts': {'":': 0, '<': 127, '<?xml version=': 0, '>': 130, 'https://': 52, 'lorem ipsum': 0, 'www.': 33, 'xml': 1}, 'pii_count': 1, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '18', 'dir': 'jupyter-structured-clean-dedup', 'id': '624597', 'license': 'Apache-2.0', 'n_tokens_mistral': 10330, 'n_tokens_neox': 9956, 'n_words': 2994, 'path': 'scraping.ipynb', 'repo_name': 'versae/master-us-apcd', 'size': '29033'}