Tomando el atributo href de un elemento A

114

Intentando encontrar los enlaces en una página.

mi expresión regular es:

/<a\s[^>]*href=(\"\'??)([^\"\' >]*?)[^>]*>(.*)<\/a>/

pero parece fallar en

<a title="this" href="that">what?</a>

¿Cómo cambiaría mi expresión regular para tratar con href que no se coloca primero en la etiqueta a?

Bergin
fuente

Respuestas:

208

Las expresiones regulares confiables para HTML son difíciles . A continuación se explica cómo hacerlo con DOM :

$dom = new DOMDocument;
$dom->loadHTML($html);
foreach ($dom->getElementsByTagName('a') as $node) {
    echo $dom->saveHtml($node), PHP_EOL;
}

Lo anterior buscaría y generaría el "HTML externo" de todos los Aelementos de la $htmlcadena.

Para obtener todos los valores de texto del nodo, haz

echo $node->nodeValue; 

Para comprobar si el hrefatributo existe, puede hacer

echo $node->hasAttribute( 'href' );

Para obtener el hrefatributo que harías

echo $node->getAttribute( 'href' );

Para cambiar el hrefatributo que harías

$node->setAttribute('href', 'something else');

Para eliminar el hrefatributo que harías

$node->removeAttribute('href'); 

También puede consultar el hrefatributo directamente con XPath

$dom = new DOMDocument;
$dom->loadHTML($html);
$xpath = new DOMXPath($dom);
$nodes = $xpath->query('//a/@href');
foreach($nodes as $href) {
    echo $href->nodeValue;                       // echo current attribute value
    $href->nodeValue = 'new value';              // set new attribute value
    $href->parentNode->removeAttribute('href');  // remove attribute
}

Ver también:

En una nota al margen: estoy seguro de que esto es un duplicado y puede encontrar la respuesta en algún lugar aquí

Gordon
fuente
Las expresiones regulares confiables para analizar HTML son intrínsecamente imposibles, incluso porque HTML no es un lenguaje regular.
Asciiom
19

Estoy de acuerdo con Gordon, DEBES usar un analizador HTML para analizar HTML. Pero si realmente quieres una expresión regular, puedes probar esta:

/^<a.*?href=(["\'])(.*?)\1.*$/

Esto coincide <aal principio de la cadena, seguido de cualquier número de cualquier carácter (no codicioso) y .*?luego href=seguido del enlace rodeado por "o'

$str = '<a title="this" href="that">what?</a>';
preg_match('/^<a.*?href=(["\'])(.*?)\1.*$/', $str, $m);
var_dump($m);

Salida:

array(3) {
  [0]=>
  string(37) "<a title="this" href="that">what?</a>"
  [1]=>
  string(1) """
  [2]=>
  string(4) "that"
}
Toto
fuente
solo para información: si buscamos en un texto que contiene muchos elementos, la expresión (. *?) es incorrecta
Michal - wereda-net
5

El patrón que desea buscar sería el patrón de anclaje de enlace, como (algo):

$regex_pattern = "/<a href=\"(.*)\">(.*)<\/a>/";
Alex Pliutau
fuente
1
¿Qué pasa si el ancla tiene más atributos?
funerr
3

¿Por qué no simplemente emparejas?

"<a.*?href\s*=\s*['"](.*?)['"]"

<?php

$str = '<a title="this" href="that">what?</a>';

$res = array();

preg_match_all("/<a.*?href\s*=\s*['\"](.*?)['\"]/", $str, $res);

var_dump($res);

?>

luego

$ php test.php
array(2) {
  [0]=>
  array(1) {
    [0]=>
    string(27) "<a title="this" href="that""
  }
  [1]=>
  array(1) {
    [0]=>
    string(4) "that"
  }
}

que funciona. Acabo de quitar las primeras abrazaderas de captura.

Aif
fuente
2
Recomiendo usarlo preg_match_all("/<a.*?href\s*=\s*['\"](.*?)['\"]/", $str, $res, PREG_SET_ORDER);para capturar correctamente todos los valores href en el usoforeach($res as $key => $val){echo $val[1]}
Ignacio Bustos
3

Para quien aún no obtiene las soluciones muy fáciles y rápidas usando SimpleXML

$a = new SimpleXMLElement('<a href="www.something.com">Click here</a>');
echo $a['href']; // will echo www.something.com

Esta funcionando para mi

Milán Malani
fuente
2

No estoy seguro de lo que está tratando de hacer aquí, pero si está tratando de validar el enlace, mire el filter_var () de PHP

Si realmente necesita usar una expresión regular, consulte esta herramienta, puede ayudar: http://regex.larsolavtorvik.com/

Adán
fuente
2

Usando su expresión regular, la modifiqué un poco para satisfacer sus necesidades.

<a.*?href=("|')(.*?)("|').*?>(.*)<\/a>

Yo personalmente sugiero que use un analizador HTML

EDITAR: Probado

Ruel
fuente
usando myregextester.com - lo siento, no encuentra los enlaces
bergin
dice: NO HAY PARTIDOS. VERIFICAR COLISIÓN DEL DELIMITADOR.
bergin
¿Puede decirme el texto que debe coincidir? Yo uso:<a title="this" href="that">what?</a>
Ruel
1

Examen rápido: <a\s+[^>]*href=(\"\'??)([^\1]+)(?:\1)>(.*)<\/a> parece hacer el truco, siendo la primera coincidencia "o", la segunda el valor de "href" "eso" y la tercera el "qué?".

La razón por la que dejé la primera coincidencia de "/ 'allí es que puedes usarla para hacer una referencia inversa más tarde para el cierre" /', por lo que es lo mismo.

Vea el ejemplo en vivo en: http://www.rubular.com/r/jsKyK2b6do

CharlesLeaf
fuente
1
@bergin, por favor especifique, ¿qué no funciona? Obtengo el valor exacto del href en su HTML de prueba. ¿Qué esperas que esto no haga? Veo que usa un sitio diferente para las pruebas, allí también obtengo el valor 'href' con éxito de su ejemplo. myregextester.com/?r=d966dd6b
CharlesLeaf
0

preg_match_all ("/ (] >) (. ?) (</ a) /", $ contenido, $ impmatches, PREG_SET_ORDER);

Se prueba y obtiene todas las etiquetas de cualquier código html.

Ravi Prakash
fuente
0

Lo siguiente funciona para mí y devuelve ambos hrefy valuede la etiqueta de anclaje.

preg_match_all("'\<a.*?href=\"(.*?)\".*?\>(.*?)\<\/a\>'si", $html, $match);
if($match) {
    foreach($match[0] as $k => $e) {
        $urls[] = array(
            'anchor'    =>  $e,
            'href'      =>  $match[1][$k],
            'value'     =>  $match[2][$k]
        );
    }
}

La matriz multidimensional llamada $urlsahora contiene submatrices asociativas que son fáciles de usar.

Meloman
fuente