Jannes: mit RegExp alle Links auslesen, aber nicht die mit rel=nofollow

Hallo zusammen,

wie schon im Betreff zu lesen ist, möchte ich alle Links aus einem HTML-Body auslesen. Das mache ich momentan so:

preg_match_all('/<a.*?href='"['"].*?</a>/msi', $body, $links);

Ich möchte aber keine Links in den Treffern haben die ein rel=nofollow beinhalten. Wie kann man das mit in den RegExp-Ausdruck bringen? Es soll ja für links passen die so:
<a href="xy.html" rel="nofollow">xy</a>
oder so:
<a rel="nofollow" href="xy.html">xy</a>
geschrieben sind.

Gruß,
Jannes

  1. Hi,

    Ich möchte aber keine Links in den Treffern haben die ein rel=nofollow beinhalten. Wie kann man das mit in den RegExp-Ausdruck bringen?

    "Some people, when confronted with a problem, think 'I know, I’ll use regular expressions.' Now they have two problems." --Jamie Zawinski

    Regular Expressions sind ein mächtiges Hilfsmittel. In verdammt vielen Fällen sind sie jedoch keine Lösung. Hier beispielsweise trifft dies zu.

    Cheatah

    --
    X-Self-Code: sh:( fo:} ch:~ rl:| br:> n4:& ie:% mo:) va:) de:] zu:) fl:{ ss:) ls:~ js:|
    X-Self-Code-Url: http://emmanuel.dammerer.at/selfcode.html
    X-Will-Answer-Email: No
    X-Please-Search-Archive-First: Absolutely Yes
    1. Hi Cheatah,

      Regular Expressions sind ein mächtiges Hilfsmittel. In verdammt vielen Fällen sind sie jedoch keine Lösung. Hier beispielsweise trifft dies zu.

      Ok, da bin ich ja einerseits schon mal froh, weil ich damit eh' nicht so gut zurecht komme ;-)

      Hast Du vielleicht einen Vorschlag für einen anderen Ansatz?

      (Sonst würde ich evtl. doch mit preg_match arbeiten, und zuerst alle Links _komplett_ sammeln, und diese Liste dann einzeln abarbeiten)

      Gruß,
      Jannes

      1. (Sonst würde ich evtl. doch mit preg_match arbeiten, und zuerst alle Links _komplett_ sammeln, und diese Liste dann einzeln abarbeiten)

        in perl würde ich das machen
        s# ( <a \s [^<>]+ > [^<>]+ </a> ) #parse_link($1)#egx;

        Equivalent in PHP wäre für dich eine callback Funktion.

        mfg Beat

        --
        ><o(((°>           ><o(((°>
           <°)))o><                     ><o(((°>o
        Der Valigator leibt diese Fische
        1. Hi,

          »» (Sonst würde ich evtl. doch mit preg_match arbeiten, und zuerst alle Links _komplett_ sammeln, und diese Liste dann einzeln abarbeiten)

          in perl würde ich das machen
          s# ( <a \s [^<>]+ > [^<>]+ </a> ) #parse_link($1)#egx;

          Das findet aber nicht alle Links, z.B.:
          <a href="bla"><b>blubb</b></a>

          Dafür findet es auch Nichtlinks, z.B.:
          <a name="bla">blubb</a>

          cu,
          Andreas

          --
          Warum nennt sich Andreas hier MudGuard?
          O o ostern ...
          Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
          1. Hi,

            Dafür findet es auch Nichtlinks, z.B.:
            <a name="bla">blubb</a>

            jaja, href= muß schon mit drin sein, (soweit war ich schon).

            Also, ich habs jetzt so gemacht:
            preg_match_all('/(<a.*?href=['"].*?['"].*?</a>)/msi', $page, $links);
            foreach($links[1] as $a)
            {
            if(preg_match('/rel=["']nofollow/i',$a))
            {
            continue;
            }
            if(!preg_match('/href='"['"]/i', $a, $link))
            {
            continue;
            }
            $a = $link[1];

            ...  
            

            Das ist vielleicht nicht so performant, aber da die Datei eh nur 1-2 mal im Monat laufen soll ist mir das egal. Und es hat den Vorteil das ich verstehe was passiert ;-)

            Gruß,
            Jannes

            1. if(preg_match('/rel=["']nofollow/i',$a))

              Das ist nun wirklich übertrieben.

              1. »» if(preg_match('/rel=["']nofollow/i',$a))

                Das ist nun wirklich übertrieben.

                Ja? Wieso denn?

                Es _könnte_ ja das im Tag stehen:

                rel='nofollow'
                 rel="nofollow"
                 rel=nofollow
                 class="nofollow" (ohne rel attribut)

                oder was weiß ich.

                Aber es muß noch ein Sternchen rein, für rel=nofollow
                '/rel=["']*nofollow/i'

                oder was?

                Gruß,
                Jannes

                1. Ich habe zwar noch nie rel="nofollow", oder welche Schreibweise auch immer, benutzt aber kann nofollow nicht nur in einer Bedeutung zwischen "<a" und dem nächsten ">" vorkommen? Warum nicht nur nach nofollow suchen, ohne preg_match?

                  Auch solltest Du bei deinem Vorgehen bedenken, daß rel="nofollow" oder ähnliches im Linktext vorkommen könnte. Du solltest den Platz in $links[1] lieber für was anderes benutzen und nicht noch mal das reinschreiben, was in $links[0] ohnehin schon steht.

                  1. Hi,

                    Ich habe zwar noch nie rel="nofollow", oder welche Schreibweise auch immer, benutzt aber kann nofollow nicht nur in einer Bedeutung zwischen "<a" und dem nächsten ">" vorkommen? Warum nicht nur nach nofollow suchen, ohne preg_match?

                    Hat er doch schon geschrieben. z.B. class="nofollow".

                    cu,
                    Andreas

                    --
                    Warum nennt sich Andreas hier MudGuard?
                    O o ostern ...
                    Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
            2. Hi,

              preg_match_all('/(<a.*?href=['"].*?['"].*?</a>)/msi', $page, $links);

              Schau mal, was da gematcht wird ;-)

              <a name="bla">blubb</a><a href="bla">blubb</a>

              cu,
              Andreas

              --
              Warum nennt sich Andreas hier MudGuard?
              O o ostern ...
              Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
              1. Hi,

                »» preg_match_all('/(<a.*?href=['"].*?['"].*?</a>)/msi', $page, $links);

                Schau mal, was da gematcht wird ;-)

                <a name="bla">blubb</a><a href="bla">blubb</a>

                das ist allerdings echt doof! Da werde ich wohl noch mal ran müßen.

                Gruß,
                Jannes

                1. Hi,

                  das ist allerdings echt doof! Da werde ich wohl noch mal ran müßen.

                  Siehe auch https://forum.selfhtml.org/?t=185389&m=1230136

                  cu,
                  Andreas

                  --
                  Warum nennt sich Andreas hier MudGuard?
                  O o ostern ...
                  Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                  1. Hi,

                    Siehe auch https://forum.selfhtml.org/?t=185389&m=1230136

                    :-) ja, ich hätte es so machen sollen wie mein Gefühl es mir schon zu Anfang gesagt hat: lass die Finger von Kram den du nicht verstehst!

                    Ausgangspunkt für das unglückliche Abenteuer war, das es Seiten geben kann, die im meta-robots halt noindex,follow stehen haben können. Für den Fall wollte ich hier, sozusagen mal schnell vorab, die Links rausfiltern. Bei den Seiten die zu indexieren sind gehe ich sowieso den Text komplett einzeln durch (etwas später im Code). Ich werde wohl hier neu ansetzen. Da kann ich mir das ganze preg_gematche im Vorfeld sparen ;-)

                    Gruß,
                    Jannes

                    1. Hi,

                      :-) ja, ich hätte es so machen sollen wie mein Gefühl es mir schon zu Anfang gesagt hat: lass die Finger von Kram den du nicht verstehst!

                      schon, aber dann lernst Du ja nichts.

                      Cheatah

                      --
                      X-Self-Code: sh:( fo:} ch:~ rl:| br:> n4:& ie:% mo:) va:) de:] zu:) fl:{ ss:) ls:~ js:|
                      X-Self-Code-Url: http://emmanuel.dammerer.at/selfcode.html
                      X-Will-Answer-Email: No
                      X-Please-Search-Archive-First: Absolutely Yes
          2. »» (Sonst würde ich evtl. doch mit preg_match arbeiten, und zuerst alle Links _komplett_ sammeln, und diese Liste dann einzeln abarbeiten)

            in perl würde ich das machen
            s# ( <a \s [^<>]+ > [^<>]+ </a> ) #parse_link($1)#egx;

            Das findet aber nicht alle Links, z.B.:
            <a href="bla"><b>blubb</b></a>

            Dafür findet es auch Nichtlinks, z.B.:
            <a name="bla">blubb</a>

            Du hast offenbar nicht aufgepasst.

            Das ist keine regex, die zum Ziel führt. sonst bräuchte ich ja s///eg nicht.
            Das ist eine Regex, die erst mal das Rohmaterial an eine Sub übergibt.
            In dieser kannst du dann entscheiden, was wie zu machen ist.

            mfg Beat

            --
            ><o(((°>           ><o(((°>
               <°)))o><                     ><o(((°>o
            Der Valigator leibt diese Fische
      2. Hi,

        Hast Du vielleicht einen Vorschlag für einen anderen Ansatz?

        ja:

        (Sonst würde ich evtl. doch mit preg_match arbeiten, und zuerst alle Links _komplett_ sammeln, und diese Liste dann einzeln abarbeiten)

        ;-)

        Cheatah

        --
        X-Self-Code: sh:( fo:} ch:~ rl:| br:> n4:& ie:% mo:) va:) de:] zu:) fl:{ ss:) ls:~ js:|
        X-Self-Code-Url: http://emmanuel.dammerer.at/selfcode.html
        X-Will-Answer-Email: No
        X-Please-Search-Archive-First: Absolutely Yes
  2. Kennst Du schon Look-around assertions* und hast damit versucht zum Ziel zu kommen?

    * Der <denk>einzige</denk> Weg Zeichenketten zu "negieren".

    1. Kennst Du schon Look-around assertions* und hast damit versucht zum Ziel zu kommen?

      * Der <denk>einzige</denk> Weg Zeichenketten zu "negieren".

      Ich denke kaum dass das hier angesagt ist.
      Du kannst ein <a> Element parsen lassen, und dann Attribute abfragen.
      Das führt zu kontrollierteren Ergebnissen, als mit Regulären Ausdrücken zu sagen, was du nicht willst. Damit wirst du nämlich nie fertig.

      mfg Beat

      --
      ><o(((°>           ><o(((°>
         <°)))o><                     ><o(((°>o
      Der Valigator leibt diese Fische
      1. Ich denke kaum dass das hier angesagt ist.

        Darauf hat Cheatah ja schon hingewiesen und Jannes wird den Weg wohl auch lieber gehen.

        Das führt zu kontrollierteren Ergebnissen, als mit Regulären Ausdrücken zu sagen, was du nicht willst. Damit wirst du nämlich nie fertig.

        Mit einer Bedingung wird man schon fertig.

        1. Mit einer Bedingung wird man schon fertig.

          Und wo setzt du die Bedingung an?

          mfg Beat

          --
          ><o(((°>           ><o(((°>
             <°)))o><                     ><o(((°>o
          Der Valigator leibt diese Fische
          1. »» Mit einer Bedingung wird man schon fertig.

            Und wo setzt du die Bedingung an?

            Zwischen "<a" und dem nächsten ">" aber natürlich nicht alleine. Oder darf "rel=nofollow" noch woanders stehen?

            1. »» Mit einer Bedingung wird man schon fertig.
              Und wo setzt du die Bedingung an?
              Zwischen "<a" und dem nächsten ">" aber natürlich nicht alleine. Oder darf "rel=nofollow" noch woanders stehen?

              Werde praktisch!

              <a(?!rel=nofollow)
              oder
              <a(?!.*rel=nofollow)
              oder
              <a\s(?!rel=nofollow)
              oder ...

              mfg Beat

              --
              ><o(((°>           ><o(((°>
                 <°)))o><                     ><o(((°>o
              Der Valigator leibt diese Fische
              1. »» > »» Mit einer Bedingung wird man schon fertig.
                »» > Und wo setzt du die Bedingung an?
                »» Zwischen "<a" und dem nächsten ">" aber natürlich nicht alleine. Oder darf "rel=nofollow" noch woanders stehen?

                Werde praktisch!

                Daß es nicht unbedingt praktisch ist, wissen wir doch nun.

              2. Hi,

                selbst wenn man die fehlenden "" ergänzt:

                <a(?!rel=nofollow)
                <a\s(?!rel=nofollow)

                jeweils:
                <a href="bla" rel="nofollow">blubb</a>

                (a kann ne ganze Reihe Attribute haben, das rel="nofollow" kann also an vielen Stellen stehen, wird also ziemlich aufwändig).

                <a(?!.*rel=nofollow)

                <a href="bla">blubb</a><a rel="nofollow"

                (da hilft auch not-greedy nicht)

                cu,
                Andreas

                --
                Warum nennt sich Andreas hier MudGuard?
                O o ostern ...
                Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                1. (a kann ne ganze Reihe Attribute haben, das rel="nofollow" kann also an vielen Stellen stehen, wird also ziemlich aufwändig).

                  Warum sollten einen die vielen anderen Attribute, außer href, interessieren?

                  1. Hi,

                    »» (a kann ne ganze Reihe Attribute haben, das rel="nofollow" kann also an vielen Stellen stehen, wird also ziemlich aufwändig).

                    Warum sollten einen die vielen anderen Attribute, außer href, interessieren?

                    Der gegebene Ausdruck
                    <a\s(?!rel="nofollow")
                    verbietet rel="nofollow" nur als erstes Attribut.
                    Wenn man nach dieser Methode vorgeht und nicht wie von Dir per
                    "Zwischen "<a" und dem nächsten ">" aber natürlich nicht alleine. Oder darf "rel=nofollow" noch woanders stehen?"
                    muß man an jeder möglichen Vorkommens-Stelle verbieten.
                    Also nach jedem möglichen Attribut.
                    Oder eben so wie von Dir vorgeschlagen insgesamt innerhalb von <a und dem nächstmöglichen > (wobei es genaugenommen das nächstmögliche > außerhalb eines Attributwerts sein muß - < ist zwar innerhalb von Attributwerten nicht erlaubt, > dagegen schon, z.B. title="-->" ...).

                    Der von Beat vorgeschlagene Ausdruck matcht zwar richtigerweise nicht auf
                    <a ref="nofollow" href="bla"
                    ber er matcht fälschlicherweise auf
                    <a href="bla" ref="nofollow"
                    oder
                    <a id="blubb" ref="nofollow" href="bla"
                    oder
                    <a style="color:red" ref="nofollow" href="bla"
                    oder
                    <a style="color:red" href="bla" ref="nofollow"

                    Wenn man also nicht insgesamt \sref=["']nofollow["']\s? *) innerhalb von <a > verbietet (wie von Dir vorgeschlagen), sondern dies explizit an bestimmten Stellen tun will (wie von Beat vorgeschlagen), dann muß man eben alle möglichen Stellen angeben.
                    Oder zwischen <a und dem ref=["']nofollow["'] muß eine beliebige Anzahl von Attributen samt ggf. Wert zugelassen werden. Also sowas wie
                    (?:\s+(?:id|style|class|href(?:lang)?|name|lang)*(?:=(?:"[^"]*"|'[^']*'))?)*
                    wobei die Liste der zulässigen Attributnamen hier nur angedeutet ist und sicher nicht vollständig ist. Und bei den Attributwerten muß für den Fall HTML (statt XHTML) auch noch die Variante ohne Anführungszeichen berücksichtigt werden.
                    Und im Falle von HTML auch noch die Shorttags-Variante <a href=bla/blubb/

                    Insgesamt gesehen war mein Einwand an dieser Stelle nur ein weiterer auf dem Weg, klarzumachen, daß das mit regulären Ausdrücken nicht wirklich einfach ist. Die anderen (von Beat vorgeschlagenen) Ausdrücke hab ich ja schon an anderen Stellen des Threads als unzutreffend dargestellt.

                    Das wirklich sauber und vollständig als Regex zu implementieren, ist ziemlich aufwändig - da dürfte es wirklich einfacher sein, einen (fertigen) Parser für die jeweilige Dokumentsprache zu benutzen. Die sind in langer Arbeit entstanden und enthalten mit Glück auch schon alle Sonderfälle.

                    Ich behaupte mal, daß ich nicht gerade ein Anfänger bin bzgl. Regex, aber einen Regex zu schreiben, der wirklich alle laut (X)HTML-Standard zulässigen Sonderfälle berücksichtigt, würde mich sicher einige Tage kosten.
                    Wenn man dann noch die Fälle dazunimmt, die zwar nicht standardkonform sind, aber in gängigen Browsern dennoch funktionieren, wird's ein Riesenaufwand.

                    *) was auch schon wieder eine Ungenauigkeit enthält, denn das zweite ["'] müßte eigentlich eine Backreferenz auf das erste ["'] sein, denn nur matchende "" bzw. '' grenzen einen Attributwert ein, weder attr="val' noch attr='val" ist korrekt - oder es muß, wie oben angedeutet, als Alternative geschrieben werden.

                    cu,
                    Andreas

                    --
                    Warum nennt sich Andreas hier MudGuard?
                    O o ostern ...
                    Fachfragen unaufgefordert per E-Mail halte ich für unverschämt und werde entsprechende E-Mails nicht beantworten. Für Fachfragen ist das Forum da.
                    1. Hallo,

                      da habe aber eine Antwort provoziert. Meine Frage war eher rhetorisch, weil es Jannes nur um das eine Attribut ging.

                      ... innerhalb von <a und dem nächstmöglichen > (wobei es genaugenommen das nächstmögliche > außerhalb eines Attributwerts sein muß - < ist zwar innerhalb von Attributwerten nicht erlaubt, > dagegen schon, z.B. title="-->" ...).

                      Daran habe ich nicht gedacht.

  3. Es soll ja für links passen die so:
    <a href="xy.html" rel="nofollow">xy</a>
    oder so:
    <a rel="nofollow" href="xy.html">xy</a>
    geschrieben sind.

    Das kann in letzter Konsequenz nur ein echter HTML-Parser berücksichtigen, der dir eine Schnittstelle wie z.B. das DOM anbietet.
    Sind das nur eigene oder beliebige HTML-Dokumente, die du da verarbeitest?

    Mathias

    1. Hi,

      Das kann in letzter Konsequenz nur ein echter HTML-Parser berücksichtigen, der dir eine Schnittstelle wie z.B. das DOM anbietet.
      Sind das nur eigene oder beliebige HTML-Dokumente, die du da verarbeitest?

      Ausgangspunkt war, die eigene Seite mit einer komfortablen und kontrollierbaren Suchfunktion auszustatten, aber der Anspruch ist gewachsen. Nach Möglichkeit wäre es auch schön, wenn man beliebige (kleine) Seiten oder Seitenzweige scannen könnte.

      Gruß,
      Jannes