Cómo extraer información de un tag HTML con Regex? | C#

Intermedio

El contenido expuesto en Html es una increíble fuente de información para las aplicaciones modernas, en muchas ocasiones necesitamos extraer información de un tag en particular, en este artículo aprenderemos a hacerlo particularmente centrándonos en el tag <img>.

Las expresiones regulares son una herramienta muy poderosa, aunque críptica, que podemos utilizar para simplificar esta titánica tarea.

He implementado este método el cual extrae la lista de urls de las imágenes contenidas en los tag <img> de una cadena con contenido HTML.

Pensemos en la expresión regular.

Necesitamos hallar una cadena de texto que inicie con "<img":

  • después pueden haber uno o más espacios por lo que en la expresión regular agregamos \s que indica cualquier carácter que represente espacios y el signo + que indica que debe haber como mínimo un carácter pero pueden ser n, quedando así "<img\s+"
  • luego de eso pueden haber varios atributos dentro del tag.

Uno de esos atributos es el atributo src que es el que nos interesa, pero realmente no sabemos en que posición puede aparecer el atributo src:

  • Ninguno de los caracteres siguientes debe ser el carácter > , es decir el tag no se puede cerrar antes de src : "<img\s+[^>]*"
  • luego buscamos un final de palabra justo después del cual debe aparecer la palabra src: "<img\s+[^>]*\bsrc"
  • seguidamente esperamos encontrar de 0 a n caracteres de espacio, un signo = y de nuevo de 0 a n caracteres de espacio: "<img\s+[^>]*\bsrc\s*\=\s*"
  • Ya estamos cerca, ahora esperamos encontrar unas comillas, pero las comillas pueden ser carácter " o carácter ', para evitar líos de escape characters y demás utilizaré hexadecimal para estos dos caracteres: "<img\s+[^>]*\bsrc\s*\=\s*[\x27\x22]"

De aquí en adelante se encuentra la url que buscamos, esta acaba justo al encontrar de nuevo uno de los caracteres [\x27\x22] . "<img\s+[^>]*\bsrc\s*\=\s*[\x27\x22] [\x27\x22]"

Esta es la información que realmente nos interesa, así que dentro de la expresión regular creamos un grupo con el nombre Url lo cual nos permitirá extraer la url fácilmente.

 "<img\s+[^>]*\bsrc\s*\=\s*[\x27\x22](?<Url>)[\x27\x22]"

Ahora que tenemos el grupo debemos adicionar que este esta compuesto de 0 a n caracteres diferente de las comillas porque estas cerrarían el valor del atributo, quedando así:

 "<img\s+[^>]*\bsrc\s*\=\s*[\x27\x22](?<Url>[^\x27\x22]*)[\x27\x22]"

Eso es todo, ya tenemos nuestra expresión regular, espero no haya sido muy confuso, pero tengan en cuenta que este post no es acerca de aprender expresiones regulares sino de usarlas para un propósito específico.

Ahora utilizaremos esa expresión para extraer la lista de imágenes.

Creamos un nuevo objeto de tipo Regex para procesar la expresión regular creada y extraer las coincidencias (Matches) encontradas, a cada Match le extraemos el valor hallado y si este es una cadena diferente de vacío o espacios en blanco, la agregamos a la colección de Url resultante.

 /// <summary>Regular expression to get image urls from a HTML string</summary>
private const string STR_IMGTAG_SRC_EXP = @"<img\s+[^>]*\bsrc\s*\=\s*[\x27\x22](?<Url>[^\x27\x22]*)[\x27\x22]";

/// <summary>
/// Extracts the first image Url from a html string
/// </summary>
/// <param name="htmlString">A string containing html code</param>
/// <returns>a collection with the image Urls contained in htmlString parameter</returns>
/// <remarks>This method uses regular expressions,so using System.Text.RegularExpressions; 
/// must be addeed</remarks>
public static List<string> ExtractImageUrisFromHtml(string htmlString)  
{

    var rgx = new Regex(STR_IMGTAG_SRC_EXP,
                        RegexOptions.IgnoreCase | RegexOptions.Multiline);

    var lista = new List<string>();
    var matches = rgx.Matches(htmlString);

    foreach (Match match in matches)
    {
        var url = match.Groups["Url"].Value;
        if (!string.IsNullOrWhiteSpace(url))
        {
            lista.Add(
            match.Groups["Url"].Value);
        }
    }
    return lista;
}

He creado una versión un poco más especializada para extraer únicamente la url de la primera url de imagen encontrada, y de paso asegurarse de devolver siempre un valor de Url válido, una cadena vacía no es un valor de Url válido por lo que hago uso de tempuri en este caso:

 /// <summary>Default Uri</summary>
private const string TEMPURI = "https://tempuri.org";

/// <summary>
/// Extracts the first image Url from a html string
/// </summary>
/// <param name="htmlString">A string containing html code</param>
/// <returns>a string with the Url or first image in the htmlString parameter</returns>
/// <remarks>This method uses regular expressions,so using System.Text.RegularExpressions; must be addeed</remarks>
public static string ExtractFirstHtmlImage(string htmlString)  
{
    string respuesta = TEMPURI;
    try
    {
        var rgx = new Regex(
            STR_IMGTAG_SRC_EXP,
                        RegexOptions.IgnoreCase | RegexOptions.Multiline);

        var match = rgx.Match(htmlString);

        respuesta = match.Groups["Url"].Value;

        if (respuesta == "")
            respuesta = TEMPURI;
    }
    catch { respuesta = TEMPURI; }

    return respuesta;
}