Cómo agregar rel = “nofollow” a los enlaces con preg_replace ()

La función siguiente está diseñada para aplicar rel="nofollow" a todos los enlaces externos y sin enlaces internos a menos que la ruta coincida con una URL raíz predefinida definida como $my_folder continuación.

Entonces, dadas las variables …

 $my_folder = 'http://localhost/mytest/go/'; $blog_url = 'http://localhost/mytest'; 

Y el contenido …

 internal internal cloaked link external 

El resultado final, después del reemplazo debe ser …

 internal internal cloaked link external 

Tenga en cuenta que el primer enlace no se modifica, ya que es un enlace interno.

El enlace en la segunda línea también es un enlace interno, pero dado que coincide con nuestra cadena $my_folder , también obtiene el nofollow .

El tercer enlace es el más fácil, ya que no coincide con el blog_url , obviamente es un enlace externo.

Sin embargo, en el script a continuación, TODOS mis enlaces están obteniendo nofollow . ¿Cómo puedo arreglar el script para hacer lo que quiero?

 function save_rseo_nofollow($content) { $my_folder = $rseo['nofollow_folder']; $blog_url = get_bloginfo('url'); preg_match_all('~~isU',$content["post_content"],$matches); for ( $i = 0; $i "); $result .= ' rel="nofollow">'; $content["post_content"] = str_replace($matches[0][$i], $result, $content["post_content"]); } } return $content; } 

Intenta hacerlo más legible primero, y solo después haz que tus reglas sean más complejas:

 function save_rseo_nofollow($content) { $content["post_content"] = preg_replace_callback('~< (a\s[^>]+)>~isU', "cb2", $content["post_content"]); return $content; } function cb2($match) { list($original, $tag) = $match; // regex match groups $my_folder = "/hostgator"; // re-add quirky config here $blog_url = "http://localhost/"; if (strpos($tag, "nofollow")) { return $original; } elseif (strpos($tag, $blog_url) && (!$my_folder || !strpos($tag, $my_folder))) { return $original; } else { return "< $tag rel='nofollow'>"; } } 

Da la siguiente salida:

 [post_content] => internal internal cloaked link external 

El problema en su código original podría ser $ rseo que no fue declarado en ninguna parte.

Aquí está la solución DOMDocument …

 $str = 'internal internal cloaked link external external external external '; $dom = new DOMDocument(); $dom->preserveWhitespace = FALSE; $dom->loadHTML($str); $a = $dom->getElementsByTagName('a'); $host = strtok($_SERVER['HTTP_HOST'], ':'); foreach($a as $anchor) { $href = $anchor->attributes->getNamedItem('href')->nodeValue; if (preg_match('/^https?:\/\/' . preg_quote($host, '/') . '/', $href)) { continue; } $noFollowRel = 'nofollow'; $oldRelAtt = $anchor->attributes->getNamedItem('rel'); if ($oldRelAtt == NULL) { $newRel = $noFollowRel; } else { $oldRel = $oldRelAtt->nodeValue; $oldRel = explode(' ', $oldRel); if (in_array($noFollowRel, $oldRel)) { continue; } $oldRel[] = $noFollowRel; $newRel = implode($oldRel, ' '); } $newRelAtt = $dom->createAttribute('rel'); $noFollowNode = $dom->createTextNode($newRel); $newRelAtt->appendChild($noFollowNode); $anchor->appendChild($newRelAtt); } var_dump($dom->saveHTML()); 

Salida

 string(509) "< !DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">  internal internal cloaked link external external external external  " 

Prueba este (PHP 5.3+):

  • omitir la dirección seleccionada
  • Permitir establecer manualmente el parámetro rel

y código:

 function nofollow($html, $skip = null) { return preg_replace_callback( "#(]+?)>#is", function ($mach) use ($skip) { return ( !($skip && strpos($mach[1], $skip) !== false) && strpos($mach[1], 'rel=') === false ) ? $mach[1] . ' rel="nofollow">' : $mach[0]; }, $html ); } 

Ejemplos:

 echo nofollow('something'); // will be same because it's already contains rel parameter echo nofollow('something'); // ad // add rel="nofollow" parameter to anchor echo nofollow('something', 'localhost'); // skip this link as internall link 

Usar expresiones regulares para hacer este trabajo correctamente sería bastante complicado. Sería más fácil usar un analizador real, como el de la extensión DOM . DOM no es muy amigable para principiantes, entonces lo que puedes hacer es cargar el HTML con DOM y luego ejecutar las modificaciones con SimpleXML . Están respaldados por la misma biblioteca, por lo que es fácil usar uno con el otro.

Así es como se ve:

 $my_folder = 'http://localhost/mytest/go/'; $blog_url = 'http://localhost/mytest'; $html = ' internal internal cloaked link external '; $dom = new DOMDocument; $dom->loadHTML($html); $sxe = simplexml_import_dom($dom); // grab all  nodes with an href attribute foreach ($sxe->xpath('//a[@href]') as $a) { if (substr($a['href'], 0, strlen($blog_url)) === $blog_url && substr($a['href'], 0, strlen($my_folder)) !== $my_folder) { // skip all links that start with the URL in $blog_url, as long as they // don't start with the URL from $my_folder; continue; } if (empty($a['rel'])) { $a['rel'] = 'nofollow'; } else { $a['rel'] .= ' nofollow'; } } $new_html = $dom->saveHTML(); echo $new_html; 

Como pueden ver, es realmente corto y simple. Dependiendo de sus necesidades, es posible que desee utilizar preg_match() en lugar de las cosas strpos() , por ejemplo:

  // change the regexp to your own rules, here we match everything under // "http://localhost/mytest/" as long as it's not followed by "go" if (preg_match('#^http://localhost/mytest/(?!go)#', $a['href'])) { continue; } 

Nota

Me perdí el último bloque de código en el OP cuando leí la pregunta por primera vez. El código que publiqué (y básicamente cualquier solución basada en DOM) es más adecuado para procesar una página completa en lugar de un bloque de HTML. De lo contrario, DOM intentará “arreglar” su HTML y puede agregar una etiqueta , un DOCTYPE, etc.

 < ? $str='internal internal cloaked link external'; function test($x){ if (preg_match('@localhost/mytest/(?!go/)@i',$x[0])>0) return $x[0]; return 'rel="nofollow" '.$x[0]; } echo preg_replace_callback('/href=[\'"][^\'"]+/i', 'test', $str); ?>