故事背景:网站一直使用的是Unsplash的随机图床API,由于unsplash在国内属于半墙状态,所以有很多墙内小伙伴反映页面加载很慢。博主试图找一些国内图床接口用来取代unsplash,可惜都不太符合审美(点赞unsplash图片质量),无意间发现了一个unsplash国内镜像站:https://unsplash.dogedoge.com/,但可惜的是站长只镜像了静态文件,网站没有任何API接口可供调用。万幸的是通过对比发现镜像图片id和原unsplash网站一致,也就是只需要替换图片前缀就能得到国内镜像链接,于是准备自己动手做一个中间件用来实现镜像站的随机图片接口功能。

接口需求分析

  1. 需要接收图片长宽、图片标签等参数

  2. 将参数传递给unsplash官方接口获取随机图片链接,简称原始链接

  3. 通过拼接替换url,获取国内镜像图片链接,简称最终链接

  4. 将请求重定向到最终链接,获取图片

接口实现

由于source.unsplash接口已作废,所以以下部分已失效,仅做存档使用,请知悉!!!

获取Unsplash原始链接

  • 观察unsplash官方接口:https://source.unsplash.com/random/900×700/?blog,night,sea
    其中900x700分别代表图片宽高,blog,night,sea代表图片标签,也就是随机哪些类型的图片

  • 当我们发送请求时,接口会返回一个携带302响应,把我们重定向到真实的图片链接,Headers中的location就是我们要获取的图片链接了

  • 通过代码简单实现如下:

    import requests
    
    url = "https://source.unsplash.com/random/900×700/?blog"
    
    # 这里要取消重定向,否则response会跳转到图片链接
    response = requests.get(url,allow_redirects=False)
    
    print(response.headers.get("Location"))
  • 返回示例:https://images.unsplash.com/photo-1499750310107-5fef28a66643?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8YmxvZ3x8fHx8fDE2OTgxNDE1Mjg&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080

拼接国内镜像最终链接

  • 这一步就比较简单了,直接替换原始链接中的https://images.unsplash.com/部分即可

  • 代码如下:

    import requests
    from urllib.parse import urljoin
    
    url = "https://source.unsplash.com/random/900×700/?blog"
    
    try:
        response = requests.get(url, allow_redirects=False)
        location = response.headers.get("Location")
        if location:
            final_url = urljoin("https://dogefs.s3.ladydaily.com/~/source/unsplash/", location.replace("https://images.unsplash.com/", ""))
            print(final_url)
        else:
            print("Location header not found in the response.")
    except requests.RequestException as e:
        print(f"Request failed: {e}")
  • 返回示例:https://dogefs.s3.ladydaily.com/~/source/unsplash/photo-1531346878377-a5be20888e57?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxfDB8MXxyYW5kb218MHx8YmxvZ3x8fHx8fDE2OTgxNDE0ODA&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=1080

  • 打开示例链接,图片可以正常访问,且国内访问速度正常。

web框架代码(已作废)

  • 上面我们使用脚本实现了最终链接的获取,但我们最终是要通过API接口供网站调用,所以需要将脚本搬到web框架中,由于博主的服务器内存告急,因此这里采用了羽量级的web.py框架,另外还有flask、django等框架也可实现

  • 最终代码如下:

    import web
    import requests
    from urllib.parse import urljoin
    
    urls = (
        '/get_image', 'get_image'
    )
    
    app = web.application(urls, globals())
    
    
    class get_image:
        def GET(self):
            # 接收宽高、tag参数
            width = web.input().get('width', '100')
            height = web.input().get('height', '100')
            tags = web.input().get('tags', '') 
    
            # 构建带参数的原始链接
            url = f"https://source.unsplash.com/random/{width}x{height}/?{tags}" 
    
            try:
                response = requests.get(url, allow_redirects=False)
                location = response.headers.get("Location")
                if location:
                    final_url = urljoin("https://dogefs.s3.ladydaily.com/~/source/unsplash/",
                                        location.replace("https://images.unsplash.com/", ""))
                    raise web.seeother(final_url)  # 使用web.seeother进行重定向
                else:
                    return "Location header not found in the response."
            except requests.RequestException as e:
                return f"Request failed: {e}"
    
    
    if __name__ == "__main__":
        app.run()

web服务代码

修改思路:通过unsplash开发者平台调用随机接口,需要先注册开发者,创建APP后拿到自己的Access_Key,代码变更如下:

import web
import requests
from urllib.parse import urljoin

urls = (
    '/get_image', 'get_image'
)

app = web.application(urls, globals())

class get_image:
    def GET(self):
        width = web.input().get('width', '100')
        height = web.input().get('height', '100')
        tags = web.input().get('tags', '')  # 获取标签参数
        client_id = "替换为自己的Access_Key"
        url = "https://api.unsplash.com/photos/random"

        params = {
            'client_id': client_id,
            'query': tags,
            'w': width,
            'h': height
        }

        try:
            response = requests.get(url, params=params)
            response.raise_for_status()  # 检查请求是否成功
            data = response.json()
            image_url = data['urls']['regular']
            # 将最终图片地址替换为https://dogefs.s3.ladydaily.com/
            modified_url = urljoin("https://dogefs.s3.ladydaily.com/~/source/unsplash/", image_url.split("/")[-1])
            raise web.seeother(modified_url)  # 使用web.seeother进行重定向
        except requests.RequestException as e:
            return f"Request failed: {e}"
        except KeyError:
            return "Error: Unable to fetch the image URL from the response."

if __name__ == "__main__":
    app.run()

接口服务部署

  • 在服务器中安装依赖

    pip install web.py
    pip install requests
  • 启动接口服务

    nohup python3 app.py 8888 >/dev/null 2>&1 &
  • nginx反向代理

        location /unsplash {
          proxy_pass http://localhost:8888/get_image;
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
        }

最终效果

  • 请求格式

    https://blog.newmorning.work/unsplash?width=900&height=700&tags=blog,night,blue

  • 返回

    https://dogefs.s3.ladydaily.com/~/source/unsplash/photo-1620590532278-c9225fc3eac6?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=700&ixid=MnwxfDB8MXxyYW5kb218MHx8YmxvZyxuaWdodCxibHVlfHx8fHx8MTY5ODE0MjYwNw&ixlib=rb-4.0.3&q=80&utm_campaign=api-credit&utm_medium=referral&utm_source=unsplash_source&w=900

参考文档