写于:20260204
以前还在用旧主题的时候,主题作者的博客里有一篇文章是自建 Bing 每日一图 API 的,当时我觉得挺有用的就转载到我的博客了:[转载]自建 Bing 每日一图 API
虽然我的博客现在主题已经换了,原作者的博客也已经不再维护,不过这个 API 我自己还是一直在用的,比如现在博客的背景图和友链站图片失效时的替代图都是用的这个。
本来的话是没打算动这个代码的,只是恰好前阵子发觉网站的每日一图 API 有点异常,排查后发现是 API 使用的 302 跳转方式和博客用的 Service Worker 有点冲突,于是就把原 API 的 302 跳转改为了由本站直接缓存文件并返回图片/json的方式,也兼容了 API 原本的参数功能。只是可惜,最后在我的再三斟酌下还是决定以后弃用 Service Worker 了。
于是,改完的 API 似乎又不是很需要了,反而 302 跳转的方式还更轻便?但毕竟代码都已经改完了,我也不想再换回去了,索性以后就这么用吧,顺带也把改完了的代码分享一下。_(:з)∠)_
改版 API 的说明
我不认为自己是这个代码的创作者,充其量算个修改者,所以我依然保留原作者的信息,以及标注。
作者:Otstar Lin
链接:https://blog.ixk.me
然后关于这个 API,和原版相比最主要改变是请求时不再直接 302 跳转返回 Bing 每日一图的图片地址,而是由 API 所在服务器去拉取 Bing 每日一图的 json 数据,然后将 json 和图片都缓存到本地并直接提供给 API 请求者,这样做的好处是降低对 Bing API 的依赖以及兼容性更好(比如不让跨域之类的),坏处是相比直接 302 跳转的方式会稍稍加重点服务器负担以及缓存需要占用自己服务器的一定磁盘空间。不过反正也不是什么超大型 API,消耗几乎可以忽略,问题不大!
API 调用及示例
使用方法
以下假设你的网站是 example.com,你根据本文后面的搭建教程在网站根目录下创建了一个 api 的文件夹,并将 API 文件 bing.php 放在了里面。
基础调用地址
https://example.com/api/bing.php
这是最基础的调用地址,会返回 Bing 今天的每日一图,图片分辨率是 1920×1080。
调用参数
这里和原版 API 的参数保持一致,API 支持以下调用参数:
| 参数代码 | 参数含义 | 可用参数 |
| rand | 是否随机显示最近 15 天内的图片 | true 或者不加这个参数 |
| day | 显示指定的最近图片(最大 15 ) | -1,0,1,2,3,4,5,6,7……15(0为今天,-1为昨天) |
| size | 指定获取图片大小 | 详见下方可用分辨率 |
| info | 获取图片基础信息(json格式) | true 或者不加这个参数 |
以上所有参数均非必要,默认参数为 rand=false,day=0,size=1920×1080,info=false
可用分辨率:
- 1920×1080
- 1366×768
- 1280×768
- 1024×768
- 800×600
- 800×480
- 768×1280
- 720×1280
- 640×480
- 480×800
- 400×240
- 320×240
- 240×320
- 注:中间的x为英文字母x
参数调用示例
默认调用:
不带任何参数调用
Info调用:
调用链接:https://example.com/api/bing.php?info=true
{
"title": "Emerald Bay and Fannette Island, Lake Tahoe, California, United States (© Bill Stevenson/Cavan Images)",
"url": "https://www.bing.com/th?id=OHR.FanetteIsland_EN-CA7084329765_1920x1080.jpg",
"time": "20260203",
"day": 0,
"size": "1920x1080",
"cache": {
"json": true,
"img": true
},
"api": {
"author": "Otstar",
"url": "https://www.ixk.me",
"modified_by": "hikami",
"modified_url": "https://hikami.moe"
}
}
随机调用:
调用链接:https://example.com/api/bing.php?rand=true
API 搭建教程
代码
这里假设你的域名是 example.com,你想要的调用地址是 https://example.com/api/bing.php 这种的,那么在网站的根目录下新建一个 api 文件夹,在其中再新建一个 bing.php 的文件,并放入下面的代码。
<?php
// ====================== 基础配置 ======================
$BASE_CACHE_DIR = __DIR__ . '/cache/bing/'; //基础缓存目录
$JSON_CACHE_DIR = $BASE_CACHE_DIR . 'json/'; //json缓存目录
$IMG_CACHE_DIR = $BASE_CACHE_DIR . 'img/'; //图片缓存目录
$CACHE_TTL = 86400; // 缓存24小时
foreach ([$JSON_CACHE_DIR, $IMG_CACHE_DIR] as $dir) {
if (!is_dir($dir)) mkdir($dir, 0755, true);
}
$httpCtx = stream_context_create([
'http' => [
'timeout' => 5 //拉取超时
]
]);
// ====================== 参数处理 ======================
$rand = ($_GET['rand'] ?? '') === 'true';
$day = intval($_GET['day'] ?? 0);
$day = max(-1, min(15, $day)); // 限制范围
$size = $_GET['size'] ?? '1920x1080';
if (!preg_match('/^\d+x\d+$/', $size)) {
$size = '1920x1080';
}
$info = ($_GET['info'] ?? '') === 'true';
if ($rand) {
$day = rand(-1, 7);
}
// ====================== JSON 缓存 ======================
$jsonCacheFile = $JSON_CACHE_DIR . "bing_{$day}.json";
$jsonData = null;
$jsonFromCache = false;
// 1. 尝试读取缓存
if (file_exists($jsonCacheFile) && (time() - filemtime($jsonCacheFile)) < $CACHE_TTL) {
$content = file_get_contents($jsonCacheFile);
$jsonData = json_decode($content, true);
if ($jsonData) {
$jsonFromCache = true;
} else {
@unlink($jsonCacheFile); // 缓存损坏则删除
}
}
// 2. 缓存失效或损坏,重新拉取
if (!$jsonFromCache) {
$json = @file_get_contents(
"https://www.bing.com/HPImageArchive.aspx?format=js&idx={$day}&n=1",
false,
$httpCtx
);
if (!$json) {
// 如果有旧缓存,用旧的兜底
if (file_exists($jsonCacheFile)) {
$jsonData = json_decode(file_get_contents($jsonCacheFile), true);
$jsonFromCache = true;
} else {
http_response_code(502);
exit('Failed to fetch Bing JSON');
}
} else {
$jsonData = json_decode($json, true);
if ($jsonData) {
file_put_contents($jsonCacheFile, $json, LOCK_EX);
}
}
}
// ====================== 解析 JSON ======================
if (empty($jsonData['images'][0])) {
http_response_code(500);
exit('Invalid Bing JSON');
}
$imgBase = 'https://www.bing.com' . $jsonData['images'][0]['urlbase'];
$imgUrl = "{$imgBase}_{$size}.jpg";
// ====================== 图片缓存 ======================
$imgCacheKey = "bing_{$day}_{$size}.jpg";
$imgCacheFile = $IMG_CACHE_DIR . $imgCacheKey;
if (file_exists($imgCacheFile) && (time() - filemtime($imgCacheFile)) < $CACHE_TTL) {
$imgBinary = file_get_contents($imgCacheFile);
$imgFromCache = true;
} else {
$imgBinary = @file_get_contents($imgUrl, false, $httpCtx);
if (!$imgBinary) {
http_response_code(502);
exit('Failed to fetch image');
}
file_put_contents($imgCacheFile, $imgBinary, LOCK_EX);
$imgFromCache = false;
}
// ====================== info 模式 ======================
if ($info) {
header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: public, max-age=86400');
$infoEtag = md5((file_exists($jsonCacheFile) ? filemtime($jsonCacheFile) : 'nocache') . "_{$size}");
header('ETag: "' . $infoEtag . '"');
if (
isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') === $infoEtag
) {
http_response_code(304);
exit;
}
echo json_encode([
'title' => $jsonData['images'][0]['copyright'] ?? '',
'url' => $imgUrl,
'time' => $jsonData['images'][0]['startdate'] ?? '',
'day' => $day,
'size' => $size,
'cache' => [
'json' => $jsonFromCache,
'img' => $imgFromCache ?? false
],
'api' => [
'author' => "Otstar",
'url' => "https://www.ixk.me",
'modified_by' => "hikami",
'modified_url' => "https://hikami.moe"
]
], JSON_UNESCAPED_UNICODE);
exit;
}
// ====================== 输出图片(二进制) ======================
// 浏览器缓存
if (file_exists($imgCacheFile)) {
$length = filesize($imgCacheFile);
$etag = md5(filemtime($imgCacheFile) . $length);
} else {
$length = strlen($imgBinary);
$etag = md5($imgBinary);
}
if (
isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
trim($_SERVER['HTTP_IF_NONE_MATCH'], '"') === $etag
) {
header('Cache-Control: public, max-age=86400, immutable');
header('ETag: "' . $etag . '"');
http_response_code(304);
exit;
}
header('Content-Type: image/jpeg');
header('Content-Length: ' . $length);
header('Cache-Control: public, max-age=86400, immutable');
header('ETag: "' . $etag . '"');
echo $imgBinary;
exit;
保存之后,你现在应该就能访问 https://example.com/api/bing.php 看到效果了,如果你对于具体的设置比如缓存目录有自己的想法,可以根据上面写的注释来修改。
Nginx 防盗链配置
如果你想让 API 可被他人公开使用,那么可以不管 Nginx 的配置,如果你只想要 API 能被自己的网站所用,可以在 Nginx 配置文件里加入以下内容。
location = /api/bing.php {
valid_referers none blocked server_names *.example.com;
if ($invalid_referer) { return 403; }
fastcgi_pass unix:/tmp/php-cgi.sock;
include fastcgi.conf;
try_files $fastcgi_script_name =404;
}加入这个配置之后,会限制请求 API 时带哪些 Referer 才是合法请求,不符合的就会返回 403 禁止访问。按以上配置合法的 Referer 如下:
- none:不带 Referer,比如直接访问
- blocked:Referer 被隐藏或截断
- server_names:Nginx 配置里写的自己服务器的域名
- *.example.com:按照自己的需要改为自己的域名,意思是自己域名下面的所有子域名
fastcgi_pass 根据自己 PHP 的配置来,不过一般都是这个。另外,添加完配置之后记得重载下 Nginx 生效。
nginx -s reload结束
以上就是全部内容了,如果想要自己弄 Bing 每日一图 API 的,用这个改版的代码或者原版的代码都是没问题的,两个的参数都是一样的,只是侧重点不同。
PS:本文特色图片为 Pixiv 画师 TID 的作品,原作链接:https://www.pixiv.net/artworks/43571549







