Der Kampf mit regulären Ausdrücken und negativem Lookahead

Heute mal ein kleiner Beitrag zum Thema reguläre Ausdrücke. Viele Programmierer fürchten sich vor Ihnen, doch bieten sie riesiges Potential, wenn man sie richtig einsetzt. Insbesondere soll es heute um ein Problem gehen, das bei mir aufkam, als ich ein Backlink-Check-Script gebastelt habe

Erstmal zum Hintergrund: Wenn eine andere Seite Backlinks setzt, bringt das bessere Google-Positionen (vereinfacht gesprochen, in der Hoffnung, dass kein SEO mitliest 😉 )
Jedenfalls wurde vor einigen Monaten von den großen Suchmaschinen-Anbietern das berühmt-berüchtigte nofollow-Attribut eingeführt, welches dafür sorgt, dass Links Ihre Power nicht an die verlinkte Seite weitergeben. Das ganze sieht dann so aus:

<a href="http://www.blabla.de" ref="nofollow">Linktext</a>

Ein Backlink-Check ist nun dazu da diese Links auf externen Seiten zu überprüfen.
Zielsetzung war also, dass ein Cronjob 1 mal pro Nacht ausgeführt wird, der sämtliche Seiten durchcheckt, die auf mich Links gesetzt haben. Sei es nun um irgendwelche Linktauschaktionen zu überwachen oder einfach darüber informiert zu sein, ob ein freiwillig gesetzter Link von einer anderen Seite besteht.g

Die DB dazu ist ganz einfach und soll hier nicht weiter erläutert werden – ist hier auch nicht von Interesse.

Das eigentliche Problem ist nun, dass der Anchor-Tag (<a>) noch viel mehr Attribute enthalten kann (z.B. target, class, style, id usw.). Deshalb ist es sinnvoll einen regulären Ausdruck zu benutzen.
Mein Ziel war es nun mit einem einzigen regulären Ausdruck festzustellen, ob es sich um einen gültigen, vererbenden Backlink handelt.

Mein erster Versuch war erstmal den Link-Tag zu meiner Seite aus dem Quelltext der externen URL herauszufiltern (ohne Beachtung des nofollow-Attributs).

$url = "http://www.halligalli.de/index.php";
$source = file_get_contents($url);
if(!preg_match("!<a ([^<]*?)href=[\"']http://www.meineurl.de(.*?)[\"'](.*?)>(.+?)</a>!is",$source)) echo "Link nicht gesetzt";

Dieser reguläre Ausdruck untersucht den Quelltext der URL halligalli.de (als mein fiktiver, externer Linkpartner) auf das Vorkommen eines Links zu meinem (ebenfalls fiktiven) Projekts meineurl.de. Wenn ein a-Tag vorkommt, das auf meine URL zeigt, “matcht” der reguläre Ausdruck, wenn nicht, wird “Link nicht gesetzt” ausgegeben.

Nun aber zum nofollow-Attribut (erste Erweiterung):

$url = "http://www.halligalli.de/index.php";
$source = file_get_contents($url);
preg_match("!<a ([^<]*?)href=[\"']http://www.meineurl.de(.*?)[\"'](.*?)>(.+?)</a>!is",$source,$link);
if(!$link[0] || strpos($link[0],"ref=\"nofollow\"")!==false || strpos($link[0],"ref='nofollow'")!==false) echo "Link nicht gesetzt";

Hier wird der “matchende” String (der a-Tag mit dem Link zu meiner Site) in das Array $link geschrieben. Falls es nun $link[0] nicht gibt, gibt es keinen Link zu meiner Website. Wenn es $link[0] gibt, muss noch überprüft werden, ob das nofollow-Attribut in diesem Link-Tag vorkommt. Das mache ich ganz einfach per strpos.
Diese Variante funktioniert, aber mein Ziel war eigentlich es mit einem regulären Ausdruck zu machen.

Letztlich wollte ich mit negativen Lookaheads (Wort darf nicht vorkommen: (!?wort) versuchen das nofollow-Attribut auszuschließen. Leider kam ich auf keine korrekte Lösung, weil zwischen dem Parameter href und ref ja auch noch zahlreiche andere stehen können bzw. die Reihenfolge der Attribute überhaupt nicht festgelegt ist. Falls da jemand eine Lösung hat, würde ich mich über einen Kommentar freuen.

Letztlich ist dann obige Lösung doch diejenige, die ich einsetze. Man kann es auch mit 2 preg_match-Funktionen machen, aber das hat kaum Sinn.

Wer aber schon lange so einen Backlink-Checker gesucht hat, der wird dieses Script eventuell einsetzen wollen. Ich hänge an dieser Stelle mal keine Datei an, denn es sind ja wirklich nur obige 3 Zeilen. Dort könntem an für mehrere Backlinks noch ein Schleifchen drumschnüren und falls es mehrere Links zur eigenen Seite auf der gleichen URL gibt, sollte man sich preg_match_all in diesem Zusammenhang mal ansehen.
Ich mache das dann im Übrigen auch nicht per Ausgabe, dass der Link nicht vorhanden ist (denn die liest ja niemand bei einem Cronjob), sondern ich schicke einfach eine Mail an mich.

Ich hoffe, dass Ihr dieses Script gebrauchen könnt, denn ich habe letztens in mehreren Foren gelesen, dass so etwas gesucht wird. Irgendwie wird aber kaum Brauchbares diesbezüglich kostenlos angeboten. Deshalb habe ich mich mal kurz selbst hingesetzt.
Bitte nach dem Copy-and-Paste die Hochkommas korrigieren. Die Blogsoftware verhandelt die in solche komischen, schrägen Hochkommas.

Jan hat 152 Beiträge geschrieben

11 Kommentare zu “Der Kampf mit regulären Ausdrücken und negativem Lookahead

  1. Andreas sagt:

    Ergänzung: Attributwerte, die aus einem Begriff bestehen müssen nicht zwangsläufig innerhalb von Quotes stehen 😉

    || strpos($link[0],”ref=nofollow”)!==false

  2. admin sagt:

    Oh, was es nicht alles gibt 😉
    Also entweder man macht sichs einfach und sucht per strpos einfach nach “nofollow” oder man muss sich ein wenig mehr anstrengen.
    Mir fällt dann aber grad nicht viel mehr ein…

  3. robo47 sagt:

    Vielleicht sollte man auch noch eine Überprüfung auf ein “nofollow” im Kopf des HTML-Dokuments machen, was wenn es nicht gerade die Startseite ist wo der Link kommt, auch vorkommen kann.

  4. admin sagt:

    Ungefähr so:
    !<a ([^<]*?)href=[\"']http://www.meineurl.de(.*?)[\"'](.*?)(!?ref=[\"']?nofollow[\"']?)(.*?)>(.+?)</a>!is
    Problem daran ist eben, dass der reguläre Ausdruck dann auf das ref=”nofollow” stößt und dieses Attribut dann eben einfach zu dem (.*?) davor bzw. dahinter dazuschaufelt. Problem ist eben, dass die Reihenfolge der Attribute willkürlich wählbar ist.

  5. PHP Blogger sagt:

    Hi, die Cronjobausgaben kann man sich einfach per Mail zu schicken lassen (ohne die Mail mit PHP zu versenden). Auf meinem Rootserver ist das z.B. praktisch, um die Backup-Zusammenfassung zu lesen…

  6. admin sagt:

    An so was ähnliches hab ich eben aufm Heimweg auch gedacht. Bin zwar kein Freund von Python, aber man könnte es ja mit SimpleXML lösen.
    Einfach per preg_match_all() sämtliche Links zu einer bestimmten Domain holen, in ein XML-Dokument packen und anschließend jedes Link-Tag auf das Attribut rel untersuchen.
    Na ich bastel da mal was…da ist die letzte Codezeile noch nicht geschrieben 😉

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>