如何使用php从html中提取img src、title和alt?

165
我想创建一个页面,其中列出了我网站上所有图像的标题和替代表示形式。 我已经编写了一个小程序来查找和加载所有HTML文件,但现在我卡在如何从这个HTML中提取src,title和alt。
<img <b>src</b>="/image/fluffybunny.jpg" <b>title</b>="Harvey the bunny" <b>alt</b>="a cute little fluffy bunny" />

我猜这应该用一些正则表达式来完成,但是由于标签的顺序可能会变化,并且我需要所有的标签,我不知道如何以优雅的方式解析它(我可以逐个字符地努力去做,但那很痛苦)。


$html = '<img border="0" src="/images/image.jpg" alt="Image" width="100" height="100" />';preg_match( '@src="([^"]+)"@' , $html, $match );$src = array_pop($match);// 将返回 /images/image.jpg echo $src;//https://paulund.co.uk/get-image-src-with-php - Karra Max
10个回答

273
$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');
}

我很好奇这个是否比preg_match运行得更快。 - 321zeno
5
我喜欢这篇文章的易读性!XPath和正则表达式也可以实现相同的功能,但18个月后再读起来就没那么容易了。 - Dylan Valade
1
虽然短小简单,但这是一种巨大的资源浪费……这意味着使用DOMDocument从标签中提取属性会产生很多(!!!)开销。 - patrick
如何限制示例最多10张图片? - vaneayoung
这应该是首选答案,直接而且不使用正则表达式。在这里你不需要编写确定性自动机解析器。 - clockw0rk
显示剩余2条评论

217

编辑:现在我更了解了

使用正则表达式来解决这类问题是一个糟糕的主意,很可能会导致代码难以维护和不可靠。最好使用一个HTML解析器。

使用正则表达式的解决方案

在这种情况下,最好将过程分为两个部分:

  • 获取所有的img标签
  • 提取它们的元数据

我假设您的文档不是严格的xHTML,因此无法使用XML解析器。例如,使用这个网页源代码:

/* 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&d=identicon&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)" />

[...]
        )

)

然后我们通过循环获取所有的img标签属性:
$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&d=identicon&r=PG" height=32 width=32 alt="gravatar image" />] => Array
        (
            [0] => Array
                (
                    [0] => src="http://www.gravatar.com/avatar/df299babc56f0a79678e567e87a09c31?s=32&d=identicon&r=PG"
                    [1] => alt="gravatar image"
                )

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

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

        )

   [..]
        )

)

正则表达式对CPU的消耗较大,因此您可能希望缓存此页面。如果您没有缓存系统,可以使用ob_start并从文本文件中加载/保存来调整自己的缓存系统。

这些东西是如何工作的?

首先,我们使用preg_match_all函数,该函数获取与模式匹配的每个字符串,并将其输出到第三个参数中。

正则表达式:

<img[^>]+>

我们将其应用于所有的HTML网页上。它可以被理解为“每个以""字符并以">"结尾的字符串”。
(alt|title|src)=("[^"]*")

我们将它连续应用在每个img标签上。它可以被解读为“以'alt'、'title'或'src'开头的每个字符串,然后是一个'=',然后是一堆不是'的'的东西,最后以'的'结束。隔离括号之间的子字符串。
最后,每当你想处理正则表达式时,拥有好的工具来快速测试它们是很方便的。检查这个在线正则表达式测试工具
编辑:回答第一个评论。
确实,我没有考虑到(希望很少的)使用单引号的人。
好吧,如果你只使用',只需将所有的"替换为'。
如果你混合使用两者。首先你应该给自己一个耳光:-),然后尝试使用(" | ')代替"和[^ø]代替[^"]。

唯一的问题是单引号:“img src ='picture.jpg'/>”不起作用,正则表达式始终期望双引号。 - Sam
Tre 我的朋友。我关于那个添加了一条注释。谢谢。 - Bite code
1
我不建议向下滚动(好吧,可以滚动来看一下):虽然代码看起来很简单,因此很容易让人们使用,但是当你只想从标记中获取属性时,DOMDocument有太多的开销... - patrick
如果您不知道要解析的HTML标签,只有一行HTML并且需要1-2个属性,则此解决方案非常好。如果您不需要解析整个文档,则加载DOMDoc会产生很多无用的内存开销。 - viion
1
这不包括 alt=fooalt='foo' - mgutt

68

这里只提供一个使用PHP的XML功能完成任务的小例子:

$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'];
}

我使用了DOMDocument::loadHTML()方法,因为这个方法可以处理HTML语法,不强制输入文档成为XHTML。严格来说,转换成SimpleXMLElement并不是必须的,只是让使用xpath和xpath结果更加简单。


1
这种方法非常直接,但是有些人在调用loadHTML方法(@$doc->loadHTML)时可能想要使用@符号,因为它可以防止警告出现。 - Alex Polo
1
在处理错误时,请先调用此函数:libxml_use_internal_errors(true);。您还可以使用libxml_get_errors()循环遍历这些XML错误。 - Matt

10

如果它是XHTML,就像你的例子一样,你只需要使用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);
?>

输出:

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"
  }
}

7

我使用 preg_match 来实现它。

在我的情况下,我有一个字符串,其中仅包含一个 <img> 标记(没有其他标记),我从 WordPress 中获取该字符串并尝试获取 src 属性,以便可以通过 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);

在提取标题或alt文本的模式中,您可以简单地使用$pattern = '/title="([^"]*)"/';来提取标题或$pattern = '/alt="([^"]*)"/'; 来提取 alt。不幸的是,我的正则表达式能力还不足以一次性提取所有三个(alt/title/src)。

2
如果img标签的属性使用单引号,它将无法工作; <img src ='image.png'> - numediaweb
你不应该回答“针对你的情况”,而是应该回答原帖作者的确切/精确问题。 - mickmackusa

6
您可以使用 simplehtmldom。 simplehtmldom 支持大多数 jQuery 选择器。以下是一个示例:
// Create DOM from URL or file
$html = file_get_html('http://www.google.com/');

// Find all images
foreach($html->find('img') as $element)
       echo $element->src . '<br>';

// Find all links
foreach($html->find('a') as $element)
       echo $element->href . '<br>'; 

5
脚本必须像这样进行编辑:

foreach($result[0] as $img_tag)

因为preg_match_all返回一个数组的数组。

2

我已经阅读了这个页面上很多评论,抱怨使用dom解析器是不必要的开销。可能比纯粹的正则表达式调用更昂贵,但OP已经声明对于img标签中属性的顺序没有控制。这个事实导致了不必要的正则表达式模式复杂性。此外,使用dom解析器提供了可读性、可维护性和dom感知能力(正则表达式不具备dom感知能力)等其他好处。

我喜欢正则表达式,并回答了许多有关正则表达式的问题,但在处理有效的HTML时,很少有理由使用正则表达式而不是解析器。

在下面的演示中,请看一下DOMDocument如何轻松、干净地处理任意顺序的img标签属性以及引号混合(以及根本不引用)。还请注意,没有针对性的属性的标记完全没有影响--提供一个空字符串作为值。

代码:(演示)

$test = <<<HTML
<img src="/image/fluffybunny.jpg" title="Harvey the bunny" alt="a cute little fluffy bunny" />
<img src='/image/pricklycactus.jpg' title='Roger the cactus' alt='a big green prickly cactus' />
<p>This is irrelevant text.</p>
<img alt="an annoying white cockatoo" title="Polly the cockatoo" src="/image/noisycockatoo.jpg">
<img title=something src=somethingelse>
HTML;

libxml_use_internal_errors(true);  // silences/forgives complaints from the parser (remove to see what is generated)
$dom = new DOMDocument();
$dom->loadHTML($test);
foreach ($dom->getElementsByTagName('img') as $i => $img) {
    echo "IMG#{$i}:\n";
    echo "\tsrc = " , $img->getAttribute('src') , "\n";
    echo "\ttitle = " , $img->getAttribute('title') , "\n";
    echo "\talt = " , $img->getAttribute('alt') , "\n";
    echo "---\n";
}

输出:

IMG#0:
    src = /image/fluffybunny.jpg
    title = Harvey the bunny
    alt = a cute little fluffy bunny
---
IMG#1:
    src = /image/pricklycactus.jpg
    title = Roger the cactus
    alt = a big green prickly cactus
---
IMG#2:
    src = /image/noisycockatoo.jpg
    title = Polly the cockatoo
    alt = an annoying white cockatoo
---
IMG#3:
    src = somethingelse
    title = something
    alt = 
---

使用这种技术在专业代码中会让您拥有一个干净的脚本,避免很多问题,也不会让同事希望您去别的地方工作。"最初的回答"。

1

以下是我从上述所有信息中拼凑出来的 PHP 函数,用于类似的目的,即动态调整图像标签的宽度和长度属性...可能有些笨拙,但似乎可靠地工作:

function ReSizeImagesInHTML($HTMLContent,$MaximumWidth,$MaximumHeight) {

// find image tags
preg_match_all('/<img[^>]+>/i',$HTMLContent, $rawimagearray,PREG_SET_ORDER); 

// put image tags in a simpler array
$imagearray = array();
for ($i = 0; $i < count($rawimagearray); $i++) {
    array_push($imagearray, $rawimagearray[$i][0]);
}

// put image attributes in another array
$imageinfo = array();
foreach($imagearray as $img_tag) {

    preg_match_all('/(src|width|height)=("[^"]*")/i',$img_tag, $imageinfo[$img_tag]);
}

// combine everything into one array
$AllImageInfo = array();
foreach($imagearray as $img_tag) {

    $ImageSource = str_replace('"', '', $imageinfo[$img_tag][2][0]);
    $OrignialWidth = str_replace('"', '', $imageinfo[$img_tag][2][1]);
    $OrignialHeight = str_replace('"', '', $imageinfo[$img_tag][2][2]);

    $NewWidth = $OrignialWidth; 
    $NewHeight = $OrignialHeight;
    $AdjustDimensions = "F";

    if($OrignialWidth > $MaximumWidth) { 
        $diff = $OrignialWidth-$MaximumHeight; 
        $percnt_reduced = (($diff/$OrignialWidth)*100); 
        $NewHeight = floor($OrignialHeight-(($percnt_reduced*$OrignialHeight)/100)); 
        $NewWidth = floor($OrignialWidth-$diff); 
        $AdjustDimensions = "T";
    }

    if($OrignialHeight > $MaximumHeight) { 
        $diff = $OrignialHeight-$MaximumWidth; 
        $percnt_reduced = (($diff/$OrignialHeight)*100); 
        $NewWidth = floor($OrignialWidth-(($percnt_reduced*$OrignialWidth)/100)); 
        $NewHeight= floor($OrignialHeight-$diff); 
        $AdjustDimensions = "T";
    } 

    $thisImageInfo = array('OriginalImageTag' => $img_tag , 'ImageSource' => $ImageSource , 'OrignialWidth' => $OrignialWidth , 'OrignialHeight' => $OrignialHeight , 'NewWidth' => $NewWidth , 'NewHeight' => $NewHeight, 'AdjustDimensions' => $AdjustDimensions);
    array_push($AllImageInfo, $thisImageInfo);
}

// build array of before and after tags
$ImageBeforeAndAfter = array();
for ($i = 0; $i < count($AllImageInfo); $i++) {

    if($AllImageInfo[$i]['AdjustDimensions'] == "T") {
        $NewImageTag = str_ireplace('width="' . $AllImageInfo[$i]['OrignialWidth'] . '"', 'width="' . $AllImageInfo[$i]['NewWidth'] . '"', $AllImageInfo[$i]['OriginalImageTag']);
        $NewImageTag = str_ireplace('height="' . $AllImageInfo[$i]['OrignialHeight'] . '"', 'height="' . $AllImageInfo[$i]['NewHeight'] . '"', $NewImageTag);

        $thisImageBeforeAndAfter = array('OriginalImageTag' => $AllImageInfo[$i]['OriginalImageTag'] , 'NewImageTag' => $NewImageTag);
        array_push($ImageBeforeAndAfter, $thisImageBeforeAndAfter);
    }
}

// execute search and replace
for ($i = 0; $i < count($ImageBeforeAndAfter); $i++) {
    $HTMLContent = str_ireplace($ImageBeforeAndAfter[$i]['OriginalImageTag'],$ImageBeforeAndAfter[$i]['NewImageTag'], $HTMLContent);
}

return $HTMLContent;

}

-1

这里是PHP的解决方案:

只需下载QueryPath,然后按照以下步骤操作:

$doc= qp($myHtmlDoc);

foreach($doc->xpath('//img') as $img) {

   $src= $img->attr('src');
   $title= $img->attr('title');
   $alt= $img->attr('alt');

}

完成了,你做完了!


2
不是的。这不是解决方案。 - nwalke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接