Saltar al contenido

Silabación de palabras con python y como servicio web

La silabación, según el diccionario de la RAE, es:

la división en sílabas, tanto en la pronunciación como en la escritura

En español existen diptongos, triptongas, hiatos,… No siempre es sencillo saber cómo se dividen las palabras en sílabas.

Pues resulta que existen herramientas por ahí que permiten la silabación de forma automática con una calidad muy aceptable. Un ejemplo es Silabeador TIP. Si queréis ir a los detalles podéis ir y leer el artículo “Automatic syllabification for Spanish using lemmatization and derivation to solve the prefix’s prominence issue” publicado en Experts systems with applications.

pylabeador

Y, ¿sabías que hay gente muy inteligente y maja por ahí que, basándose en Silabeador TIP, han creado una opción pythónica para poder separar las palabras en sílabas?

Gracias a Jacobo de Vera tenemos una biblioteca en Python que nos permite hacer esto. La biblioteca en cuestión se llama pylabeador.

La biblioteca está escrita en Python puro y la puedes instalar usando pip:

python -m pip install pylabeador

La biblioteca expone dos funciones principales, syllabify y syllabify_with_details. Vamos a ver cómo funciona:

from pylabeador import syllabify, syllabify_with_details

result = syllabify("ciencia")
print(result)

Y el resultado de lo anterior debería mostrar ['cien', 'cia'].

Como puedes ver, usando syllabify te devuelve la palabra dividida en sílabas como una cadena de texto.

Si queremos más detalles podemos usar syllabify_with_details:

result = syllabify_with_details("ciencia")
print(result)

Lo anterior mostrará:

SyllabifiedWord(original='ciencia', syllables=[Syllable(onset='c', nucleus='ie', coda='n', accented=False, stressed=True), Syllable(onset='c', nucleus='ia', coda='', accented=False, stressed=False)], stressed=0, accented=None)

En este caso, el resultado es un objeto SyllabifiedWord que nos aporta más información.

Dentro del objeto tenemos la palabra original:

print(result.original)

Que nos dará ciencia.

Podemos obtener el mismo resultado que usando syllabify mediante el método hyphenate:

print(result.hyphenate())

Podemos obtener una lista con objetos Syllable que nos dan información de cada sílaba:

print(result.syllables)

Lo anterior nos dará:

[Syllable(onset='c', nucleus='ie', coda='n', accented=False, stressed=True), Syllable(onset='c', nucleus='ia', coda='', accented=False, stressed=False)]

En lo anterior podemos ver dos sílabas y cada objeto Syllable dispone del ataque (onset), el núcleo (nucleus) y la coda (coda).

Por otra parte, también podemos obtener información sobre si la sílaba lleva tilde (accented) y sobre si es la sílaba que va acentuada en la palabra (stressed).

Veamos una palabra con tilde:

print(syllabify_with_details("pitón").syllables)

Lo anterior nos dará:

[Syllable(onset='p', nucleus='i', coda='', accented=False, stressed=False), Syllable(onset='t', nucleus='ó', coda='n', accented=True, stressed=True)]

En el anterior ejemplo vemos que la segunda sílaba lleva tilde y, por tanto, es la acentuada.

Y hasta aquí la primera parte del artículo.

Convertir pylabeador en un servicio web que funciona en el cliente

Como he comentado anteriormente, pylabeador está escrito únicamente usando Python y no tiene dependencias importantes. Gracias a estas peculiaridades la podemos usar directamente en Brython.

Perfecto, pues vamos a convertirlo en un servicio web que se ejecute en el cliente (navegador) sin necesidad de tener un servidor que ejecute la biblioteca Python y para que así lo pueda usar todo el mundo.

Para conseguirlo vamos a seguir los siguientes pasos:

  1. Primero creamos una nueva carpeta. Por ejemplo, una que se llame pylabeador_web. Y nos movemos a la misma:
mkdir pylabeador_web
cd pylabeador_web
  1. Creamos un entorno virtual:
python -m venv env
  1. Activamos el entorno virtual:
. env/bin/activate #(en linux)
env\\Scripts\\activate #(en Windows)
  1. Instalamos brython:
python -m pip install brython
  1. Instalamos también pylabeador:
python -m pip install pylabeador
  1. Creamos una nueva carpeta que se llame, por ejemplo, web y nos movemos a la misma:
mkdir web
cd web

Deberíamos tener una estructura como la siguiente:

pylabeador-web/
├── env
└── web
  1. Después de instalar brython tenemos disponible en la línea de comandos brython-cli que nos permite hacer una serie de cosas. La primera que vamos a hacer es crear un proyecto:
brython-cli --install

Lo anterior nos ha creado una serie de cosas en la carpeta en la que nos encontrábamos, web. Debería quedar algo así:

.
├── brython.js
├── brython_stdlib.js
├── demo.html
├── index.html
├── README.txt
└── unicode.txt
  1. En lo anterior podemos eliminar demo.html, README.txt y unicode.txt y nos quedamos solo con las bibliotecas javascript y con index.html. Nos debe quedar algo así:
.
├── brython.js
├── brython_stdlib.js
└── index.html
  1. Para hacer que pylabeador esté disponible para brython hemos de hacer lo siguiente (antes lo tenemos que tener instalado con pip como hemos hecho anteriormente):
brython-cli --add_package pylabeador

Ahora tendremos la siguiente estructura:

.
├── brython.js
├── brython_stdlib.js
├── index.html
└── Lib
    └── site-packages
        └── pylabeador
            ├── errors.py
            ├── __init__.py
            ├── __main__.py
            ├── models.py
            ├── __pycache__
            │   ├── errors.cpython-38.pyc
            │   ├── __init__.cpython-38.pyc
            │   ├── __main__.cpython-38.pyc
            │   ├── models.cpython-38.pyc
            │   ├── pylabeador.cpython-38.pyc
            │   ├── syllabify.cpython-38.pyc
            │   ├── util.cpython-38.pyc
            │   └── __version__.cpython-38.pyc
            ├── pylabeador.py
            ├── syllabify.py
            ├── util.py
            └── __version__.py
  1. Ahora vamos a meter la dependencia de pylabeador en la web que estamos haciendo. Edito el fichero index.html. El original es:
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<script type="text/javascript" src="brython.js"></script>
<script type="text/javascript" src="brython_stdlib.js"></script>
</head>

<body onload="brython(1)">
<script type="text/python">
from browser import document

document <= "Hello"
</script>
</body>

</html>

y el modificado queda (solo añado la línea donde importo pylabeador)

<!doctype html>
<html>

<head>
<meta charset="utf-8">
<script type="text/javascript" src="brython.js"></script>
<script type="text/javascript" src="brython_stdlib.js"></script>
</head>

<body onload="brython(1)">
<script type="text/python">
from browser import document
import pylabeador

document <= "Hello"
</script>
</body>

</html>
  1. El fichero brython_stdlib.js, que contiene parte de la biblioteca estándar de Python, dispone de muchas cosas que igual no necesitamos para nuestra aplicación. Es interesante reducir el tamaño de este fichero para que la página cargue más rápidamente, para ahorrar recursos, contaminar menos, etc. Podemos reducir el tamaño de este fichero e incluir pylabeador (y otras dependencias si las hubiera) dentro un nuevo fichero haciendo lo siguiente:
brython-cli --modules

Lo anterior busca en los distintos ficheros buscando las dependencias que usamos y crea una nueva distribución. Se habrá creado un nuevo fichero brython_modules.js que incluirá las partes de la biblioteca estándar que necesitamos además de las dependencias de terceros que estemos usando, en este caso solo pylabeador. El tamaño ha pasado de 3.9M a 2.4M. No es maravilloso pero es una mejora. Además de tener un tamaño menor nos permite no tener que usar un servidor para servir los imports de bibliotecas Python que Brython realiza mediante peticiones AJAX.

  1. Ahora hemos de sustituir los script que usamos dentro del index.html. Cambiamos brython_stdlib.js por brython_modules.js. Quedará así:
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<script type="text/javascript" src="brython.js"></script>
<script type="text/javascript" src="brython_modules.js"></script>
</head>

<body onload="brython(1)">
<script type="text/python">
from browser import document
import pylabeador

document <= "Hello"
</script>
</body>

</html>

Hasta ahora hemos preparado la infraestructura que necesitamos. Ahora necesitamos meter un poco de lógica, un poco de formato y de estilo al tema. Algunos temas, como el estilo, los voy a obviar ya que no es el objeto del artículo.

Completamos index.html con el código que necesitamos para hacer nuestra aplicación

El TL;DR es el siguiente, el fichero index.html quedará así:

<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>Pybonacci | PyLabeador online</title>
  <script type="text/javascript" src="brython.js"></script>
  <script type="text/javascript" src="brython_modules.js"></script>
  <style>
.center {
  margin: auto;
  width: 80%;
  padding: 10px;
  text-align: center;
}
.consolas {
  font-family: Consolas, monaco, monospace;
}
.red {
  color: white;
  background-color: red;
}
.footer {
  position: fixed;
  left: 0;
  bottom: 0;
  width: 100%;
  background-color: #aaa;
  color: black;
  text-align: center;
}
.stressed {
  color: blue;
}
.accented {
  color: green;
}
  </style>
</head>

<body onload="brython(1)">
  <script type="text/python">
from browser import document, html
from pylabeador import syllabify_with_details

def get_validate_word(ev):
    palabra = document["text"].value
    if palabra and palabra.isalpha():
        write_result(palabra)
    else:
        write_error()

def write_result(palabra):
    document["text"].value = ""
    document["result"].html = ""
    result = syllabify_with_details(palabra)
    document["result"] <= html.P(result.hyphenate())
    document["result"] <= html.P(
        html.B(
            "Sílaba |  Ataque | Núcleo | Coda | Lleva tilde | Lleva acento"
        ),
        Class="consolas"
    )
    for s in result.syllables:
        tildada = "No"
        acentuada = "No"
        _class = None
        if s.accented:
            tildada = "Sí"
            _class = "accented"
        if s.stressed:
            acentuada = "Sí"
            if _class is None:
                _class = "stressed"
        text = (
            f"{s.onset+s.nucleus+s.coda:^6} | "
            f"{s.onset:^6} | "
            f"{s.nucleus:^6} | "
            f"{s.coda:^4} | "
            f"{tildada:^11} | "
            f"{acentuada:^12}"
        )
        text = text.replace(" ", "&nbsp;")
        document["result"] <= html.P(text, Class=f"{_class} consolas")


def write_error():
    document["text"].value = ""
    document["result"].html = ""
    document["result"] <= html.DIV(
        "La palabra no es válida. Escribe una palabra válida",
        Class="center red"
    )

document["btn"].bind("click", get_validate_word)
  </script>

  <H1 class="center">pylabeador online</H1>
  <div class="center">
    <label for="text">Escribe la palabra a analizar</label>
    <br>
    <input type="text" name="text" id="text" maxlength="30">
    <br>
    <input id="btn" type="submit" value="Silabea">
  </div>
  <div id="result" class="center"></div>
  <div class="footer">
    <p>
      <a href="https://github.com/jdevera/pylabeador" target="_blank">pylabeador</a> (por <a href="https://twitter.com/jovianjake" target="_blank">Jacobo de Vera</a>) | 
      PyLabeador-Web (por <a href="https://pybonacci.org">Kiko Correoso</a>) | 
      Hecho con &#9829; usando <a href="https://brython.info" target="_blank">Brython</a> | 
      <a href="">Más información</a>.
    </p>
  </div>

</body>

</html>

Si el anterior fichero, junto con brython.js y brython_modules.js, los metéis en una carpeta en un servidor ya podéis servir la aplicación.

Se vería así:

Ahora la versión no tan larga, explicando un poco lo que hemos metido en el fichero index.html.

En la sección <head> del fichero HTML hemos puesto cosas típicas de una página web y no voy a entrar mucho en detalle. Ahí metemos los scripts javascript que necesitamos, un poco de estilo CSS para hacer la página más pintona y poco más.

En la sección del <body> hay dos partes principales, el script de Python/Brython y las partes de marcado HTML para tener la estructura del documento.

La segunda parte es esta:

  <H1 class="center">pylabeador online</H1>
  <div class="center">
    <label for="text">Escribe la palabra a analizar</label>
    <br>
    <input type="text" name="text" id="text" maxlength="30">
    <br>
    <input id="btn" type="submit" value="Silabea">
  </div>
  <div id="result" class="center"></div>
  <div class="footer">
    <p>
      <a href="https://github.com/jdevera/pylabeador" target="_blank">pylabeador</a> (por <a href="https://twitter.com/jovianjake" target="_blank">Jacobo de Vera</a>) | 
      PyLabeador-Web (por <a href="https://pybonacci.org">Kiko Correoso</a>) | 
      Hecho con &#9829; usando <a href="https://brython.info" target="_blank">Brython</a> | 
      <a href="">Más información</a>.
    </p>
  </div>

En lo anterior tenemos:

  • un cabecero para darle un título al documento.
  • un mini formulario para poder introducir una palabra.
  • un <div> vacío, de inicio que es donde se mostrará posteriormente el resultado.
  • y un <footer> con algo de información y enlaces.

Nada muy complejo.

Vamos ahora a la parte de acción, el script Python/Brython:

from browser import document, html
from pylabeador import syllabify_with_details

def get_validate_word(ev):
    palabra = document["text"].value
    if palabra and palabra.isalpha():
        write_result(palabra)
    else:
        write_error()

def write_result(palabra):
    document["text"].value = ""
    document["result"].html = ""
    result = syllabify_with_details(palabra)
    document["result"] <= html.P(result.hyphenate())
    document["result"] <= html.P(
        html.B(
            "Sílaba |  Ataque | Núcleo | Coda | Lleva tilde | Lleva acento"
        ),
        Class="consolas"
    )
    for s in result.syllables:
        tildada = "No"
        acentuada = "No"
        _class = None
        if s.accented:
            tildada = "Sí"
            _class = "accented"
        if s.stressed:
            acentuada = "Sí"
            if _class is None:
                _class = "stressed"
        text = (
            f"{s.onset+s.nucleus+s.coda:^6} | "
            f"{s.onset:^6} | "
            f"{s.nucleus:^6} | "
            f"{s.coda:^4} | "
            f"{tildada:^11} | "
            f"{acentuada:^12}"
        )
        text = text.replace(" ", "&nbsp;")
        document["result"] <= html.P(text, Class=f"{_class} consolas")


def write_error():
    document["text"].value = ""
    document["result"].html = ""
    document["result"] <= html.DIV(
        "La palabra no es válida. Escribe una palabra válida",
        Class="center red"
    )

document["btn"].bind("click", get_validate_word)

Vamos a desgranar un poco lo anterior para entender lo que hace.

En la parte de los imports importamos pylabeador y el módulo browser que es específico de Brython y me permite interactuar con el DOM y crear etiquetas HTML, entre otras cosas.

from browser import document, html
from pylabeador import syllabify_with_details

La función get_validate_word es la que actuará cuando pulsemos el botón. Unimos el evento click del botón con esta función usando la última línea del script, document["btn"].bind("click", get_validate_word). El anterior código traducido a javascript sería algo así: document.getElementById("btn").addEventListener("click", get_validate_word);.

La función get_validate_word recibirá el evento click por eso recibe un parámetro que hemos llamado ev. Después del click se leerá lo que haya en el input de la página, si hay una palabra válida llamará a la función write_result y si no hay una palabra válida llamará a la función write_error.

La función write_error se encargará de mostrar un mensaje de error en pantalla si lo que hemos metido en el input no parece una palabra.

Si lo que metemos en el input parece una palabra se debería ejecutar write_result. Esta función será la encargada de usar pylabeador y mostrar el resultado en pantalla.

Y ahora dejo la aplicación metida en un <iframe> aquí debajo para que la puedas usar en vivo o en este enlace para verla a pantalla completa:

Espero que te resulte útil.

Saludos.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

+ seventy three = 81

Pybonacci