Como la idea es hacer Aprendizaje Automático en Español, se me ocurrió buscar textos en castellano y recordé a Hernan Casciari que tiene los cuentos de su blog disponibles online y me pareció un buen desafío.
Para quien no conozca a Hernan Casciari, es un escritor genial, hace cuentos muy entretenidos, de humor (y drama) muy reales, relacionados con su vida, infancia, relaciones familiares con toques de ficción. Vivió en España durante más de una década y tuvo allí a su primera hija. En 2005 fue premiado como “El mejor blog del mundo” por Deutsche Welle de Alemania. En 2008 Antonio Gasalla tomó su obra “Más respeto que soy tu madre” y la llevó al teatro con muchísimo éxito. Escribió columnas para importantes periódicos de España y Argentina hasta que fundó su propia editorial Orsai en 2010 donde no depende de terceros para comercializar ni distribuir sus productos y siempre ofrece versione en pdf (gratuitos). Tiene 7 libros publicados, apariciones en radio (Vorterix y Perros de la Calle) y hasta llevó sus historias a una genial puesta en escena llamada “Obra en Construcción” que giró por muchas provincias de la Argentina, España y Uruguay.
Agenda del Día: “NLP tradicional”
Lo cierto es que utilizaremos la librería python NLTK para NLP y haremos uso de varias funciones y análisis tradicionales, me refiero a que sin meternos – aún- en Deep Learning (eso lo dejaremos para otro futuro artículo).
Obtener los Datos (los cuentos)
Exploración Inicial
Limpieza de datos
Análisis Exploratorio
Análisis de Sentimiento
Modelado de Tópicos
Vamos al código!
1 – Obtener los Cuentos
Para obtener los textos, haremos webscraping (LEER ARTíCULO) en el blog de Hernan Casciari, recorreremos los cuentos que afortunadamente están clasificados en directorios por año, del 2004 al 2005 y guardaremos todos los posts de cada año en un archivo txt.
ATENCIóN: Este código puede tardar MUCHOS minutos en descargar todos los textos, pues para ser amables con el servidor, haremos un sleep(0.75) entre cada request (y son 386 cuentos).
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# Web scraping, pickle imports
importrequests
frombs4 importBeautifulSoup
importpickle
fromtimeimportsleep
# Web Scrapes transcript data from blog
defurl_to_transcript(url):
'''Obtener los enlaces del blog de Hernan Casciari.'''
page=requests.get(url).text
soup=BeautifulSoup(page,"lxml")
print('URL',url)
enlaces=[]
fortitle insoup.find_all(class_="entry-title"):
foraintitle.find_all('a',href=True):
print("Found link:",a['href'])
enlaces.append(a['href'])
sleep(0.75)#damos tiempo para que no nos penalice un firewall
Cargaremos los archivos txt que creamos en el paso anterior y lo pasaremos a una estructura en un dataframe de Pandas para seguir usando en el próximo paso.
A partir del dataset que limpiamos, creamos y contamos las palabras: (el archivo spanish.txt lo incluye NLTK ó si no lo tienes, copia de mi Github en el mismo directorio en donde tienes el código)
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# We are going to create a document-term matrix using CountVectorizer, and exclude common Spanish stop words
Vemos en el listado que hay palabras muy usadas pero que realmente no tienen un significado útil para el análisis. Entonces haremos lo siguiente: uniremos las 12 listas de más palabras en un nuevo ranking y de esas, tomaremos las “más usadas” para ser agregar en nuestro listado de Stop Words.
Python
1
2
3
4
5
6
7
8
9
10
11
fromcollectionsimportCounter
# Let's first pull out the top 30 words for each anio
Ahora quitaremos las Stop words de nuestro dataset. Usaremos el listado de spanish.txt, el que generamos recién y uno adicional que hice yo a partir de los resultados obtenidos (ojo… esto les puede parecer arbitrario y en parte lo es!)
Haremos una primer aproximación a “qué tenía Hernan Casciari en su cabeza” entre 2004 y 2015 en sus cuentos usando un modo de visualización llamado WordCloud. Esto puede requerir que debas instalar la librería Wordcloud con Pip ó si tienes instalado Anaconda, desde la interface ó por terminal con conda install -c conda-forge wordcloud
#wordlists.fileids() # con esto listamos los archivos del directorio
cfd=nltk.ConditionalFreqDist(
(word,genre)
forgenre inanios
forwinwordlists.words(genre+'.txt')
forword in['casa','mundo','tiempo','vida']
ifw.lower().startswith(word))
cfd.plot()
5 – Análisis de Sentimiento
Ahora probaremos analizando los sentimientos en cuanto a “positivos y negativos” encontrados en el texto y sus cambios de polaridad. Para simplificar usaremos una librería llamada TextBlob que ya tiene esta funcionalidad hecha, aunque NO LO recomiendo para uso en producción. Por desgracia sólo funciona con textos en inglés, por lo que además nos obliga a traducir el texto con lo que eso conlleva… Pero para fines educativos -cómo los de este blog- es un buen ejemplo para ver el análisis de sentimiento.
Ahora intentaremos analizar el comportamiento del sentimiento a medida que el autor escribía cuentos a lo largo de los años. Para ello, tomaremos de a 12 “trozos” de texto de cada año y los analizaremos. (NOTA: Esto no es preciso realmente, pues no coincide temporalmente con 12 meses, es para dar una idea al lector de las diversas técnicas que podemos aplicar).
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
importmath
defsplit_text(text,n=12):
'''Takes in a string of text and splits into n equal parts, with a default of 12 equal parts.'''
# Calculate length of text, the size of each chunk of text and the starting points of each chunk of text
length=len(text)
size=math.floor(length/n)
start=np.arange(0,length,size)
# Pull out equally sized pieces of text and put it into a list
Ahora intentaremos obtener “automáticamente” algunos de los temas sobre los que escribe el autor. A decir verdad para que funcione deberíamos aplicar Lemmatization y limpiar mejor nuestro dataset. Para poder mostrar esta técnica nos vale, aunque no obtendremos resultados realmente buenos.
podemos intuir (¿forzosamente?) que lo que detectó el algoritmo se refiere a estos 3 temas:
Jugar / Niñez
Fútbol
Futuro
Conclusiones finales
Repasemos lo que hicimos y que resultados sacamos:
Extracción de 386 textos -> conseguimos los cuentos de 2004 al 2015
Limpiamos los textos, quitamos caracteres que no utilizamos y creamos un listado de stop_words (palabras para omitir)
Exploración de datos:
Realizamos estadísticas básicas, como el vocabulario usado, cantidad de palabras por año y promedio por posts.
Creamos Nubes de Palabras por año ya que es una manera de visualizar textos
Análisis de Sentimiento: visualizamos las variaciones en los textos a lo largo del tiempo y vimos leves sobresaltos, pero por lo general, una tendencia neutral.
Modelado de temáticas: en este punto no creo que hayamos conseguido unas categorías muy definidas. Parte del problema es que no pudimos hacer Lemmatization pues no conseguí herramientas Python en Español. Otra opción es que no hay temáticas claras.
ATENCIóN: este artículo es algo “estándar”, como para comenzar a entender el NLP aplicado y cómo -con diversas técnicas- comprender el lenguaje humano. Realmente hay muchas más aplicaciones y tareas que se pueden hacer. Debo decir que casi todo “en el mercado” está hecho para analizar textos en inglés y parte de la dificultad para desarrollar el ejercicio consistió en llevarlo al castellano. Si conoces otras buenas herramientas en español, escríbeme!
ATENCIóN (2): Podrás encontrar diferencias entre las visualizaciones en este artículo y el último Jupyter Notebook colgado en Github, esto se debe a que hubo actualizaciones en el código que no están reflejados en el artículo.
Espero en el futuro poder mostrar más utilidades del NLP y también llegar a usar NLP con algoritmos de Deep Learning (por ejemplo con redes neuronales convolucionales).
Suscripción al Blog
Recibe los nuevos artículos sobre Machine Learning, redes neuronales, NLP y código Python cada 3 semanas aprox.
sos un genio!! grandisimo aporte… Tengo un caso concreto que estoy interesado en desarrollar con mi equipo. Esta relacionado con interpretacion de emails para poder automatizar las respuestas ( utilizando tecnologia de automatizacion de procesos (RPA) ) si te interesa colaborar, serias mas que bienvenido
Hola! El formato dle blog de Casciari cambio un poco
Deberias cambiar la funcion url_get_text() a:
def url_get_text(url):
”’Obtener los textos de los cuentos de Hernan Casciari.”’
print(‘URL’,url)
text=””
try:
page = requests.get(url).text
soup = BeautifulSoup(page, “lxml”)
text = [p.text for p in soup.find(class_=”section-single-content”).find_all(‘p’)]
except Exception:
print(‘ERROR, puede que un firewall nos bloquea.’)
return ”
sleep(0.75) #damos tiempo para que no nos penalice un firewall
return text
Muchas gracias por esta información, estoy iniciando en el uso de Python y en el tema de la NLP. Te comento que he seguido paso a paso el ejercicio; sin embargo, se me presentó un problema cuando quiero acceder al sitio web y es que no puedo establecer una conexión con la dirección URL que se indica en el código.
Podrías indicarme en donde puede estar mi error.
El mensaje que me muestra es el siguiente:
ConnectionError: HTTPSConnectionPool(host=’editorialorsai.com’, port=443): Max retries exceeded with url: /category/epocas/2004/ (Caused by NewConnectionError(‘: Failed to establish a new connection: [WinError 10061] No se puede establecer una conexión ya que el equipo de destino denegó expresamente dicha conexión’))
Hola Maria, gracias por comentar. El error informa que la web desde donde intentas extraer la información (de los cuentos de Casciari) ha denegado tu request. Esto puede ser porque hayas hecho demasiadas peticiones en poco tiempo y te hayan confundido con un ataque DDos ú otro agente malicioso.
Deberás esperar y tratar bien al servidor
saludos,
Hola! Yo tmb econtré ese problema, pero ya pude hallar el blog del escritor. Sin embargo, ahora el sitio base es https://hernancasciari.com/blog/‘, pero el código lanza un error FileNotFoundError: [Errno 2] No such file or directory: ‘blog/2003.txt’ creo que esto es por que el sitio web ya no tiene las mismas URL´s, pero no he sabido como adaptarlo. Agradecería mucho un update de esto! muchas gracias es un ejercicio muy bueno
Hola, como podría trabajar Lemmatization para textos en castellano?
Muchas gracias por compartir tan magnífico trabajo.
Hola Emilio, gracias por escribir! Saludos
sos un genio!! grandisimo aporte… Tengo un caso concreto que estoy interesado en desarrollar con mi equipo. Esta relacionado con interpretacion de emails para poder automatizar las respuestas ( utilizando tecnologia de automatizacion de procesos (RPA) ) si te interesa colaborar, serias mas que bienvenido
Hola! El formato dle blog de Casciari cambio un poco
Deberias cambiar la funcion url_get_text() a:
def url_get_text(url):
”’Obtener los textos de los cuentos de Hernan Casciari.”’
print(‘URL’,url)
text=””
try:
page = requests.get(url).text
soup = BeautifulSoup(page, “lxml”)
text = [p.text for p in soup.find(class_=”section-single-content”).find_all(‘p’)]
except Exception:
print(‘ERROR, puede que un firewall nos bloquea.’)
return ”
sleep(0.75) #damos tiempo para que no nos penalice un firewall
return text
Gracias hombre, muy buen dato!
Excelente ejemplo, muchas gracias!!
Muchas gracias por esta información, estoy iniciando en el uso de Python y en el tema de la NLP. Te comento que he seguido paso a paso el ejercicio; sin embargo, se me presentó un problema cuando quiero acceder al sitio web y es que no puedo establecer una conexión con la dirección URL que se indica en el código.
Podrías indicarme en donde puede estar mi error.
El mensaje que me muestra es el siguiente:
ConnectionError: HTTPSConnectionPool(host=’editorialorsai.com’, port=443): Max retries exceeded with url: /category/epocas/2004/ (Caused by NewConnectionError(‘: Failed to establish a new connection: [WinError 10061] No se puede establecer una conexión ya que el equipo de destino denegó expresamente dicha conexión’))
Hola Maria, gracias por comentar. El error informa que la web desde donde intentas extraer la información (de los cuentos de Casciari) ha denegado tu request. Esto puede ser porque hayas hecho demasiadas peticiones en poco tiempo y te hayan confundido con un ataque DDos ú otro agente malicioso.
Deberás esperar y tratar bien al servidor
saludos,
El sitio parece haber sido sustituido por otro que no contiene cuentos. Es una empresa llamada «Orsai Audiovisuales».
Hola! Yo tmb econtré ese problema, pero ya pude hallar el blog del escritor. Sin embargo, ahora el sitio base es https://hernancasciari.com/blog/‘, pero el código lanza un error FileNotFoundError: [Errno 2] No such file or directory: ‘blog/2003.txt’ creo que esto es por que el sitio web ya no tiene las mismas URL´s, pero no he sabido como adaptarlo. Agradecería mucho un update de esto! muchas gracias es un ejercicio muy bueno