views:

24637

answers:

11

I would like to create a page where all images which reside on my website are listed with title and alternative representation.

I already wrote me a little program to find and load all html files, but now I am stuck at how to extract src, title and alt from the html
< img src="/image/fluffybunny.jpg" title="Harvey the bunny" alt="a cute little fluffy bunny"/>

I guess this should be done with some regex, but since the order of the tags may vary, and I need all of them, I don't really know how to parse this in an elegant way (I could do it the hard char by char way, but thats painful).

A: 

You can also try SimpleXML if the HTML is guaranteed to be XHTML - it will parse the markup for you and you will be able to access the attributes just by their name. (There are DOM libraries as well if it's just HTML and you can't depend on the XML syntax.)

Borek
I don't think it's XHTML - for the DOM libraries, have you more information about these, and how to use them for this question? Thanks!
Sam
Well it has been answered by Anonymous...
Borek
A: 

How about using a regular expression to find the img tags (something like "<img[^>]*>"), and then, for each img tag, you could use another regular expression to find each attribute.

Maybe something like " ([a-zA-Z]+)=\"([^"]*)\"" to find the attributes, though you might want to allow for quotes not being there if you're dealing with tag soup... If you went with that, you could get the parameter name and value from the groups within each match.

MB
Uh, yes, I was thinking something along these lines, but I'm looking for an implementation of the idea - I'm not good at regex :(
Sam
No me neither! A regexp tester is worth its weight in gold - I use an eclipse plugin but I'm sure there are many others available. Also the regexp information at www.regular-expressions.info is the best I've seen online. My guess is that regexp would be the simplest way to do what you want to do.
MB
I use RegexBuddy, it's awesome :) It's a pity though that there's no standard syntax for regex find/replace (so the regex you put in also does the replace operation simultaneously. Instead, you need program code to do the two parts.
Chris Dennett
A: 

You can write a regexp to get all img tags (<img[^>]*>), and then use simple explode: $res = explode("\"", $tags), the output will be something like this:

$res[0] = "<img src=";
$res[1] = "/image/fluffybunny.jpg";
$res[2] = "title=";
$res[3] = "Harvey the bunny";
$res[4] = "alt=";
$res[5] = "a cute little fluffy bunny";
$res[6] = "/>";

If you delete the <img tag before the explode, then you will get an array in the form of

property=
value

so the order of the properties are irrelevant, you only use what you will like.

Biri
+10  A: 

Use xpath.

For php you can use simplexml or domxml

see also this question

Anonymous
Good idea, never thought of, worth an upvote.
Biri
xpath seems cool from what I can gleam - do you need to install libraries to use it, and where could I find usage examples for php?
Sam
yup, my thoughts exactly.. You could pull out your regex tricks... but what you need is to traverse the DOM
paan
look at the first two links for php examples, and the stackoverflow link to the oder post, there is a good tutorial on xpath
Anonymous
+16  A: 

Just to give a small example of using PHP's XML functionality for the task:

$doc=new DOMDocument();
$doc->loadHTML("<html><body>Test<br><img src=\"myimage.jpg\" title=\"title\" alt=\"alt\"></body></html>");
$xml=simplexml_import_dom($doc); // just to make xpath more simple
$images=$xml->xpath('//img');
foreach ($images as $img) {
    echo $img['src'] . ' ' . $img['alt'] . ' ' . $img['title'];
}

I did use the DOMDocument::loadHTML() method because this method can cope with HTML-syntax and does not force the input document to be XHTML. Strictly speaking the conversion to a SimpleXMLElement is not necessary - it just makes using xpath and the xpath results more simple.

Stefan Gehrig
Surely this approach is very straight-forward but someone might want to use the @ sign when calling the loadHTML method (@$doc->loadHTML) as it would prevent warnings from showing up.
Alex Polo
+3  A: 

If it's XHTML, your example is, you need only simpleXML.

<?php
$input = '<img src="/image/fluffybunny.jpg" title="Harvey the bunny" alt="a cute little fluffy bunny"/>';
$sx = simplexml_load_string($input);
var_dump($sx);
?>

Output:

object(SimpleXMLElement)#1 (1) {
  ["@attributes"]=>
  array(3) {
    ["src"]=>
    string(22) "/image/fluffybunny.jpg"
    ["title"]=>
    string(16) "Harvey the bunny"
    ["alt"]=>
    string(26) "a cute little fluffy bunny"
  }
}
DreamWerx
+13  A: 

EDIT : now that I know better

Using regexp to solve this kind of problem is a bad idea and will likely lead in unmaintainable and unreliable code. Better us an HTML parser.

Solution With regexp

In that case it's better to split the process in two parts :

  • get all the img tag
  • extract their metadata

I will assume your doc is not xHTML strict so you can't use an XML parsor. E.G. with this web page source code :

/* preg_match_all match the regexp in all the $html string and output everything as 
an array in $result. "i" option is used to make it case insensitive */

preg_match_all('/<img[^>]+>/i',$html, $result); 

print_r($result);
Array
(
    [0] => Array
        (
            [0] => <img src="/Content/Img/stackoverflow-logo-250.png" width="250" height="70" alt="logo link to homepage" />
            [1] => <img class="vote-up" src="/content/img/vote-arrow-up.png" alt="vote up" title="This was helpful (click again to undo)" />
            [2] => <img class="vote-down" src="/content/img/vote-arrow-down.png" alt="vote down" title="This was not helpful (click again to undo)" />
            [3] => <img src="http://www.gravatar.com/avatar/df299babc56f0a79678e567e87a09c31?s=32&amp;d=identicon&amp;r=PG" height=32 width=32 alt="gravatar image" />
            [4] => <img class="vote-up" src="/content/img/vote-arrow-up.png" alt="vote up" title="This was helpful (click again to undo)" />

[...]
        )

)

Then we get all the img tag attributes with a loop :

$img = array();
foreach( $result as $img_tag)
{
    preg_match_all('/(alt|title|src)=("[^"]*")/i',$img_tag, $img[$img_tag]);
}

print_r($img);

Array
(
    [<img src="/Content/Img/stackoverflow-logo-250.png" width="250" height="70" alt="logo link to homepage" />] => Array
        (
            [0] => Array
                (
                    [0] => src="/Content/Img/stackoverflow-logo-250.png"
                    [1] => alt="logo link to homepage"
                )

            [1] => Array
                (
                    [0] => src
                    [1] => alt
                )

            [2] => Array
                (
                    [0] => "/Content/Img/stackoverflow-logo-250.png"
                    [1] => "logo link to homepage"
                )

        )

    [<img class="vote-up" src="/content/img/vote-arrow-up.png" alt="vote up" title="This was helpful (click again to undo)" />] => Array
        (
            [0] => Array
                (
                    [0] => src="/content/img/vote-arrow-up.png"
                    [1] => alt="vote up"
                    [2] => title="This was helpful (click again to undo)"
                )

            [1] => Array
                (
                    [0] => src
                    [1] => alt
                    [2] => title
                )

            [2] => Array
                (
                    [0] => "/content/img/vote-arrow-up.png"
                    [1] => "vote up"
                    [2] => "This was helpful (click again to undo)"
                )

        )

    [<img class="vote-down" src="/content/img/vote-arrow-down.png" alt="vote down" title="This was not helpful (click again to undo)" />] => Array
        (
            [0] => Array
                (
                    [0] => src="/content/img/vote-arrow-down.png"
                    [1] => alt="vote down"
                    [2] => title="This was not helpful (click again to undo)"
                )

            [1] => Array
                (
                    [0] => src
                    [1] => alt
                    [2] => title
                )

            [2] => Array
                (
                    [0] => "/content/img/vote-arrow-down.png"
                    [1] => "vote down"
                    [2] => "This was not helpful (click again to undo)"
                )

        )

    [<img src="http://www.gravatar.com/avatar/df299babc56f0a79678e567e87a09c31?s=32&amp;d=identicon&amp;r=PG" height=32 width=32 alt="gravatar image" />] => Array
        (
            [0] => Array
                (
                    [0] => src="http://www.gravatar.com/avatar/df299babc56f0a79678e567e87a09c31?s=32&amp;d=identicon&amp;r=PG"
                    [1] => alt="gravatar image"
                )

            [1] => Array
                (
                    [0] => src
                    [1] => alt
                )

            [2] => Array
                (
                    [0] => "http://www.gravatar.com/avatar/df299babc56f0a79678e567e87a09c31?s=32&amp;d=identicon&amp;r=PG"
                    [1] => "gravatar image"
                )

        )

   [..]
        )

)

Regexps are CPU intensive so you may wan to cache this page. If you have no cache system, you can tweak your own by using ob_start and loading / saving from a text file.

How does this stuff work ?

First, we use preg_ match_ all, a function that gets every string matching the pattern and ouput it in its third parameter.

The regexps :

<img[^>]+>

We apply it on the all html web page. It can be read as every string that start with "<img", contains non ">" char and ends with a >.

(alt|title|src)=("[^"]*")

We apply it successively on each img tag. It can be read as every string starting with "alt", "title" or "src", then a "=", then a ' " ', a bunch of stuff that are not ' " ' and ends with a ' " '. Isolate the sub-strings between ().

Finally, everytime you want to deal with regexps, it handy to have good tools to quickly test them. Check this online regexp tester.

EDIT : answer to the first comment.

It's true that I did not think about the (hopefully few) people using single quotes.

Well, if you use only ', just replace all the " by '.

If you mix both. First you should slap yourself :-), then try to use ("|') instead or " and [^ø] to replace [^"].

e-satis
Only problem is single quotation marks:<img src='picture.jpg'/> will not work, the regex expects " all the time
Sam
Tre my friend. I added a note on about that. Thanks.
e-satis
+2  A: 

The script must be edited like this

foreach( $result[0] as $img_tag)

because preg_match_all return array of arrays

Bakudan
A: 
"]+>]+>/)?>"


this will extract anchor tag nested with image tag

Muhammad Irfan
+1  A: 

$url="http://example.com";

$html = file_get_contents($url);

$doc = new DOMDocument(); @$doc->loadHTML($html);

$tags = $doc->getElementsByTagName('img');

foreach ($tags as $tag) { echo $tag->getAttribute('src'); }

karim
Very cool code!
Alex Polo
A: 

I used preg_match to do it.

In my case, I had a string containing exactly one <img> tag (and no other markup) that I got from Wordpress and I was trying to get the src attribute so I could run it through timthumb.

// get the featured image
$image = get_the_post_thumbnail($photos[$i]->ID);

// get the src for that image
$pattern = '/src="([^"]*)"/';
preg_match($pattern, $image, $matches);
$src = $matches[1];
unset($matches);

In the pattern to grab the title or the alt, you could simply use $pattern = '/title="([^"]*)"/'; to grab the title or $pattern = '/title="([^"]*)"/'; to grab the alt. Sadly, my regex isn't good enough to grab all three (alt/title/src) with one pass though.

Jazzerus