从外部网站下载数百万张图片

时间:2015-01-16 19:00:21

标签: php

我正在开发一个房地产网站,我们即将获得约1M个列表的外部供稿。假设每个列表中有大约10张与之相关的照片,那就是大约1000万张照片,我们需要将它们下载到我们的服务器上,以免与它们“热链接”。

我完全失去了如何有效地做到这一点。我玩了一些数字,我得出结论,基于每个图像下载速率0.5秒,这可能需要超过58天才能完成(从外部服务器下载~10M图像)。这显然是不可接受的。

每张照片似乎大约为50KB,但这可能会有所不同,有些更大,更大,有些更小。

我一直在测试,只需使用:

copy(http://www.external-site.com/image1.jpg, /path/to/folder/image1.jpg)

我也尝试过cURL,wget和其他人。

我知道其他网站都是这样做的,而且规模要大得多,但我没有丝毫的线索,如果他们一次只花几个月就能管理这类事情。

基于我们设置接收的XML Feed的Sudo代码。我们正在使用PHP解析XML:

<listing>
    <listing_id>12345</listing_id>
    <listing_photos>
        <photo>http://example.com/photo1.jpg</photo>
        <photo>http://example.com/photo2.jpg</photo>
        <photo>http://example.com/photo3.jpg</photo>
        <photo>http://example.com/photo4.jpg</photo>
        <photo>http://example.com/photo5.jpg</photo>
        <photo>http://example.com/photo6.jpg</photo>
        <photo>http://example.com/photo7.jpg</photo>
        <photo>http://example.com/photo8.jpg</photo>
        <photo>http://example.com/photo9.jpg</photo>
        <photo>http://example.com/photo10.jpg</photo>
    </listing_photos>
</listing>

因此,我的脚本将遍历每张照片以获取特定列表并将照片下载到我们的服务器,并将照片名称插入我们的照片数据库(插入部分已经完成而没有问题)。

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

在您执行此操作之前

喜欢@BrokenBinar在评论中说。考虑主机每秒可以提供多少请求。你不想在没有他们知情的情况下向他们提出要求。然后使用类似sleep的内容来限制您的请求数量。

Curl Multi

无论如何,请使用Curl。有些重复的答案,但无论如何都要复制:

$nodes = array($url1, $url2, $url3);
$node_count = count($nodes);

$curl_arr = array();
$master = curl_multi_init();

for($i = 0; $i < $node_count; $i++)
{
    $url =$nodes[$i];
    $curl_arr[$i] = curl_init($url);
    curl_setopt($curl_arr[$i], CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($master, $curl_arr[$i]);
}

do {
    curl_multi_exec($master,$running);
} while($running > 0);


for($i = 0; $i < $node_count; $i++)
{
    $results[] = curl_multi_getcontent  ( $curl_arr[$i]  );
}
print_r($results);

来自:PHP Parallel curl requests

另一种解决方案:

的Pthread

<?php

class WebRequest extends Stackable {
    public $request_url;
    public $response_body;

    public function __construct($request_url) {
        $this->request_url = $request_url;
    }

    public function run(){
        $this->response_body = file_get_contents(
            $this->request_url);
    }
}

class WebWorker extends Worker {
    public function run(){}
}

$list = array(
    new WebRequest("http://google.com"),
    new WebRequest("http://www.php.net")
);

$max = 8;
$threads = array();
$start = microtime(true);

/* start some workers */
while (@$thread++<$max) {
    $threads[$thread] = new WebWorker();
    $threads[$thread]->start();
}

/* stack the jobs onto workers */
foreach ($list as $job) {
    $threads[array_rand($threads)]->stack(
        $job);
}

/* wait for completion */
foreach ($threads as $thread) {
    $thread->shutdown();
}

$time = microtime(true) - $start;

/* tell you all about it */
printf("Fetched %d responses in %.3f seconds\n", count($list), $time);
$length = 0;
foreach ($list as $listed) {
    $length += strlen($listed["response_body"]);
}
printf("Total of %d bytes\n", $length);
?>

来源:PHP testing between pthreads and curl

你应该真的使用搜索功能,你知道:)

答案 1 :(得分:2)

您可以将所有链接保存到某个数据库表中(它将是您的&#34;作业队列&#34;), 然后,您可以创建一个脚本,在循环中获取作业并执行此操作(获取单个链接的图像并将作业记录标记为已完成) 你可以多次执行的脚本f.e.使用supervisord。因此,作业队列将被并行处理。如果它变慢,你可以只执行另一个工作脚本(如果带宽不会减慢你的速度)

如果由于某种原因任何脚本挂起,您可以轻松再次运行它以仅获取尚未下载的图像。 Btw supervisord可以配置为在每个脚本失败时自动重启。

另一个优点是,您可以随时通过supervisorctl检查这些脚本的输出。要查看仍在等待的图像数量,您可以轻松查询&#34;作业队列&#34;表。

答案 2 :(得分:2)

我很惊讶供应商不允许您进行热链接。事实上,你不会每个月都为每一张图片服务,为什么要下载每张图片呢?允许您进行热链接可以更好地利用每个人的带宽。

我管理的目录包含数百万个项目,其中数据是本地的,但图像大多是热链接的。有时我们需要隐藏图像的来源,或者供应商要求我们缓存图像。为了实现这两个目标,我们使用代理。我们编写了自己的代理,但您可能会找到满足您需求的开源代码。

代理的工作方式是我们对加密的URL字符串进行加密和URL编码。所以http://yourvendor.com/img1.jpg变为xtX957z。在我们的标记中,img src标记类似于http://ourproxy.com/getImage.ashx?image=xtX957z

当我们的代理收到图像请求时,它会解密图像URL。代理首先在磁盘上查找映像。我们从URL中获取图像名称,因此它正在寻找像yourvendorcom.img1.jpg这样的东西。如果代理无法在磁盘上找到该映像,则它会使用解密的URL从供应商处获取映像。然后它将映像写入磁盘并将其提供给客户端。这种方法的优点是可以在不浪费带宽的情况下按需提供。我只得到我需要的图像,我只得到它们一次。