Publi

Formas de eliminar etiquetas XML/HTML desde MySQL o MariaDb. strip_tags en MySQL

popsometags
Estamos haciendo una consulta a una tabla, y vemos que la tabla tiene un texto. Pero dicho texto contiene etiquetas HTML que nos molestan o no deberían estar ahí (puede que el valor lo hayamos extraído de un campo WYSIWYG) y cuando vamos a extraer la información nos molesta.

Pero tenemos prisa y tendríamos que modificar o crear un programa y no tenemos tiempo. ¿Qué hacemos? Pues nada, implementar strip_tags() dentro de MySQL y luego pegarnos un baile.

Preparamos nuestra prueba

Primero vamos a preparar un ejemplo. No os puedo mostrar mi caso real porque contiene información confidencial (qué importante suena). Además, contiene muchos campos y nos sobran la mitad de los datos para el ejemplo; por tanto, vamos a crear una tabla y a meter algunos datos:

1
2
3
4
5
6
7
8
9
10
11
12
CREATE SCHEMA POESIA;
CREATE TABLE IF NOT EXISTS `POESIA`.`PRUEBAS` (
   `id` BIGINT NOT NULL AUTO_INCREMENT,
   `Key` VARCHAR(150) NOT NULL,
   `Value` TEXT NULL,
    PRIMARY KEY(Id)
) ENGINE = InnoDB;

INSERT INTO POESIA.PRUEBAS VALUES(NULL, 'Enlace', '<a href="https://poesiabinaria.net/">Poesía Binaria</a>');
INSERT INTO POESIA.PRUEBAS VALUES(NULL, 'LoremIpsum', '<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisl mi, volutpat nec ipsum vitae, vehicula viverra metus.</p>');
INSERT INTO POESIA.PRUEBAS VALUES(NULL, 'Test', '<p>This is a <span>test</span>.</p>');
INSERT INTO POESIA.PRUEBAS VALUES(NULL, 'Header', '<h1 class="cabecera">This is a header</h1>');

Veamos el problema

El caso es que cuando hacemos un SELECT a la tabla anterior, nos devolvería esto:

SELECT * FROM POESIA.PRUEBAS;
| id | Key        | Value
|  1 | Enlace     | <a href=”https://poesiabinaria.net/”>Poesía Binaria</a>
|  2 | LoremIpsum | <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisl mi, volutpat nec ipsum vitae, vehicula viverra metus.</p> |
|  3 | Test       | <p>This is a <span>test</span>.</p>
|  4 | Header     | <h1 class=”cabecera”>This is a header</h1>
4 rows in set (0.00 sec)

Que no sería gran problema si son sólo estos cuatro campos, pero se vuelve un verdadero marrón cuando son varios miles de campos. Tanto si los tiene que procesar una aplicación o vamos a exportarlos como CSV

Primero, pensemos

Es lo primero que hay que hacer siempre. Y es que si las etiquetas son siempre las mismas, o en definitiva son un número acotable a primera vista, es decir, sólo vemos <p> y <span> podemos utilizar un simple REPLACE y nos quitamos de líos:

SELECT id, `Key`, REPLACE(REPLACE(REPLACE(REPLACE(Value, ‘<p>’, ”), ‘</p>’, ”), ‘<span>’, ”), ‘</span>’, ”) AS Value FROM POESIA.PRUEBAS;
| id | Key        | Value
|  1 | Enlace     | <a href=”https://poesiabinaria.net/”>Poesía Binaria</a>
|  2 | LoremIpsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisl mi, volutpat nec ipsum vitae, vehicula viverra metus. |
|  3 | Test       | This is a test.
|  4 | Header     | <h1 class=”cabecera”>This is a header</h1>
4 rows in set (0.00 sec)

Aunque vemos que no vale para todas las etiquetas, y por desgracia en MySQL no tenemos un método fácil para hacer muchos reemplazos juntos. Podríamos crear una tabla temporal y montar un procedimiento almacenado que guardara valores temporales de los replaces (uno por fila), pero sería muy pesado…

MariaDB y expresiones regulares

En MySQL tampoco hay una forma fácil de reemplazar con expresiones regulares. Y digo fácil, porque siempre es posible crear un módulo en C para MySQL (o bajártelo. Podemos utilizar este, aunque tiene algunos años), pero claro, en un servidor de producción no te vas a poner a compilar nada. Es más, si te pilla el sysadmin no sales vivo.

Pero si utilizas MariaDB, desde la versión 10.0.5 tenemos la función REGEXP_REPLACE() que nos ayudará con ésta y muchas otras cosas…

SELECT id, `Key`, REGEXP_REPLACE(Value, ‘<.+?>’, ”) AS Value FROM POESIA.PRUEBAS;
| id | Key        | Value
|  1 | Enlace     | Poesía Binaria
|  2 | LoremIpsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisl mi, volutpat nec ipsum vitae, vehicula viverra metus. |
|  3 | Test       | This is a test.
|  4 | Header     | This is a header
4 rows in set (0.00 sec)

MySQL strip_tags()

Pero claro, si no vamos a instalar software en el servidor de producción y estamos utilizando MySQL, no vamos a pasarlo todo a MariaDB. Así que al menos, vamos a crear un procedimiento almacenado que nos quite las etiquetas de un campo de la base de datos y lo podamos llamar para devolver todos los campos filtrados.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DROP FUNCTION IF EXISTS fnStripTags;
DELIMITER $$
CREATE FUNCTION fnStripTags( Dirty TEXT )
RETURNS TEXT
DETERMINISTIC
BEGIN
  DECLARE iStart, iEnd, iLength int;
    WHILE Locate( '<', Dirty ) > 0 And Locate( '>', Dirty, Locate( '<', Dirty )) > 0 DO
      BEGIN
        SET iStart = Locate( '<', Dirty ), iEnd = Locate( '>', Dirty, Locate('<', Dirty ));
        SET iLength = ( iEnd - iStart) + 1;
        IF iLength > 0 THEN
          BEGIN
            SET Dirty = Insert( Dirty, iStart, iLength, '');
          END;
        END IF;
      END;
    END WHILE;
    RETURN Dirty;
END;
$$
DELIMITER ;

La función es de Peter Brawley, que a su vez hizo un port a MySQL de una función de Robert Davis. Ahora cuando hacemos:

SELECT id, `Key`, fnStripTags(Value) AS Value FROM POESIA.PRUEBAS;
| id | Key        | Value
|  1 | Enlace     | Poesía Binaria
|  2 | LoremIpsum | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent nisl mi, volutpat nec ipsum vitae, vehicula viverra metus. |
|  3 | Test       | This is a test.
|  4 | Header     | This is a header
4 rows in set (0.00 sec)

Todo funciona bien.

O podríamos utilizar también esta función:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DROP FUNCTION IF EXISTS `strip_tags`;
DELIMITER $$
CREATE FUNCTION `strip_tags`($str text) RETURNS text
BEGIN
    DECLARE $start, $end INT DEFAULT 1;
    LOOP
        SET $start = LOCATE("<", $str, $start);
        IF (!$start) THEN RETURN $str; END IF;
        SET $end = LOCATE(">", $str, $start);
        IF (!$end) THEN SET $end = $start; END IF;
        SET $str = INSERT($str, $start, $end - $start + 1, "");
    END LOOP;
END;
$$
DELIMITER ;

Mucho más sencilla y también efectiva (Quién mira Stack Overflow?).

Actualización 16/12/2016: Corregidos algunos tags HTML que no se veían bien.

También podría interesarte....

There are 3 comments left Ir a comentario

  1. Pingback: Formas de eliminar etiquetas XML/HTML desde MySQL o MariaDb. strip_tags en MySQL | PlanetaLibre /

  2. Rafael /
    Usando Google Chrome Google Chrome 61.0.3163.100 en Mac OS X Mac OS X 10.11.4

    Hola, hay alguna manera de meter esta función dentro de PHP?

    1. Gaspar Fernández / Post Author
      Usando Mozilla Firefox Mozilla Firefox 56.0 en Ubuntu Linux Ubuntu Linux

      La función puedes crearla en MySQL, desde el cliente de consola, de algún cliente de escritorio o un phpMyAdmin, y luego desde PHP cuando hagas la query puedes hacer la llamada sin problemas:
      SELECT id, `Key`, fnStripTags(Value) AS Value FROM POESIA.PRUEBAS;

      De todas formas, si estás usando PHP puedes hacer la petición normal, es decir: SELECT id, `Key`, `Value` FROM POESIA_PRUEBAS;

      Y cuando hagas el fetch_array utilizar la función strip_tags() de PHP.

Leave a Reply