Récupérer l'attribut href d'un élément A

114

Essayer de trouver les liens sur une page.

mon regex est:

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

mais semble échouer à

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

Comment changer mon expression régulière pour traiter href non placé en premier dans la balise a?

Bergin
la source

Réponses:

208

Les Regex fiables pour HTML sont difficiles . Voici comment le faire avec DOM :

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

Ce qui précède trouverait et afficherait le "externalHTML" de tous les Aéléments de la $htmlchaîne.

Pour obtenir toutes les valeurs de texte du nœud, vous faites

echo $node->nodeValue; 

Pour vérifier si l' hrefattribut existe, vous pouvez faire

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

Pour obtenir l' hrefattribut que vous feriez

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

Pour changer l' hrefattribut que vous feriez

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

Pour supprimer l' hrefattribut que vous feriez

$node->removeAttribute('href'); 

Vous pouvez également rechercher l' hrefattribut directement avec 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
}

Regarde aussi:

Sur une note latérale: je suis sûr que c'est un double et vous pouvez trouver la réponse quelque part ici

Gordon
la source
Une expression régulière fiable pour analyser le HTML est intrinsèquement impossible même si le HTML n'est pas un langage régulier.
Asciiom
19

Je suis d'accord avec Gordon, vous DEVEZ utiliser un analyseur HTML pour analyser le HTML. Mais si vous voulez vraiment une regex, vous pouvez essayer celle-ci:

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

Cela correspond <aau début de la chaîne, suivi par un nombre quelconque char (non gourmand) .*?puis href=suivi par le lien entouré par deux "ou'

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

Production:

array(3) {
  [0]=>
  string(37) "<a title="this" href="that">what?</a>"
  [1]=>
  string(1) """
  [2]=>
  string(4) "that"
}
Toto
la source
juste pour info: si nous recherchons dans un texte contenant plusieurs éléments, l'expression (. *?) est erronée
Michal - wereda-net
5

Le modèle que vous voulez rechercher serait le modèle d'ancrage de lien, comme (quelque chose):

$regex_pattern = "/<a href=\"(.*)\">(.*)<\/a>/";
Alex Pliutau
la source
1
Et si l'ancre a plus d'attributs?
funerr
3

pourquoi tu ne correspond pas

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

?>

puis

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

qui fonctionne. Je viens de supprimer les premières accolades de capture.

Aif
la source
2
Je recommande d'utiliser preg_match_all("/<a.*?href\s*=\s*['\"](.*?)['\"]/", $str, $res, PREG_SET_ORDER);pour attraper correctement toutes les valeurs href en utilisantforeach($res as $key => $val){echo $val[1]}
Ignacio Bustos
3

Pour ceux qui n'obtiennent toujours pas les solutions très faciles et rapides avec SimpleXML

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

Ça marche pour moi

Milan Malani
la source
2

Je ne suis pas sûr de ce que vous essayez de faire ici, mais si vous essayez de valider le lien, regardez le filtre_var () de PHP

Si vous avez vraiment besoin d'utiliser une expression régulière, consultez cet outil, cela peut vous aider: http://regex.larsolavtorvik.com/

Adam
la source
2

En utilisant votre regex, je l'ai un peu modifié en fonction de vos besoins.

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

Je vous suggère personnellement d'utiliser un analyseur HTML

EDIT: testé

Ruel
la source
en utilisant myregextester.com - désolé, ne trouve pas les liens
bergin
il dit: PAS DE MATCHS. VÉRIFIEZ LA COLLISION DU DELIMITER.
bergin
Pouvez-vous me dire le texte correspondant? J'utilise:<a title="this" href="that">what?</a>
Ruel du
1

Test rapide: <a\s+[^>]*href=(\"\'??)([^\1]+)(?:\1)>(.*)<\/a>semble faire l'affaire, le premier match étant "ou", le second la valeur "href" "cela" et le troisième le "quoi?".

La raison pour laquelle j'ai laissé la première correspondance de "/ 'là-dedans est que vous pouvez l'utiliser pour le référencer ultérieurement pour la fermeture" /' donc c'est la même chose.

Voir l'exemple en direct sur: http://www.rubular.com/r/jsKyK2b6do

CharlesLeaf
la source
1
@bergin veuillez préciser, qu'est-ce qui ne fonctionne pas? J'obtiens la valeur exacte du href dans votre HTML de test. Qu'attendez-vous que cela ne fasse pas? Je vois que vous utilisez un site différent pour les tests, là-bas, j'obtiens également avec succès la valeur 'href' de votre exemple. myregextester.com/?r=d966dd6b
CharlesLeaf
0

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

Il est testé et récupère toutes les balises de n'importe quel code html.

Ravi Prakash
la source
0

Ce qui suit fonctionne pour moi et renvoie à la fois hrefet valuede la balise d'ancrage.

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

Le tableau multidimensionnel appelé $urlscontient désormais des sous-tableaux associatifs faciles à utiliser.

Meloman
la source