使用正则表达式从 HTML 代码中提取第一个图像源?

21

我想知道如何实现这个。

假设:有很多包含表格、div、图片等内容的html代码。

问题:如何获取所有出现的匹配项。更具体地说,如何获取img标记的源(src = ?)。

示例:

<img src="http://example.com/g.jpg" alt="" />

在这种情况下,我该如何打印http://example.com/g.jpg?我希望假设html代码中还有其他标签,可能有多个图像。是否有可能在html代码中拥有所有图像源的数组?我知道可以通过正则表达式实现这一点,但我无法掌握它。非常感谢您的任何帮助。

可能是以下问题的重复:https://dev59.com/LnVC5IYBdhLWcg3w9GA9 和 https://dev59.com/03VC5IYBdhLWcg3w9F89 等。 - Josh E
10个回答

41
虽然正则表达式在许多任务中都很好用,但我发现它通常在解析HTML DOM时效果不佳。HTML的问题在于文档结构变化多端,因此很难准确(准确意味着100%的成功率和无误报)提取标签。
我建议您使用DOM解析器,例如SimpleHTML,并按如下方式使用:
function get_first_image($html) {
    require_once('SimpleHTML.class.php')

    $post_html = str_get_html($html);

    $first_img = $post_html->find('img', 0);

    if($first_img !== null) {
        return $first_img->src;
    }

    return null;
}

一些人可能认为这是过度设计,但最终会更易于维护,并且还允许更多的可扩展性。例如,使用DOM解析器,我也可以获取alt属性。
可以设计一个正则表达式来实现相同的目标,但它将受到限制,强制要求alt属性在src之后或之前,而要克服这种限制将增加正则表达式的复杂性。
此外,请考虑以下情况。要使用正则表达式正确匹配标记并仅获取src属性(在第2组中捕获),您需要以下正则表达式:
<\s*?img\s+[^>]*?\s*src\s*=\s*(["'])((\\?+.)*?)\1[^>]*?>

如果出现以下情况,上述方法可能会失败:

  • 属性或标签名为大写字母,且未使用i修饰符。
  • src属性周围没有引号。
  • src之外的另一个属性在其值中某处使用了>字符。
  • 其他我未预见到的原因。

因此,不要使用正则表达式来解析dom文档。


编辑:如果您需要所有图片:

function get_images($html){
    require_once('SimpleHTML.class.php')

    $post_dom = str_get_dom($html);

    $img_tags = $post_dom->find('img');

    $images = array();

    foreach($img_tags as $image) {
        $images[] = $image->src;
    }

    return $images;
}

2
@Ahmad Fouad:没错,使用DOM解析器可以忽略属性顺序和大小写。 - Andrew Moore
非常好。我认为我会采用这个解决方案,因为发布正则表达式的用户发布各种公式,并假定源将以特定方式和顺序输入...如果客户以非传统方式放置源,则可能变得无用:(非常感谢! - Ahmad Fouad
@andrew:XPath执行的是完全相同的操作,这也是我对simplehtmlDOM插件抵制的一部分原因,因为它不使用XPath语法。这使得语法更简单,但功能和可移植性较差。如果您使用XPath查询,可以将该查询移植到任何其他支持XPath的语言中,例如js。对于您的示例:'//input[@type="checkbox"]','//ul[class="some_class]//li//a"',我确实同意CSS语法更好(至少在jquery中),但XPath也可以执行'//tr[td[@class="cost"] > 10]'以获取所有具有成本td且值超过10的表行。 - Anthony
@Anthony:直到你开始像 input.some_class.another_class 这样的选择器...(它是写成 <input class="some_class another_class"> 还是 <input class="another_class some_class">。最终,这都是个人偏好的问题。 - Andrew Moore
请编辑答案,删除$image->src';中不必要的单引号 - 谢谢,因为我一直在寻找这个。 - Ricki
显示剩余12条评论

12

使用这个更有效:

preg_match_all('/<img [^>]*src=["|\']([^"|\']+)/i', $html, $matches);
foreach ($matches[1] as $key=>$value) {
    echo $value."<br>";
}

示例:

$html = '
<ul>     
  <li><a target="_new" href="http://www.manfromuranus.com">Man from Uranus</a></li>       
  <li><a target="_new" href="http://www.thevichygovernment.com/">The Vichy Government</a></li>      
  <li><a target="_new" href="http://www.cambridgepoetry.org/">Cambridge Poetry</a></li>      
  <img width="190" height="197" border="0" align="right" alt="upload.jpg" title="upload.jpg" class="noborder" src="value1.jpg" />
  <li><a href="http://www.verot.net/pretty/">Electronaut Records</a></li>      
  <img width="190" height="197" border="0" align="right" alt="upload.jpg" title="upload.jpg" class="noborder" src="value2.jpg" />
  <li><a target="_new" href="http://www.catseye-crew.com">Catseye Productions</a></li>     
  <img width="190" height="197" border="0" align="right" alt="upload.jpg" title="upload.jpg" class="noborder" src="value3.jpg" />
</ul>
<img width="190" height="197" border="0" align="right" alt="upload.jpg" title="upload.jpg" class="noborder" src="res/upload.jpg" />
  <li><a target="_new" href="http://www.manfromuranus.com">Man from Uranus</a></li>       
  <li><a target="_new" href="http://www.thevichygovernment.com/">The Vichy Government</a></li>      
  <li><a target="_new" href="http://www.cambridgepoetry.org/">Cambridge Poetry</a></li>      
  <img width="190" height="197" border="0" align="right" alt="upload.jpg" title="upload.jpg" class="noborder" src="value4.jpg" />
  <li><a href="http://www.verot.net/pretty/">Electronaut Records</a></li>      
  <img src="value5.jpg" />
  <li><a target="_new" href="http://www.catseye-crew.com">Catseye Productions</a></li>     
  <img width="190" height="197" border="0" align="right" alt="upload.jpg" title="upload.jpg" class="noborder" src="value6.jpg" />
';   
preg_match_all('/<img .*src=["|\']([^"|\']+)/i', $html, $matches);
foreach ($matches[1] as $key=>$value) {
    echo $value."<br>";
} 

输出:

value1.jpg
value2.jpg
value3.jpg
res/upload.jpg
value4.jpg
value5.jpg
value6.jpg

这段代码获取了代码中最新插入的图像的src。我尝试在代码中添加了3个图像标签,但它只收集了一个图像(一个源)...是否可以稍微调整一下以显示数组中的所有图像源? - Ahmad Fouad
我可能会考虑使用正则表达式,因为我有很多客户仍在使用PHP 4。这将是一件痛苦的事情。 - Ahmad Fouad
@Ahmad Fouad:这是一个稍旧的DOM解析器,适用于PHP4 - http://php-html.sourceforge.net/ - Andrew Moore
我的代码不是返回第一个或最后一个匹配项,而是返回所有“src”值。请提供一个例子,证明我的代码并没有按照我所说的那样工作。 - inakiabt
对我来说非常有效。谢谢。 - Richard Dev
显示剩余5条评论

7
这是我的解决方案:
preg_match('@<img.+src="(.*)".*>@Uims', $html, $matches);
$src = $matches[1];

我认为你的三个点需要替换成[^>],否则你可能会匹配页面上第一个和最后一个img标签之间的所有内容。 - Kip
我相信这会错过<img src='x.bmp' />,因为使用了单引号。 - RC.
实际上,我认为中间的点应该是 [^>"]。 - Kip

5
我假设你所有的src=都有引号包裹着URL。
<img[^>]+src=\"([^\"]+)\"

这里发布的其他答案对你的代码作出了其他假设。

一些流行的 CMS 会这样写:scr='...' 或者甚至带空格写成 src = '...'`。 - Sliq

2

我认为使用正则表达式无法预测所有情况。

最好的方法是使用DOM和PHP5类DOMDocument和xpath。这是实现你想要的功能的最清洁的方式。

$dom = new DOMDocument();
$dom->loadHTML( $htmlContent );
$xml = simplexml_import_dom($dom);
$images = $xml -> xpath('//img/@src');

2

我同意Andrew Moore的观点。使用DOM会更好。HTML DOM图像集合将返回所有图像对象的引用。

假设在您的标题中有以下内容:

<script type="text/javascript">
    function getFirstImageSource()
    {
        var img = document.images[0].src;
        return img;
    }
</script>

然后在你的网页主体中,你需要:

<script type="text/javascript">
  alert(getFirstImageSource());
</script>

这将返回第一张图片的源代码。你也可以像这样循环遍历它们(在头部部分):
function getAllImageSources()
    {
        var returnString = "";
        for (var i = 0; i < document.images.length; i++)
        {
            returnString += document.images[i].src + "\n"
        }
        return returnString;
    }

(在正文中)

<script type="text/javascript">
  alert(getAllImageSources());
</script>

如果你正在使用JavaScript来实现这个功能,请记住不能通过循环遍历头部的图片集合来运行函数。换句话说,你不能像这样做:
<script type="text/javascript">
    function getFirstImageSource()
    {
        var img = document.images[0].src;
        return img;
    }
    window.onload = getFirstImageSource;  //bad function

</script>

因为这样不起作用。当执行头部时,图像尚未加载,因此您将获得空结果。

希望这可以在某种程度上帮助您。如果可能的话,我会利用DOM。您会发现您的大部分工作已经为您完成了。


2
我不确定你是否必须使用正则表达式来获取结果。如果不是,你可以尝试使用simpleXML和XPath,这将更可靠地实现你的目标:
首先,将HTML导入DOM文档对象。如果出现错误,请在此部分关闭错误,并确保之后重新打开它们:
 $dom = new DOMDocument();
 $dom -> loadHTMLFile("filename.html");

接下来,将DOM导入到一个simpleXML对象中,如下所示:
 $xml = simplexml_import_dom($dom);

现在,您可以使用一些方法将所有图像元素(及其属性)放入数组中。我更喜欢使用XPath,因为我在遍历DOM时取得了更好的运气:
 $images = $xml -> xpath('//img/@src');

这个变量现在可以被视为一个包含您的图片URL的数组:
 foreach($images as $image) {
    echo '<img src="$image" /><br />
    ';
  }
所有的图片都在,没有任何浪费。
以下是上述内容的非注释版本:

 $dom = new DOMDocument();
 $dom -> loadHTMLFile("filename.html");

 $xml = simplexml_import_dom($dom);

 $images = $xml -> xpath('//img/@src');

 foreach($images as $image) {
    echo '<img src="$image" /><br />
    ';
  }

1
你可以试试这个:
preg_match_all("/<img\s+src=\"(.+)\"/i", $html, $matches);
foreach ($matches as $key=>$value) {
    echo $key . ", " . $value . "<br>";
}

1

既然您不需要验证HTML,可以先尝试在文本上使用strip_tags()清除大部分无用信息。

然后您可以搜索类似于

"/\<img .+ \/\>/i"

反斜杠用于转义特殊字符,如<,>,/.。 .+ 表示 img 标签内必须有一个或多个任意字符。 您可以通过在表达式周围加上括号来捕获其中的一部分。例如 (.+) 可以捕获 img 标签的中间部分。

当您决定具体捕获中间的哪一部分时,可以将 (.+) 修改为更具体的内容。


0
<?php    
/* PHP Simple HTML DOM Parser @ http://simplehtmldom.sourceforge.net */

require_once('simple_html_dom.php');

$html = file_get_html('http://example.com');
$image = $html->find('img')[0]->src;

echo "<img src='{$image}'/>"; // BOOM!

使用PHP Simple HTML DOM解析器可以在几行代码中完成任务。


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