Pegando o atributo href de um elemento A

114

Tentando encontrar os links em uma página.

minha regex é:

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

mas parece falhar em

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

Como eu mudaria minha regex para lidar com href não colocado primeiro na tag a?

Bergin
fonte

Respostas:

208

Regex confiável para HTML é difícil . Aqui está como fazer isso com DOM :

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

O código acima iria encontrar e produzir o "outerHTML" de todos os Aelementos da $htmlstring.

Para obter todos os valores de texto do nó, você faz

echo $node->nodeValue; 

Para verificar se o hrefatributo existe, você pode fazer

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

Para obter o hrefatributo, você faria

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

Para alterar o hrefatributo, você faria

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

Para remover o hrefatributo que você faria

$node->removeAttribute('href'); 

Você também pode consultar o hrefatributo diretamente com 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
}

Veja também:

Em uma nota: tenho certeza de que é uma duplicata e você pode encontrar a resposta em algum lugar aqui

Gordon
fonte
Regex confiável para análise de HTML é inerentemente impossível, mesmo porque o HTML não é uma linguagem regular.
Asciiom de
19

Eu concordo com Gordon, você DEVE usar um analisador de HTML para analisar HTML. Mas se você realmente deseja uma regex, pode tentar esta:

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

Este partidas <ano início da cadeia de caracteres, seguido por um número qualquer de qualquer carvão animal (não ávido) .*?, em seguida, href=seguido pela ligação rodeado por um ou outro "ou'

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

Resultado:

array(3) {
  [0]=>
  string(37) "<a title="this" href="that">what?</a>"
  [1]=>
  string(1) """
  [2]=>
  string(4) "that"
}
Totó
fonte
apenas para informação: se pesquisarmos em um texto contendo muitos elementos, a expressão (. *?) está errada
Michal - wereda-net
5

O padrão que você deseja procurar seria o padrão de âncora do link, como (algo):

$regex_pattern = "/<a href=\"(.*)\">(.*)<\/a>/";
Alex Pliutau
fonte
1
E se a âncora tiver mais atributos?
funerr
3

por que você simplesmente não combina

"<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);

?>

então

$ 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. Acabei de remover as primeiras chaves de captura.

Aif
fonte
2
Eu recomendo usar a preg_match_all("/<a.*?href\s*=\s*['\"](.*?)['\"]/", $str, $res, PREG_SET_ORDER);fim de capturar corretamente todos os valores href no usoforeach($res as $key => $val){echo $val[1]}
Ignacio Bustos
3

Para quem ainda não conseguiu as soluções muito fáceis e rápidas usando SimpleXML

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

Está funcionando para mim

Milan Malani
fonte
2

Não tenho certeza do que você está tentando fazer aqui, mas se você está tentando validar o link, olhe em filter_var do PHP ()

Se você realmente precisa usar uma expressão regular, verifique esta ferramenta, ela pode ajudar: http://regex.larsolavtorvik.com/

Adão
fonte
2

Usando seu regex, eu o modifiquei um pouco para atender às suas necessidades.

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

Eu pessoalmente sugiro que você use um analisador de HTML

EDIT: Testado

Ruel
fonte
usando myregextester.com - desculpe, não faz encontrar os links
bergin
diz: NO MATCHES. VERIFIQUE PARA COLISÃO DE DELIMITER.
Bergin
Você pode me dizer o texto para corresponder? Eu uso:<a title="this" href="that">what?</a>
Ruel
1

Teste rápido: <a\s+[^>]*href=(\"\'??)([^\1]+)(?:\1)>(.*)<\/a>parece funcionar, com a primeira correspondência sendo "ou ', a segunda o' href 'valor' que 'e a terceira o' o quê? '

A razão pela qual deixei a primeira correspondência de "/ 'lá é que você pode usá-la para referenciá-la posteriormente para o fechamento" /', então é a mesma coisa.

Veja o exemplo ao vivo em: http://www.rubular.com/r/jsKyK2b6do

CharlesLeaf
fonte
1
@bergin especifique, o que não funciona? Pego o valor exato de href em seu HTML de teste. O que você espera que isso não aconteça? Vejo que você usa um site diferente para teste, lá também obtenho o valor 'href' com sucesso de seu exemplo. myregextester.com/?r=d966dd6b
CharlesLeaf
0

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

Ele é testado e busca todas as tags de qualquer código html.

Ravi Prakash
fonte
0

O seguinte está funcionando para mim e retorna hrefe valueda tag âncora.

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]
        );
    }
}

A matriz multidimensional chamada $urlscontém agora submatrizes associativas que são fáceis de usar.

Meloman
fonte