2010-11-06 37 views
1

Donc, je veux faire correspondre les structures de lien ci-dessous avec un preg_match_all en php ..Avoir un peu de maux de tête regex avec des liens divers et délimiteurs href (« et «)

<a garbage href="http://this.is.a.link.com/?query=this has invalid spaces" possible garbage> 
<a garbage href='http://this.is.a.link.com/?query=this also has has invalid spaces' possible garbage> 
<a garbage href=http://this.is.a.link.com/?query=no_spaces_but_no_delimiters possible garbage> 
<a garbage href=http://this.is.a.link.com/?query=no_spaces_but_no_delimiters> 

je peux obtenir » et » urls deilmited un en faisant

'#<a[^>]*?href=("|\')(.*?)("|\')#is' 

ou je peux obtenir tous les trois, mais pas s'il y a des espaces dans les deux premiers avec:

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

Comment puis-je formuler ceci pour qu'il prenne "et" délimité avec des espaces potentiels, mais aussi des URL correctement encodées sans délimiteurs.

+1

[Le

ne peut pas tenir il est trop tard. ] (http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454) - aka ne pas analyser html avec regex, ce n'est pas possible. .. – ircmaxell

+0

BTW, je recommande d'utiliser cette syntaxe: [ab] au lieu de: (a | b) parce qu'il est plus commun (plus facile pour la plupart d'entre nous de lire), plus court, et probablement plus rapide. – JasonWoof

Répondre

1

OK, cela semble fonctionner:

'#<a[^>]*?href=((["\'][^\'"]+["\'])|([^"\'\s>]+))#is' 

($ matches [1] contient les urls)

seul inconvénient est que les urls cités ont les citations encore, de sorte que vous devrez les décoller:

$first = substr($match, 0, 1); 
if($first == '"' || $first == "'") 
    $match = substr($match, 1, -1); 
+0

Super, c'est parfait pour cette application. Je peux ressasser les résultats après pour couper les citations.Je voulais juste éviter d'exécuter deux preg_match_all pour obtenir les liens avec et sans délimiteurs, c'est une solution acceptable! en ce qui concerne les guillemets, preg_replace ("# ('|") # "," ", $ subject) récursivement fait l'affaire – tweak2

+1

trim ($ subject," \ "'") fait l'astuce pour désinfecter après , comme l'a souligné Alan. Il est probable que cela nécessite moins de ressources. – tweak2

1

EDIT: J'ai modifié cela pour travailler un peu mieux que ce que j'avais posté à l'origine.

Vous avez presque dans la seconde regex:

'#<a[^>]*?href=("|\')?(.*?)[\\1|>]#is' 

Renvoie le tableau suivant:

array(3) { 
    [0]=> 
    array(4) { 
    [0]=> 
    string(92) "<a garbage href="http://this.is.a.link.com/?query=this has invalid spaces" possible garbage>" 
    [1]=> 
    string(101) "<a garbage href='http://this.is.a.link.com/?query=this also has has invalid spaces' possible garbage>" 
    [2]=> 
    string(94) "<a garbage href=http://this.is.a.link.com/?query=no_spaces_but_no_delimiters possible garbage>" 
    [3]=> 
    string(77) "<a garbage href=http://this.is.a.link.com/?query=no_spaces_but_no_delimiters>" 
    } 
    [1]=> 
    array(4) { 
    [0]=> 
    string(1) """ 
    [1]=> 
    string(1) "'" 
    [2]=> 
    string(0) "" 
    [3]=> 
    string(0) "" 
    } 
    [2]=> 
    array(4) { 
    [0]=> 
    string(74) "http://this.is.a.link.com/?query=this has invalid spaces" possible garbage" 
    [1]=> 
    string(83) "http://this.is.a.link.com/?query=this also has has invalid spaces' possible garbage" 
    [2]=> 
    string(77) "http://this.is.a.link.com/?query=no_spaces_but_no_delimiters possible garbage" 
    [3]=> 
    string(60) "http://this.is.a.link.com/?query=no_spaces_but_no_delimiters" 
    } 
} 

Fonctionne avec ou sans délimiteurs.

+0

Ce dernier bit devrait être '(?: \ 1 |>)', pas '[\\ 1 |>]'. Les références arrières ne fonctionnent pas dans les classes de caractères et l'opérateur OR n'est pas nécessaire. Cela correspond en fait à l'une des: backslash, '1',' | ', ou'> '. D'un autre côté, '(" | \ ') ', bien que pas incorrect, serait beaucoup plus efficace si vous utilisiez une classe de caractères à la place:' (["\'])' –

0

Lorsque vous dites que vous voulez les faire correspondre, essayez-vous d'extraire des informations des liens, ou simplement de trouver des hyperliens avec un href? Si vous êtes seulement après ce dernier, cela devrait fonctionner très bien:

/<a[^>]*href=[^\s].*?>/ 
+0

cela supprime les liens qui sont " ou 'délimité et avoir des espaces en eux – tweak2

1

Utilisez un analyseur DOM. Vous ne pouvez pas analyser (x) HTML avec des expressions régulières.

$html = <<<END 
<a garbage href="http://this.is.a.link.com/?query=this has invalid spaces" possible garbage> 
<a garbage href='http://this.is.a.link.com/?query=this also has has invalid spaces' possible garbage> 
<a garbage href=http://this.is.a.link.com/?query=no_spaces_but_no_delimiters possible garbage> 
<a garbage href=http://this.is.a.link.com/?query=no_spaces_but_no_delimiters> 
END; 

$domd = new DOMDocument(); 
libxml_use_internal_errors(true); 
$domd->loadHTML($html); 
libxml_use_internal_errors(false); 

$items = $domd->getElementsByTagName("a"); 
foreach ($items as $item) { 
    var_dump($item->getAttribute("href")); 
} 
+0

Je pense qu'un dom parser lui-même utilise une forme de regex en interne et impliquerait des frais généraux inutiles.En mon expérience regex est incroyablement rapide.Si je faisais toute forme de collecte de données de page au-delà simple liens, je serais en utilisant un analyseur dom et en spécifiant des groupes – tweak2

+0

@ tweak2: Il n'utilise pas une forme de regex XML/HTML n'est pas un langage régulier, donc il n'est pas possible d'utiliser une regex. solution robuste de loin +1 – ircmaxell

+0

Qu'est-ce que c'est avec tous les gens et "pas une langue régulière" de la merde? Patterns n'ont pas été régulière car ils ont obtenu des références, et encore moins un tas d'autres choses comme la récursivité. les réponses sont comp sans importance pour le domaine de ce que les modèles modernes peuvent analyser. – tchrist

0

Comme @JasonWoof indiqué, vous devez utiliser une alternance intégrée: une alternative pour les URL cités, une pour non-cité. Je recommande également d'utiliser un groupe de capture pour déterminer quel type de citation est utilisé, comme @DanHorrigan l'a fait. Avec l'ajout d'une préanalyse négative ((?!\\2)) et quantificateurs possessifs (*+), vous pouvez créer une regex très robuste qui est aussi très rapide:

~ 
<a\\s+[^>]*?\\bhref= 
(
    (["'])   # capture the opening quote 
    (?:(?!\\2).)*+ # anything else, zero or more times 
    \\2    # match the closing quote 
| 
    [^\\s>]*+ # anything but whitespace or closing brackets 
) 
~ix 

See it in action on ideone. (Les antislashs doublés sont parce que le regex est écrit dans le forme d'un heredoc PHP, je préfèrerais utiliser un nowdoc, mais ideone est apparemment encore en cours d'exécution PHP 5.2.)