Felix Riesterer: Regex um <img>-Elemente mit und ohne alt-Attribut

Liebe Mitlesende,

ich möchte den HTML-Quelltext einer Datei zu reinem Text reduzieren, um ihn inhaltlich durchsuchbar zu machen. Dabei entferne ich <img>-Elemente, bei denen ich jedoch ein eventuell gesetztes alt-Attribut (ich kenne das Muss aus der Spec) inhaltlich erhalten will.

Beispiel:

Schau mal: <img src="./dir/file.png" id="f" alt="flying dogs">  
Magst Du <img src="./dir/file2.jpg" class="favorites" title="Schoki bis zum Abwinken" />?

Aus obigem Code soll werden:

Schau mal:  flying dogs  
Magst Du   ?

Es soll also der Alt-Text in ein vorausgehendes und ein nachfolgendes Leerzeichen eingebettet als Ersatz für das ganze <img>-Element stehen. Ist kein Alt-Text vorhanden, stehen eben nur die Leerzeichen.

Folgenden PHP-Code habe ich erfolgreich getestet, jedoch kann er fehlende alt-Attribute nicht erkennen:

$entry['text'] = preg_replace(  
    '~(?is)<img[^>]*?alt="([^"]*)"[^>]*/?>~',  
    ' $1 ',  
    $tmp['html']  
);

Wenn ich das alt-Attribut optional mache (also in runde Klammern mit nachfolgendem Fragezeichen setze), wird der Inhalt nicht mehr als Ersatz notiert - so als würde keiner gefunden:

$entry['text'] = preg_replace(  
    '~(?is)<img[^>]*?(alt="([^"]*)")?[^>]*/?>~',  
    ' $2 ',  
    $tmp['html']  
);

Bei dem zweiten Code-Beispiel wird zwar das gesamte <img>-Element durch zwei Leerzeichen ersetzt, der Inhalt des alt-Attributs wird aber in keinem Fall eingetragen.

Wer kann mir helfen, dass der Inhalt des alt-Attributs nach der Ersetzung wieder erscheint?

Liebe Grüße,

Felix Riesterer.

--
ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
  1. Hello Lieer Felix,

    ich möchte den HTML-Quelltext einer Datei zu reinem Text reduzieren, um ihn inhaltlich durchsuchbar zu machen.

    Dann schau dir auch mal

    http://sourceforge.net/projects/simplehtmldom/

    an.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bikers-lodge.com
    1. Lieber Tom,

      http://sourceforge.net/projects/simplehtmldom/

      toller Tip! Danke! Hab's mir gezogen und werde damit etwas experimentieren. :-)

      Liebe Grüße,

      Felix Riesterer.

      --
      ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
      1. Hello lieber Felix,

        http://sourceforge.net/projects/simplehtmldom/

        toller Tip! Danke! Hab's mir gezogen und werde damit etwas experimentieren. :-)

        Und wie ich dich kenne, wirst Du deinen Schülern damit beweisen, dass es absolut unsinnig ist, ihre eMail-Namen auf den Webseiten hinter "blah_at_dings_dot_bums" zu verstecken ;-)

        Das behindert nur die Menschen, nicht aber die Maschinen.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bikers-lodge.com
  2. Hallo Felix!

    Bei dem zweiten Code-Beispiel wird zwar das gesamte <img>-Element durch zwei Leerzeichen ersetzt, der Inhalt des alt-Attributs wird aber in keinem Fall eingetragen.

    Wer kann mir helfen, dass der Inhalt des alt-Attributs nach der Ersetzung wieder erscheint?

    Mal abgesehen davon, dass regex nicht das optimale Werkzeug zum Parsen von html ist (<img alt=">"...>, nesting usw). Wenn das DEIN INPUT ist, dann weisst du ja, was du zu erwarten hast.

    Es sollte mit einem lookahead-conditional klappen, wenn auch nicht besonders schön:

    ~(?is)<img(?(?=[^>]* alt="([^"]+)")).*?>~

    nach dem <img...
    (? startet conditional
    (?= startet condition (lookahead)
    [^>]* alt="([^"]+)") hier sind wir noch in der Bedingung, in die wir auch gleich eine capturing-group gepackt haben, die den alt-text erfassen soll. Es ist kein ODER definiert, matcht also so oder so. Dann noch .*?  möglichst wenige irgendwelche Zeichen bis zum >.

    Ersetzt mit $1: dem, was durch die erste (hier einzige) capturing group gegebenenfalls gecaptured wurde. Beispiel zum probieren.

    Hoffe, die Idee hilft und herzliche Grüße,
    Jonny 5

    1. Lieber Jonny 5,

      Mal abgesehen davon, dass regex nicht das optimale Werkzeug zum Parsen von html ist

      das lese ich immer wieder, sehe im Moment aber nicht, warum ich es mir umständlicher machen sollte. Toms Hinweis auf die superleichte Parserklasse war da zwar schon ganz gut. Ich liebe Klassen, die in einer einzigen Datei notiert werden können! Mal sehen, welchen Nutzen mir das Teil noch bringen wird.

      (<img alt=">"...>, nesting usw). Wenn das DEIN INPUT ist, dann weisst du ja, was du zu erwarten hast.

      Solcher Input sollte ausgeschlossen sein, da HTML-Code von meinem CMS vor dem Speichern durch einen echten Parser gejagt wird, der alle < _und_ _>_ in Attributwerten als Entity notiert. Sollte mal ein per FTP hochgeladenes File in dieser Richtung eklig sein, dann stehen halt in den Suchindices "lustige Sachen" drin...

      Es sollte mit einem lookahead-conditional klappen, wenn auch nicht besonders schön:

      ~(?is)<img(?(?=[^>]* alt="([^"]+)")).*?>~

      uff! Was ein Monster! Aber hey, es funzt(tm)! Da sage ich artig "Danke"!

      Beispiel zum probieren.

      Herzlichen Dank! Diese Lookaheads setze ich nur sehr spärlich ein. Für ein in meinen Augen so läppisches Beispiel hatte ich erwartet, dass es solcher Geschütze nicht bedarf...

      Liebe Grüße,

      Felix Riesterer.

      --
      ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
      1. Hallo nochmal Felix!

        läppisches Beispiel hatte ich erwartet, dass es solcher Geschütze nicht bedarf...

        Findest du zu großkalibrig? Nach neuerlicher Überlegung müsste es eigentlich auch so klappen, indem die optional vorangehenden [^>]* auch in den optionalen Teil gepackt werden:

        ~<img(?>[^>]* alt="([^"]+)")?[^>]*>~is

        Verwende hier für den optionalen Teil eine atomic group (non-capturing), die für den alt-text wiederum eine capturing-group enhält. Im Prinzip sollte anstatt der atomic group hier auch eine non-capturing group (?: eingesetzt werden können.

        Das Problem war wohl dieser Teil [^>]*?(alt="([^"]*)")?[^>]*, bei dem die engine nicht wissen kann, dass du gerne den optionalen Teil (alt="([^"]*)")? zwischen zwei eigentlich ja auch optionalen Stücken [^>]* haben möchtest.

        Schönen Abend,
        Jonny 5

        1. Lieber Jonny 5,

          ~<img(?>[^>]* alt="([^"]+)")?[^>]*>~is

          klappt wie verrückt!

          Das Problem war wohl dieser Teil [^>]*?(alt="([^"]*)")?[^>]*, bei dem die engine nicht wissen kann, dass du gerne den optionalen Teil (alt="([^"]*)")? zwischen zwei eigentlich ja auch optionalen Stücken [^>]* haben möchtest.

          Ganz offensichtlich, ja. Deswegen hat das mit dem Look-ahead geklappt, mit meinen *-Quantifizierern aber nicht. Jetzt, glaube ich, habe ich etwas verstanden. Herzlichen Dank dafür!

          Liebe Grüße,

          Felix Riesterer.

          --
          ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
          1. Hallo Felix,

            klappt wie verrückt!

            das freut mich, habe auch wieder etwas dabei gelernt.

            ~<img(?>[^>]* alt="([^"]+)")?[^>]*>~is

            Achja, der s (PCRE_DOTALL) modifier wäre natürlich nicht mehr nötig, da ja in diesem pattern kein Metazeichen Punkt vorkommt, aber das hast du bestimmt auch schon bemerkt.

            viele Grüße,
            Jonny 5

            1. Lieber Jonny 5,

              klappt wie verrückt!

              das freut mich, habe auch wieder etwas dabei gelernt.

              :-))

              Achja, der s (PCRE_DOTALL) modifier wäre natürlich nicht mehr nötig, da ja in diesem pattern kein Metazeichen Punkt vorkommt, aber das hast du bestimmt auch schon bemerkt.

              Ich mache mir darüber keine Gedanken. Wenn ich im gesamten Quelltext eines Dokumentes "regexe", dann setze ich ihn per default - um unnötiges Fehlersuchen zu vermeiden. Stören tut er ja nicht!

              Liebe Grüße,

              Felix Riesterer.

              --
              ie:% br:> fl:| va:) ls:[ fo:) rl:| n4:? de:> ss:| ch:? js:) mo:} zu:)
  3. hi,

    ich möchte den HTML-Quelltext einer Datei zu reinem Text reduzieren, um ihn inhaltlich durchsuchbar zu machen. Dabei entferne ich <img>-Elemente, bei denen ich jedoch ein eventuell gesetztes alt-Attribut (ich kenne das Muss aus der Spec) inhaltlich erhalten will.

    Meine Idee hierzu triffts nicht ganz, löst jedoch eine weitere, evntl. bestehende Anforderung: Image-Sitemaps automatisch erstellen (für die Suchmaschine ein xml erzeugen, wo unter dem jeweiligen URL die dazugehörigen Images aufgelistet sind).

    Im HTML steht dann z.B. sowas:
    Hund:  %bild1%
    Katze: %bild2%

    also die Bilder sind einfach nur durchnumeriert. Damit die Template-Engine die Bilder in die richtigen Platzhalter setzt, bekommt der URL ein Attribut was die Bilder in der richtigen Reihenfolge enthält:
    bilder=/hund.png:AltHund:400:300 /katze.png:AltKatze:150:150

    woraus die Liste mit der richtigen Reihenfolge erzeugt wird. Nachdem die Template-Engine ihr Geschäft verrichtet hat, kriegt der Browser z.B.:
    Hund: <img src="/hund.png" alt="AltHund" width="400" height="300" title="AltHund" />

    Horst