网上找了一个利用simple_html_dom分析每个网页上所有图片地址,得出最大图片的代码。
我把它结合到MYSQL QUERY里,(从数据库调出一组URL地址,逐个进行每个页面最大图片的解析,然后输出结果,考虑结果写进外部文件json.txt里)但是发现,如果URL地址过多的话,会出现 Fatal error: Out of memory  的错误。虽然可以通过调整PHP.INI的设置,提高内存。但是个人感觉,这个分析过程相当长,如果长时间占用内存,对服务器的性能相当不利。求教高手,是否有办法在在每次FOREACH之后,清除内存?因为每个URL分析完图片大小后,已经FWRITE进外部json.txt文档里,前面分析的数据完全可以清楚掉。求解决问题的代码。谢谢。
<?php
require_once 'simple_html_dom.php';
...//mysql query
while ($row = mysql_fetch_array($result))
{
$url = $row['url']; //'http://news.csdn.net/';
$biggestImage = '';
// process
$maxSize = -1;
$html = file_get_html($url);
// base url
foreach($html->find('img') as $element) {
    $pic = $element->src;
    if($pic=='')continue;// it happens on your test url
    $image=@getimagesize($pic);// get the rest images width and height
    if (($image[0] * $image[1]) > $maxSize) {   
        $maxSize = $image[0] * $image[1];  //compare images' sise
        $biggestImage = $pic;
    }
}
echo '<img src="'.$biggestImage.'" />'; //echo the biggest one, fwrite in json.txt
}
?>

解决方案 »

  1.   

    感觉内存并不是这里的几个变量占用的,倒是有可能缓存了大量的文件状态信息(也不太确定,因为clearstatcache()函数的说明信息中,受影响的函数并不包括getimagesize()函数),在foreach循环体中加上clearstatcache(true);试试看。
      

  2.   

    echo '<img src="'.$biggestImage.'" />'; //echo the biggest one, fwrite in json.txt
    unset($biggestImage,$html,$element,$image,$url);
    }
    这个代码没什么可以优化的, 也许你的内存真的不够用, 或许问题不是出在这几行代码里。
      

  3.   

     $image=@getimagesize($pic);把这个删掉, 看看有什么错误信息。
      

  4.   

    还有, 你还可以用strlen($biggestImage)之类的函数打印变量的长度, 大小
    就知道这段程序是不是真的吃内存如果本身变量就那么大, 程序上而言, 没得优化。
      

  5.   

    @T5500, @coolesting, 感谢2位。 我PHP内存设置128M。每一个URL的 strlen($biggestImage) 输出值在 65-130, 测试下来,一个foreach大概用30-40个URL,会出现内存不足的现象。现在就去测试一下 unset($biggestImage,$html,$element,$image,$url);和clearstatcache(true);不过先得调整一下set_time_limit; 
      

  6.   

    这个问题我遇到过, 你得显式调用解构函数:
    $html->__destruct();嗯,又查了它的手册: 可以用clear
    http://simplehtmldom.sourceforge.net/manual_faq.htm#memory_leak这个问题是因为php5对象都用reference,所以循环里不会立即释放占用的内存(需要等到gc运行)
      

  7.   

    @helloyou0 ~~ $html->__destruct(); 怎么用啊?E文不是很好…… Memory leak 和 $html->__destruct(); 哪个更好?另外,如果我想把这些图片URL写进数据库。30-40个URL分析大概要3-4分钟,是不是最好先缓存进外部文件,然后再一次写入?还是一边分析一边insert也没问题?谢谢。
      

  8.   

    你有
    ...
    $html = file_get_html($url);
    // base url
    foreach($html->find('img') as $element) {
    ...先检查 $html 是否占用大量内存
    $memo = memory_get_usage();
    $html = file_get_html($url);
    echo memory_get_usage() - $memo;
    如果的确是的话,可以这样做:
    $html = file_get_html($url);
    // base url
    $tmp = $html->find('img');
    unset($html);
    foreach($tmp as $element) {同样可用这种方法检查其他部位
      

  9.   


    $html->__destruct();
    或 $html->clear();放在你的while循环的结束之前, 就是那个  } 之前. 如果还有问题, 用楼上唠叨的方法慢慢调试,找到有问题的地方.它那个html是很占内存的,因为把整个页面都dom展开了,里面N个嵌套的simple_html_dom对象,
    但是只要每次循环释放掉, 就应该没有问题.
      

  10.   

    $html = file_get_html($url);  //这句非常吃内存
    foreach($html->find('img') as $element) {===========================================
    这样写代码本身就很吃内存,
    1.  每次循环都重新初始化一个类
    2.  对象放在循环语句中===========================================
    客观情况, 应该这样优化代码$html = file_get_html($url); 
    这行放在while循环上面, 如
    $html = init_html(); 
    while () {
    .....然后在这个$html对象的类中增加一个get_html的方法实现file_get_html的功能,如
    用这行 $html->get_html($url); 代替 file_get_html($url); 这样改了之后, 保证你的内存用少一半以上。
      

  11.   

    $html = file_get_html($url);
    从你写代码的风格, 估计你在这行里重新定义了很多次类。
    如果是, 用10楼的解决方法, unset()这个是治标不治本的下策
      

  12.   

    to  coolesting (骄傲青蛙) 如果他有能力改写这个类,就不会在这里提问了
    他的代码显然是套用了 simple_html_dom 的样例程序
    如果是自己写的,那些注释显然是不必要的
      

  13.   

    事实和我猜的一样, 。
    这个任务留給楼主自己学习吧function file_get_html() {    $dom = new simple_html_dom;    $args = func_get_args();    $dom->load(call_user_func_array('file_get_contents', $args), true);    return $dom;}
      

  14.   

    测试结果:
    $html = file_get_html($url);
    $html = init_html(); 
    while($row = mysql_fetch_array($qry)){
    $url = $row['url'];
    $biggestImage = '';
    // process
    $maxSize = -1;
    // base url
    $memo = memory_get_usage();
    $html->get_html($url);
    foreach($html->find('img') as $element) {
        $pic = $element->src;
        if($pic=='')continue;// it happens on your test url
        $image=@getimagesize($pic);// get the rest images width and height
        if (($image[0] * $image[1]) > $maxSize) {   
            $maxSize = $image[0] * $image[1];  //compare images' sise
            $biggestImage = $pic;
        }
    }
    echo strlen($biggestImage);
    echo '<img src="'.$biggestImage.'" />'; //echo the biggest one
    $html->clear(); 
    unset($html);
    echo memory_get_usage() - $memo;
    }echo memory_get_usage() - $memo; 每个foreach后得出的值:
    2272
    -176
    -40
    86504
    2080
    2176
    456
    2104
    40
    104
    ...为什么会有正有负?还有,一个没图片的网址,惊人的占了 86504
    @coolesting
    $html = init_html(); 怎么用,提示 Fatal error: Call to undefined function init_html();
      

  15.   

    本帖最后由 xuzuning 于 2011-06-21 17:36:46 编辑
      

  16.   

    老大,那么如何检测 unset 的效用?
      

  17.   

    去掉 unset ,echo memory_get_usage() - $memo值:
    4888
    2384
    14040776
    4131232
    3650904
    4850408
    2446184
    2249304
    2246296

    数值要大很多很多……那个unset,真的可以清除部分内存?
      

  18.   

    unset($html);
    echo memory_get_usage() - $memo;
    =================================
    1. echo 1111 - 2222;   //这是内存负号的原因
    2. 另外那个86504 也可能是前面几个变量的叠加数, 不单指你一个url得来的值
    3. 我上面说的那二个方法是叫你学习去写的, 本身没有的 (O_o)
      

  19.   

    感谢大家,小弟太菜。你们给了许多个方法,哪个最好?
    #14楼的方法最好吗?我想不占很多内存,而且速度也不要太慢。(我已经嫌我的速度太慢了,嫌getimagesize分析照片大小的效率太低……)
    用FOR代码FOREACH的话,看上去占有内存少,但运行时间越长,其实对服务器资源,也是一种消耗。
      

  20.   

    弱弱的再问一下,这个程序,不会是把每张图片都缓存进内存里吧?为什么echo memory_get_usage() - $memo会有14040776?我只需要分析图片长和宽,然后得到长*宽最大的那张图片的URL地址,不需要分析图片有多少个KB……
      

  21.   

    这很简单,网页是各中标记和内容的有机组合
    一个网页不可能只有 img 标记吧?
    其他标记他也要分析,而并不管你是否需要