Klavor's Blog

Menu

「数据采集」每日必应壁纸数据自动采集与上传Github仓库实现教程

前言

  大学的时候有做过类似的采集程序,当时还不知道有必应每日一图API这种东西,所以当时的实现逻辑是通过正则从网页源码中爬取到相应的图片url并进行下载。而中途由于各种原因,闭站了很长一段时间,于是丢失了很多必应每日一图的精美图片。今天突然心血来潮,又根据必应每日一图的API来实现数据采集功能,相比之前写的采集程序,这一次多了一个上传Github仓库的操作,实现起来还是很有意思的。

必应每日一图API

  打开Bing官网,通过F12打开控制台,并查看network标签,可以看到页面请求了必应每日一图API这个地址。

https://cn.bing.com/HPImageArchive.aspx?format=hp&idx=0&n=1&nc=1568909543637&pid=hp&FORM=BEHPTB&ensearch=1&quiz=1&og=1&uhd=1&uhdwidth=2880&uhdheight=1620&IG=2D3A28F668884DFE833A987CBC7C8B19&IID=SERP.1050&setmkt=en-us&setlang=en-us

其中

param description
format 返回的数据格式。hp为html格式;js为json格式;其他值为xml格式。
idx 获取特定时间点的数据。如idx=1表示前一天(昨天),依此类推。经过测试最大值为7。
n 获取数据的条数。经测试,配合上idx最大可以获取到13天前的数据,即idx=7&n=7。
nc 请求的时间戳。不知道会有什么影响。
pid 未知。pid为hp时,copyrightlink返回的是相对地址。pid不为hp时,没有看到og信息。
FORM 未知。
ensearch 指定获取必应【国际版/国内版】的每日一图。当ensearch=1时,获取到的是必应国际版的每日一图数据。默认情况和其他值情况下,获取到的是必应国内版的每日一图数据。
quiz 当quiz=1时,返回必应小测验所需的相关数据。
og 水印图相关的信息。包含了title、img、desc和hash等信息。
uhd 当uhd=1时,可以自定义图片的宽高。当uhd=0时,返回的是固定宽高的图片数据。
uhdwidth 图片宽度。当uhd=1时生效。最大值为3840,超过这个值当作3840处理。
uhdheight 图片高度。当uhd=1时生效。最大值为2592,超过这个值当作2592处理。
IG 未知。
IID 未知。
setmkt 指定图片相关的区域信息。如图片名中包含的EN-CN、EN-US或者ZH-CN等。当域名为global.bing.com时才会有相应变化。值的格式:en-us、zh-cn等。
setlang 指定返回数据所使用的语言。值的格式:en-us、zh-cn等。
  各个参数之间可能会有相互影响。
  当域名为global.bing.com时:
    当ensearch=0:
      当setmkt=en-us: EN-US
      当setmkt=zh-cn: ZH-CN
    当ensearch=1:
      当setmkt=en-us: EN-US
      当setmkt=zh-cn: EN-CN
  当域名为cn.bing.com时:
    当ensearch=0:
      当setmkt=en-us: ZH-CN
      当setmkt=zh-cn: ZH-CN
    当ensearch=1:
      当setmkt=en-us: EN-CN
      当setmkt=zh-cn: EN-CN

  表格中列举的不够全面,可以根据实际开发所需尝试。

代码实现Photo Collections

  必应每日一图采集相关代码地址:https://github.com/facefruit/daily-bing-wallpaper

  可以通过github获取到最新版本的代码。

<?php
//根据测试发现必应每日一图api中会根据请求头中的cookie来判断是返回国内版还是国际版每日一图
$ensearchs = array(
    'cn' => 0, //国内版
    'en' => 1 //国际版
);
//遍历数组
foreach ($ensearchs as $ensearch_key => $ensearch_value) {
    //定义存储api数据的文件夹名称
    $bing_json_dir  = 'json/' . date('Y') . '/' . date('m');
    //获取目录路径(如"./en/json")
    $bing_json_path = $ensearch_key . '/' . $bing_json_dir;
    //判断文件是否存在,如果不存在则创建目录
    if (!file_exists($bing_json_path)) {
        mkdir($bing_json_path, 777, true);
        echo 'json文件夹不存在,已创建成功!
';
    }
    //拼接api数据的文件路径
    $file_path = $bing_json_path . '/' . date('Y-m-d') . '.json';
    echo '文件名称:' . $file_path . '
';
    $bing_json_url = '"https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&pid=hp&ensearch=' . $ensearch_value . '&quiz=1&og=1&uhd=0"';
    //请求网络获取api数据
    echo '正在下载今日bing数据...
';
    //拼接下载命令
    $download_cmd = 'wget -O ' . $file_path . ' ' . $bing_json_url;
    echo $download_cmd . '
';
    //执行文件下载
    exec($download_cmd);
    //不知道为什么用下面的方法会报异常
    ////PHP Warning:
    ////        file_get_contents("https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1"):
    ////        failed to open stream: No such file or directory in /root/photo-collections/bing-sprider.php on line 36
    ////Warning: 
    ////      file_get_contents("https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1"):
    ////        failed to open stream: No such file or directory in /root/photo-collections/bing-sprider.php on line 36
    //$request_array = array(
    //    'http' => array(
    //       'header' => 'cookie:ENSEARCH=BENVER=' . $ensearch_value,
    //        'authority' => 'www.bing.com',
    //        'method' => 'GET',
    //        'path' => '/HPImageArchive.aspx?format=js&idx=0&n=1',
    //        'scheme' => 'http'
    //    )
    //);
    //var_dump($request_array);
    //$request_context = stream_context_create($request_array);
    //$json_content = file_get_contents($bing_json_url, false, $request_context);
    //$json_content = fopen($bing_json_url, 'r', false, $request_context);
    echo '今日bing数据下载成功!
';

    //file_put_contents($file_path, $json_content);
    //从本地读取下载好的api数据
    $file_open    = fopen($file_path, 'r');
    $json_content = fread($file_open, filesize($file_path));
    //转换成json对象
    //$json_obj = json_decode($json_content, true);
    $json_obj     = json_decode($json_content, false);

    //var_dump($json_obj);
    //var_dump($json_obj->images);
    //取出images数组,由于调用api时n=1,所以数字的length=1
    $images = $json_obj->images;

    //更新Git账号信息为爬虫账号信息并同步更新仓库最新代码
    exec('git config --local user.name "Sprider"');
    exec('git config --local user.email "sprider@klavor"');
    exec('git pull devlang bing-daily-picture');

    foreach($images as $image) {
        //取出图片对象
        //$image  = $images[0];
        //var_dump($image);

        //取出变量
        $title         = $image->title;
        $caption       = $image->caption;
        $desc          = $image->desc;
        $copyrightonly = $image->copyrightonly;
        $copyright     = $image->copyright;
        $copyrightlink = $image->copyrightlink;
        $url           = $image->url;
        $urlbase       = $image->urlbase;
        $startdate     = $image->startdate;
        $fullstartdate = $image->fullstartdate;
        $enddate       = $image->enddate;
        $quiz          = $image->quiz;
        $hsh           = $image->hsh;
        $image_og      = $image->og;
        $og_title      = $image_og->title;
        $og_desc       = $image_og->desc;
        $og_img        = $image_og->img;
        $og_hash       = $image_og->hash;

        if (empty($title)) {
            $title = strstr($copyright, '(', true);
        }
        if (empty($caption)) {
            $caption = $title;
        }
        if (empty($copyrightonly)) {
            $copyrightonly = substr(strrchr($copyright, '('), 1, -1);
        }

        $file_name = substr(strrchr($urlbase, '='), 1);

        $image_count = 0;

        //修改json文件名称
        //exec('mv ' . $file_path . ' ' . $bing_json_path . '/' . $file_name . '.json');

        //$json_content = file_get_contents($bing_json_url);
        //$json_obj = json_decode($json_content, true);
        $bing_site      = 'https://cn.bing.com';
        //拼接域名获取到完整的图片地址
        $bing_image_url = $bing_site . $url;
        $sub_image_dir = $startdate;
        $sub_image_dir = substr_replace($sub_image_dir, '/', 6,0);
        $sub_image_dir = substr_replace($sub_image_dir, '/', 4,0);
        //拼接图片存储文件夹路径
        $image_dir      = $ensearch_key . '/' . $sub_image_dir;
        if (!file_exists($image_dir)) {
            mkdir($image_dir, 777, true);
        }
        //生成图片文件名
        $image_name = $file_name . '.jpg';
        //拼接图片文件路径
        $image_path = $image_dir . '/' . $image_name;
        //判断图片是否存在,不存在则下载图片
        if (!file_exists($image_path)) {
            $image_count++;
            //拼接下载图片命令
            $image_download_cmd = 'wget -O ' . $image_path . ' "' . $bing_image_url . '"';
            echo $image_download_cmd . '
';
            //执行图片下载命令
            exec($image_download_cmd);
            echo 'bing每日一图下载完成!
';
        } else {
            echo '图片已存在!
';
        }

        //水印图片下载
        $og_image_name = substr(strrchr($og_img, '='), 1);
        $og_image_path = $image_dir . '/' . $og_image_name;
        if (!file_exists($og_image_path)) {
            $image_count++;
            exec('wget -O ' . $og_image_path . ' "' . $og_img . '"');
            echo 'bing水印图片下载完成!
';
        } else {
            echo 'bing水印图片已存在!
';
        }

        if ($image_count == 0) {
            //没有下载任何图片,则将仓库重置
            $reset_cmd = 'git add --all .;';
            $reset_cmd .= 'git reset --hard HEAD';
            exec($reset_cmd);
            continue;
        }

        //拼接git提交所需要的comment信息
        $comment = 'Title: ' . $title . '
';
        $comment .= 'Caption: ' . $caption . '
';
        $comment .= 'Desc: ' . $desc . '
';
        $comment .= 'Copyrightonly: ' . $copyrightonly . '
';
        $comment .= 'Copyright: ' . $copyright . '
';
        $comment .= 'Copyrightlink: ' . $copyrightlink . '
';
        $comment .= 'Url: ' . $url . '
';
        $comment .= 'Urlbase: ' . $urlbase . '
';
        $comment .= 'Startdate: ' . $startdate . '
';
        $comment .= 'Fullstartdate: ' . $fullstartdate . '
';
        $comment .= 'Enddate: ' . $enddate . '
';
        $comment .= 'Quiz: ' . $quiz . '
';
        $comment .= 'Hsh: ' . $hsh . '
';
        $comment .= 'Og Title: ' . $og_title . '
';
        $comment .= 'Og Desc: ' . $og_desc . '
';
        $comment .= 'Og Img: ' . $og_img . '
';
        $comment .= 'Og Hash: ' . $og_hash . '
';

        //拼接git提交的shell脚本
        //$git_cmd = 'git config --local user.name "Sprider";';
        //$git_cmd .= 'git config --local user.email "sprider@klavor";';
        //$git_cmd .= 'git pull devlang bing-daily-picture;';
        //$git_cmd .= 'git add --all .;';
        //$git_cmd .= 'git commit -m "' . $comment . '";';
        //$git_cmd .= 'git push devlang bing-daily-picture;';
        //$git_cmd .= 'git config --local user.name "Klavor Lee";';
        //$git_cmd .= 'git config --local user.email "lee@klavor.com";';

        //将shell脚本输出到本地,需要注意的是需要可执行权限。否则不能执行shell脚本,从而导致提交代码失败。
        //$shell_path = 'git.sh';
        //file_put_contents($shell_path, $git_cmd);
        //chmod($shell_path, 777);
        //执行shell脚本
        //exec('./git.sh');

        //下面的方式不能够正常运行,因此替换成了shell脚本的方式
        ////报错信息:
        ////Vim:Warning:Output is not to a terminal
        //exec('git config --local user.name "Sprider"');
        //exec('git config --local user.email "sprider@klavor"');
        //exec('git pull devlang bing-daily-picture');
        exec('git add --all .');
        exec('git commit -m "'.$comment.'";');
        exec('git push devlang bing-daily-picture');
        //exec('git config --local user.name "Klavor Lee"');
        //exec('git config --local user.email "lee@klavor.com"');
    }
    //还原Git账号信息
    exec('git config --local user.name "Klavor Lee"');
    exec('git config --local user.email "lee@klavor.com"');
}

  代码写的比较LOW,不过功能是齐全的。在编写的过程中百度了很多知识点,毕竟对我来说,虽然自学过php,但是不常用,还是很陌生的。写代码的这期间,也遇到了很多问题,不过换了思路换了解决办法之后还是顺利的完成了这个功能的开发。

自动执行定时任务

  我这里使用的是宝塔提供的控制台,所以就没有自己去写定时任务的命令,直接可视化操作完成了。

添加定时任务

  手动的执行了一次任务后,发现效果还行。

执行定时任务

  执行完成后打开github仓库查看提交记录,发现新添加的图片已经成功提交。

Github提交记录

  截图的时间并不是同一个时间点,所以不要在意时间上的细节。

疑难杂症

  在开发的过程中,难免遇到一些奇奇怪怪的问题,一直没有办法解决。所以在实现过程中使用了些投机取巧的办法。

文件下载

  起初,并不知道直接将参数拼接到url中就可以。因此使用了请求头中添加“cookie:cookie:ENSEARCH=BENVER=1”的形式来请求必应国际版每日一图的数据。
  但是问题来了。代码里构造一个context对象,并通过file_get_contents()和fopen()方法下载数据的时候总是会报错。

PHP Warning: file_get_contents("https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1"): failed to open stream: No such file or directory in /root/photo-collections/bing-sprider.php on line 36
Warning:  file_get_contents("https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1"): failed to open stream: No such file or directory in /root/photo-collections/bing-sprider.php on line 36

  不是很清楚是什么原因导致这个问题,于是使用了“wget --header="cookie:cookie:ENSEARCH=BENVER=1" -O ./ 文件URL”的方式下载数据,结果还算是比较满意,可以实现国际版和国内版之间的切换。

git操作

  起初想着使用exec()函数来执行git的命令,但是当执行“git add .”时,报出了“Vim:Warning:Output is not to a terminal”的提示,到目前为止也没有找到好的解决办法。

  所以就想了一个投机取巧的方法,就是将git命令生成一个shell脚本,然后再通过exec()函数来执行这个shell脚本。事实证明,这是可以行得通的。

  不过后来直接通过exec()函数执行又没有问题了,不知道是做了什么操作导致的,不明觉厉。

写在后面

  感动,热泪盈眶。已经有很多年没有写php的代码了,这一次觉得有意思的点是通过php实现了git的自动提交。但是有一个问题,服务器开启了exec()函数的支持,不知道对安全性有没有什么影响。如果你有什么好的建议可以在下方发表评论留言。

— 于 共写了1749个字
— 文内使用到的标签:

发表评论