【问题标题】:How to extract img src, title and alt from html using php? [duplicate]如何使用 php 从 html 中提取 img src、title 和 alt? [复制]
【发布时间】:2010-09-13 09:23:24
【问题描述】:

我想创建一个页面,其中列出了我网站上的所有图像以及标题和替代表示。

我已经写了一个小程序来查找和加载所有 HTML 文件,但现在我被困在如何从这个 HTML 中提取 srctitlealt

&lt;img <b>src</b>="/image/fluffybunny.jpg" <b>title</b>="Harvey the bunny" <b>alt</b>="a cute little fluffy bunny" /&gt;

我想这应该用一些正则表达式来完成,但由于标签的顺序可能会有所不同,而且我需要所有这些,我真的不知道如何以优雅的方式解析它(我可以做到逐个字符的硬字符,但这很痛苦)。

【问题讨论】:

标签: php html regex html-parsing html-content-extraction


【解决方案1】:

我已阅读此页面上的许多 cmets,它们抱怨使用 dom 解析器是不必要的开销。好吧,它可能比单纯的正则表达式调用更昂贵,但 OP 已经声明无法控制 img 标签中属性的顺序。这一事实导致了不必要的正则表达式模式卷积。除此之外,使用 dom 解析器还提供了可读性、可维护性和 dom 感知(regex 不是 dom 感知)的额外好处。

我喜欢正则表达式,我回答了很多正则表达式问题,但是在处理有效的 HTML 时,很少有充分的理由通过解析器进行正则表达式。

在下面的演示中,看看 DOMDocument 是如何简单和干净地以任意顺序处理 img 标记属性并混合引用(并且根本没有引用)。另请注意,没有目标属性的标签根本不会造成干扰——提供一个空字符串作为值。

代码:(Demo)

$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 = 
---

在专业代码中使用这种技术将使您的脚本更干净,需要处理的问题更少,希望您在其他地方工作的同事也更少。

【讨论】:

    【解决方案2】:

    编辑:现在我知道了

    使用正则表达式解决此类问题是a bad idea,可能会导致无法维护和不可靠的代码。最好使用HTML parser

    正则表达式的解决方案

    在这种情况下,最好将流程分成两部分:

    • 获取所有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 网页。它可以被读取为每个以“&lt;img”开头、包含非“>”字符并以 > 结尾的字符串。

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

    我们将它依次应用到每个 img 标签上。它可以被读为每个以“alt”、“title”或“src”开头的字符串,然后是“=”,然后是'"',一堆不是'"'并以a结尾的东西' " '. 隔离 () 之间的子字符串

    最后,每次您想处理正则表达式时,拥有快速测试它们的好工具会很方便。检查这个online regexp tester

    编辑:回答第一条评论。

    确实,我没有考虑过(希望是少数)使用单引号的人。

    好吧,如果你只使用',只需将所有“替换为'。

    如果您将两者混合使用。首先你应该打自己:-),然后尝试使用 ("|') 代替或 " 和 [^ø] 代替 [^"]。

    【讨论】:

    • 唯一的问题是单引号: 不起作用,正则表达式一直期望“”
    • 感谢我的朋友。我对此添加了注释。谢谢。
    • 我不建议向下滚动(好的,请滚动查看):虽然代码看起来很简单,因此很容易让人们使用,但当你只是想要 DOMDocument 时,开销太大了从标签中获取属性...
    • 此解决方案适用于以下情况:您不知道要解析的 html 的标签,您有 1 行 html 并且需要 1-2 个属性。加载 DOMDoc 会产生大量内存开销,如果您不解析整个文档,这将是无用的。
    • 这不包括alt=fooalt='foo'
    【解决方案3】:

    您可以使用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>'; 
    

    【讨论】:

      【解决方案4】:

      这是一个 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;
      
      }
      

      【讨论】:

        【解决方案5】:

        脚本必须这样编辑

        foreach( $result[0] as $img_tag)

        因为 preg_match_all 返回数组数组

        【讨论】:

          【解决方案6】:
          $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 运行得更快
          • 我喜欢这本书的易读性! xpath 和 regex 也可以,但 18 个月后阅读起来就不再那么容易了。
          • 虽然简短而简单,但它是一种巨大的资源浪费......这意味着使用 DOMDocument 从标签中提取属性是很多(!!!)开销
          • 如何限制,例如最多10张图片??
          • 除了资源,这取决于用例。有些人通过学习一个简单的答案最终编写了 100 个正则表达式。
          【解决方案7】:

          这是 PHP 中的解决方案:

          只需下载QueryPath,然后如下操作:

          $doc= qp($myHtmlDoc);
          
          foreach($doc->xpath('//img') as $img) {
          
             $src= $img->attr('src');
             $title= $img->attr('title');
             $alt= $img->attr('alt');
          
          }
          

          就是这样,你完成了!

          【讨论】:

          • 不。这不是解决方案。
          【解决方案8】:

          我使用 preg_match 来做到这一点。

          在我的例子中,我有一个字符串,其中正好包含一个从 Wordpress 获得的 &lt;img&gt; 标记(并且没有其他标记),我试图获取 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);
          

          在获取标题或替代品的模式中,您可以简单地使用$pattern = '/title="([^"]*)"/'; 来获取标题或$pattern = '/title="([^"]*)"/'; 来获取替代品。可悲的是,我的正则表达式还不够好,无法一次性抓住所有三个(alt/title/src)。

          【讨论】:

          • 如果img标签属性用单引号将不起作用; &lt;img src='image.png'&gt;
          • 您不是要回答“针对您的情况”,而是要回答 OP 的确切/精确问题。
          【解决方案9】:

          仅举一个使用 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 结果更简单。

          【讨论】:

          • 当然,这种方法非常简单,但有人可能希望在调用 loadHTML 方法 (@$doc->loadHTML) 时使用 @ 符号,因为它会阻止出现警告。
          • 提前调用此函数以优雅地处理错误:libxml_use_internal_errors( true );。您还可以使用 libxml_get_errors() 遍历这些 XML 错误
          【解决方案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"
            }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-05-31
            • 1970-01-01
            • 2017-11-16
            • 2013-02-25
            • 1970-01-01
            • 1970-01-01
            • 2018-08-10
            • 1970-01-01
            相关资源
            最近更新 更多