En este post de programación en C te enseñaré cómo obtener el texto (ignorando etiquetas) que se encuentra dentro de las etiquetas <body>
de un archivo HTML.
Vamos a extraer lo que hay en el cuerpo de la página, pero además vamos a obtener solo el texto, ignorando todas las etiquetas que existan. Por poner un ejemplo, si la página es:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mi página web - By Parzibyte</title>
</head>
<body>
Aquí va el contenido
<p> Soy un párrafo</p>
Yo no estoy dentro de una etiqueta <h1> Yo soy un encabezado</h1>
<strong>Ejemplo de otra etiqueta</strong>
</body>
</html>
El programa en ANSI C debe extraer el contenido y además ignorar las etiquetas, produciendo la siguiente salida:
Aquí va el contenido
Soy un párrafo
Yo no estoy dentro de una etiqueta Yo soy un encabezado
Ejemplo de otra etiqueta
Veamos cómo resolver este ejercicio propuesto en C, ya que en otro lenguaje con soporte nativo de expresiones regulares sería fácil, pero aquí no lo es tanto.
Vamos a dividir el problema en dos partes. Primero debemos extraer lo que hay en el cuerpo, entre las etiquetas <body>
y </body>
.
Para ello usamos strstr, buscamos el inicio y fin y después extraemos lo que haya entre estas etiquetas, copiándolo a una nueva cadena.
Después de eso recorremos la cadena (que será el cuerpo del documento) y si encontramos el inicio de una etiqueta (<
) ignoramos los siguientes caracteres hasta que encontremos la etiqueta de cierre (>
).
De este modo al final tendremos la cadena limpia en donde estará todo el HTML ya parseado. Obviamente esto ni siquiera se acerca a un parseador o intérprete, solo es un ejercicio que me pareció interesante de publicar.
Aunque esto no es lo más óptimo, es lo más fácil. Necesitamos almacenar todo el contenido del archivo en una cadena, así que recorremos el archivo y vamos concatenando a la cadena.
FILE *archivo;
unsigned char bufer[TAMANIO_BUFER];
archivo = fopen(nombreArchivo, "r");
if (archivo == NULL)
{
printf("Error abriendo el archivo");
return;
}
// Leer archivo
char contenido[TAMANIO_MAXIMO_CONTENIDO] = "";
while (!feof(archivo))
{
fread(bufer, sizeof(char), sizeof(bufer), archivo);
// Almacenar contenido en el búfer
strcat(contenido, bufer);
}
Cuando el código se haya ejecutado tendremos el contenido en una cadena de C.
Siguiendo con este ejercicio de C necesitamos extraer lo que se encuentra entre las etiquetas del cuerpo. Para ello simplemente usamos strstr y luego extraemos la cadena.
// Extraer lo que hay entre <body> y </body>
const char inicioEtiqueta[] = "<body>";
const char finEtiqueta[] = "</body>";
size_t longitudInicioEtiqueta = strlen(inicioEtiqueta);
char *punteroInicio = strstr(contenido, inicioEtiqueta) + longitudInicioEtiqueta;
char *punteroFin = strstr(contenido, finEtiqueta);
if (punteroInicio == NULL || punteroFin == NULL)
{
printf("No se encontraron las etiquetas");
return;
}
char cuerpoHtml[TAMANIO_MAXIMO_CONTENIDO] = "";
char contenidoLimpio[TAMANIO_MAXIMO_CONTENIDO] = "";
size_t longitud = punteroFin - punteroInicio;
// Copiar el contenido del cuerpo a cuerpoHtml
strncpy(cuerpoHtml, punteroInicio, longitud);
Hasta este momento ya tenemos el cuerpo, pero nos falta ignorar las etiquetas.
Necesitamos declarar otra cadena, que será en donde estará el texto ya limpio sin etiquetas HTML. Más tarde podemos imprimir o guardar la cadena en un archivo de texto, mientras tanto el código que ignora las etiquetas es:
int i = 0;
const char simboloInicio = '<';
const char simboloFin = '>';
int ignorando = 0;
// Recorrer el cuerpo e ignorar etiquetas
for (i = 0; i < strlen(cuerpoHtml); i++)
{
char caracterActual = cuerpoHtml[i];
// Si encontramos una etiqueta abriendo, establecemos la bandera en true
if (caracterActual == simboloInicio)
{
ignorando = 1;
}
// Si encontramos el del final, establecemos la bandera en false
if (caracterActual == simboloFin)
{
ignorando = 0;
}
// Si no debo ignorar este carácter, y éste carácter no es >, entonces es un carácter válido
if (!ignorando && caracterActual != simboloFin)
{
// Aquí se puede hacer cualquier cosa con el char. Yo solo lo estoy concatenando
concatenarCharACadena(caracterActual, contenidoLimpio);
}
}
Nos servimos de una bandera que nos dice si debemos tomar en cuenta el carácter actual o descartarlo. En caso de que el no debamos ignorar, concatenamos ese char a la cadena del resultado.
Al terminar, vamos a tener lo que necesitamos dentro de contenidoLimpio
.
El código completo queda encerrado en una función que recibe el nombre del archivo que hay que parsear:
void extraerTexto(char *nombreArchivo)
{
FILE *archivo;
unsigned char bufer[TAMANIO_BUFER];
archivo = fopen(nombreArchivo, "r");
if (archivo == NULL)
{
printf("Error abriendo el archivo");
return;
}
// Leer archivo
char contenido[TAMANIO_MAXIMO_CONTENIDO] = "";
while (!feof(archivo))
{
fread(bufer, sizeof(char), sizeof(bufer), archivo);
// Almacenar contenido en el búfer
strcat(contenido, bufer);
}
// Extraer lo que hay entre <body> y </body>
const char inicioEtiqueta[] = "<body>";
const char finEtiqueta[] = "</body>";
size_t longitudInicioEtiqueta = strlen(inicioEtiqueta);
char *punteroInicio = strstr(contenido, inicioEtiqueta) + longitudInicioEtiqueta;
char *punteroFin = strstr(contenido, finEtiqueta);
if (punteroInicio == NULL || punteroFin == NULL)
{
printf("No se encontraron las etiquetas");
return;
}
char cuerpoHtml[TAMANIO_MAXIMO_CONTENIDO] = "";
char contenidoLimpio[TAMANIO_MAXIMO_CONTENIDO] = "";
size_t longitud = punteroFin - punteroInicio;
// Copiar el contenido del cuerpo a cuerpoHtml
strncpy(cuerpoHtml, punteroInicio, longitud);
int i = 0;
const char simboloInicio = '<';
const char simboloFin = '>';
int ignorando = 0;
// Recorrer el cuerpo e ignorar etiquetas
for (i = 0; i < strlen(cuerpoHtml); i++)
{
char caracterActual = cuerpoHtml[i];
// Si encontramos una etiqueta abriendo, establecemos la bandera en true
if (caracterActual == simboloInicio)
{
ignorando = 1;
}
// Si encontramos el del final, establecemos la bandera en false
if (caracterActual == simboloFin)
{
ignorando = 0;
}
// Si no debo ignorar este carácter, y éste carácter no es >, entonces es un carácter válido
if (!ignorando && caracterActual != simboloFin)
{
// Aquí se puede hacer cualquier cosa con el char. Yo solo lo estoy concatenando
concatenarCharACadena(caracterActual, contenidoLimpio);
}
}
// Aquí se puede hacer cualquier cosa con la cadena
printf("%s", contenidoLimpio);
}
Para usarla podemos usar el siguiente código que ya lleva todo lo necesario:
/*
https://parzibyte.me/blog
*/#include <stdio.h>
#include <string.h>
#define TAMANIO_BUFER 1000
#define TAMANIO_MAXIMO_CONTENIDO 10000
void concatenarCharACadena(char c, char *cadena)
{
char cadenaTemporal[2];
cadenaTemporal[0] = c;
cadenaTemporal[1] = '\0';
strcat(cadena, cadenaTemporal);
}
void extraerTexto(char *nombreArchivo)
{
FILE *archivo;
unsigned char bufer[TAMANIO_BUFER];
archivo = fopen(nombreArchivo, "r");
if (archivo == NULL)
{
printf("Error abriendo el archivo");
return;
}
// Leer archivo
char contenido[TAMANIO_MAXIMO_CONTENIDO] = "";
while (!feof(archivo))
{
fread(bufer, sizeof(char), sizeof(bufer), archivo);
// Almacenar contenido en el búfer
strcat(contenido, (const char *)bufer);
}
// Extraer lo que hay entre <body> y </body>
const char inicioEtiqueta[] = "<body>";
const char finEtiqueta[] = "</body>";
size_t longitudInicioEtiqueta = strlen(inicioEtiqueta);
char *punteroInicio = strstr(contenido, inicioEtiqueta) + longitudInicioEtiqueta;
char *punteroFin = strstr(contenido, finEtiqueta);
if (punteroInicio == NULL || punteroFin == NULL)
{
printf("No se encontraron las etiquetas");
return;
}
char cuerpoHtml[TAMANIO_MAXIMO_CONTENIDO] = "";
char contenidoLimpio[TAMANIO_MAXIMO_CONTENIDO] = "";
size_t longitud = punteroFin - punteroInicio;
// Copiar el contenido del cuerpo a cuerpoHtml
strncpy(cuerpoHtml, punteroInicio, longitud);
int i = 0;
const char simboloInicio = '<';
const char simboloFin = '>';
int ignorando = 0;
// Recorrer el cuerpo e ignorar etiquetas
for (i = 0; i < strlen(cuerpoHtml); i++)
{
char caracterActual = cuerpoHtml[i];
// Si encontramos una etiqueta abriendo, establecemos la bandera en true
if (caracterActual == simboloInicio)
{
ignorando = 1;
}
// Si encontramos el del final, establecemos la bandera en false
if (caracterActual == simboloFin)
{
ignorando = 0;
}
// Si no debo ignorar este carácter, y éste carácter no es >, entonces es un carácter válido
if (!ignorando && caracterActual != simboloFin)
{
// Aquí se puede hacer cualquier cosa con el char. Yo solo lo estoy concatenando
concatenarCharACadena(caracterActual, contenidoLimpio);
}
}
// Aquí se puede hacer cualquier cosa con la cadena
printf("%s", contenidoLimpio);
}
int main(int argc, char const *argv[])
{
extraerTexto("index.html");
}
Yo lo he ejecutado y funciona de maravilla:
Si tú quieres puedes descargar el código y probarlo por ti mismo.
Para terminar te invito a leer más sobre programación en C en mi blog.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.