0x00 存在意义

权且当作Docker打包的练习。

显然可以通过构造请求获得每天的壁纸,但是如果想要优雅地在其它地方使用这一网络资源,封装一个RESTful API将会保证整洁美观,在编写CSS等场景中会非常方便。

0x01 代码实现

首先是项目的目录结构:

│  bw.exe
│ Dockerfile
│ go.mod
│ go.sum
│ main.go
│ README.md

├─command
│ command.go

├─image
│ favicon.ico

├─roothandler
│ roothandler.go

└─xmlhandler
response.go
xmlhandler.go

接口逻辑

先向上游请求XML,通过处理获得关键信息,然后形成JSON作为接口返回格式(也包括直接重定向到图片,但是没有更多文本信息,可用于CSS引入)。

接口参数

接口设置了几个参数:

  • resolution: [1920, 1366, 3840]

    图片的分辨率,默认为1920,递增依次为HD、Full HD、UHD(4K)品质。在请求XML时未被使用,会在后续拼接到图片的链接中。

  • format: [json, image]

    返回形式,默认为json。在请求XML时未被使用,接口根据这个参数决定返回类型。

  • index: [0, +∞)

    图片序号,从0开始。在请求XML时需要使用。

  • mkt: [zh-CN, en-US, ja-JP, en-AU, en-UK, de-DE, en-NZ, en-CA]

    地区,决定了使用语言,默认为zh-CN。在请求XML时需要使用。

处理XML

先向接口上游请求数据,以XML形式返回。

xmlhandler/xmlhandler.go

package xmlhandler

import (
"fmt"
"io/ioutil"
"net/http"
"time" "github.com/beevik/etree"
) const (
BingURL = "https://cn.bing.com"
BingAPI = "https://cn.bing.com/HPImageArchive.aspx?format=xml&idx=%d&n=1&mkt=%s"
) var Resolution map[string]string
var Markets map[string]bool func init() {
Resolution = map[string]string{
"1366": "1366x768.jpg", // HD
"1920": "1920x1080.jpg", // Full HD
"3840": "UHD.jpg", // 3840×2160
}
Markets = map[string]bool{
"en-US": true,
"zh-CN": true,
"ja-JP": true,
"en-AU": true,
"en-UK": true,
"de-DE": true,
"en-NZ": true,
"en-CA": true,
}
} // Get: parse .XML from Bing API
func Get(index uint, market string, resolution string) (*Response, error) {
if _, ok := Resolution[resolution]; !ok {
return nil, fmt.Errorf("resolution %s not supported", resolution)
}
if _, ok := Markets[market]; !ok {
return nil, fmt.Errorf("market %s not supported", market)
} // new http.Client
var client = &http.Client{
Timeout: 5 * time.Second,
} // new http.Request
var request, err1 = http.NewRequest(http.MethodGet, fmt.Sprintf(BingAPI, index, market), nil)
if err1 != nil {
return nil, fmt.Errorf("http.NewRequest error: %s", err1)
}
request.Header.Add("Referer", "https://cn.bing.com")
request.Header.Add(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36 Edg/92.0.902.84",
) // request
var resp, err2 = client.Do(request)
if err2 != nil {
return nil, fmt.Errorf("client.Do error: %s", err2)
}
defer resp.Body.Close() // parse body
// nice etree!!! (github.com/beevik/etree)
var body, err3 = ioutil.ReadAll(resp.Body)
if err3 != nil {
return nil, fmt.Errorf("ioutil.ReadAll error: %s", err3)
}
var doc = etree.NewDocument()
if err4 := doc.ReadFromBytes(body); err4 != nil {
return nil, fmt.Errorf("ReadFromBytes error: %s", err4)
} // return Response
img := doc.SelectElement("images").SelectElement("image")
return &Response{
SDate: img.SelectElement("startdate").Text(),
EDate: img.SelectElement("enddate").Text(),
URL: fmt.Sprintf("%s%s_%s", BingURL, img.SelectElement("urlBase").Text(), Resolution[resolution]),
Copyright: img.SelectElement("copyright").Text(),
CopyrightLink: img.SelectElement("copyrightlink").Text(),
}, nil
}

xmlhandler/response.go

定义了Response结构体,作为程序内部首次请求的返回格式。

package xmlhandler

// definition of struct Response
type Response struct {
SDate string `json:"sdate"`
EDate string `json:"edate"`
URL string `json:"url"`
Copyright string `json:"copyright"`
CopyrightLink string `json:"copyright_link"`
}

形成JSON

roothandler/roothandler.go

package roothandler

import (
"Bing-Wallpaper-RESTful/xmlhandler"
"net/http"
"strconv" "github.com/gin-gonic/gin"
) // RootHandler: serve as default API using gin
func RootHandler(c *gin.Context) {
// set default query params
resolution := c.DefaultQuery("resolution", "1920")
format := c.DefaultQuery("format", "json")
index := c.DefaultQuery("index", "0")
market := c.DefaultQuery("mkt", "zh-CN") // check index
uIndex, err := strconv.ParseUint(index, 10, 64)
if err != nil {
// index invalid
c.String(http.StatusInternalServerError, "image index invalid")
return
} // check format
if format != "json" && format != "image" {
c.String(http.StatusInternalServerError, "format invalid, only `json` or `image` available")
return
} // fetch info from Bing using xmlhandler
response, err := xmlhandler.Get(uint(uIndex), market, resolution)
if err != nil {
c.String(http.StatusInternalServerError, err.Error())
return
} // redirect to image URL directly
if format == "image" && response.URL != "" {
c.Redirect(http.StatusTemporaryRedirect, response.URL)
return
}
// render response as JSON
c.JSON(http.StatusOK, response)
}

命令行设计

command/command.go

规定命令行参数并定义具体执行函数。

package command

import (
"Bing-Wallpaper-RESTful/roothandler"
"fmt" "github.com/gin-gonic/gin"
"github.com/spf13/cobra"
"github.com/thinkerou/favicon"
) // command-line
var Cmd = &cobra.Command{
Use: "run",
Short: "Run this API service",
Long: `Run this API service`,
Run: run,
} func run(cmd *cobra.Command, args []string) {
gin.SetMode(gin.ReleaseMode)
router := gin.Default()
router.Use(favicon.New("./image/favicon.ico"))
router.GET("/", roothandler.RootHandler)
router.Run(":9002") // default port: 9002
fmt.Println("API is running...")
}

主函数

main.go

package main

import (
"Bing-Wallpaper-RESTful/command"
"fmt"
"os" "github.com/spf13/cobra"
) var argVerbose bool
var rootCmd *cobra.Command func init() {
rootCmd = &cobra.Command{
Use: "bw",
Short: "Bing wallpaper API",
Long: "Top level command for Bing wallpaper API service",
}
rootCmd.PersistentFlags().BoolVarP(&argVerbose, "verbose", "v", false, "verbose output")
rootCmd.AddCommand(
command.Cmd,
)
} func main() {
if err1 := rootCmd.Execute(); err1 != nil {
fmt.Println(err1)
os.Exit(1)
}
}

0x02 程序试用

打包和帮助

command.go中的run函数对Web框架gin进行了设置,将作为发行版进行打包,仅对根路径/进行服务,接受GET,占用端口9002。

虽然Golang图形库比较欠缺发展,但是命令行的格式化库确实很优雅。

go build -o bw.exe

运行

bw run

同时可以看到gin的日志信息。

CSS引入

测试CSS导入,format要设置为image。运行Live Server。

0x03 Docker

FROM golang:alpine

ENV GO111MODULE=on \
CGO_ENABLED=0 \
GOOS=linux \
GOARCH=amd64 \
GOPROXY="https://goproxy.cn,direct" RUN mkdir -p /home/www/bw
WORKDIR /home/www/bw COPY . . RUN go build -o bw . WORKDIR /dist RUN cp /home/www/bw/bw . RUN mkdir image . RUN cp /home/www/bw/image/favicon.ico ./image/favicon.ico
# server port
EXPOSE 9002 # run
CMD ["/dist/bw", "run"]

由于有favicon.ico这一静态资源,所以需要注意复制。打包二进制时默认不会嵌入静态资源,需要使用第三方包(大量可选)或者1.16版本新增的embed功能进行嵌入,不然只能如上复制一遍资源。

用官方镜像打包使得体积过大了。

Bing每日壁纸的RESTful接口实现的更多相关文章

  1. 【开源小软件 】Bing每日壁纸 让桌面壁纸保持更新

    发布一个开源小软件,Bing每日壁纸. 该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 欢迎大家下载使用,点star!有问题请留言或者提issue. 开源地 ...

  2. 获取Bing每日壁纸用作首屏大图

    获取Bing每日壁纸用作首屏大图 Bing 搜索每天都会更换一张精美的图片作为壁纸,除了特殊时候不太好看外(比如春节那几天),没多大问题.移动端还有上每日故事,与图片现配.现在我的博客首屏图片就是Bi ...

  3. 上班从换一张桌面壁纸开始——开源小工具Bing每日壁纸

    发布一个自用的开源小软件,Bing每日壁纸,使用c# winform开发.该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 功能特性 自动获取Bing最新图片 ...

  4. DzzOffice添加动态壁纸例子-Bing每日壁纸

    Bing每日壁纸介绍:bing网站每天会更新一张不同的精选图片. 此压缩包内的程序,可以自动同步更新cn.bing.com网站每天更新的图片,作为dzzoffice的壁纸使用.实现自动每天更换不同的云 ...

  5. 【开源小软件 】Bing每日壁纸 V1.2.1

    Bing每日壁纸发布V1.2版本,下载地址Release V1.2.1 该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 本次新增国际化支持,以及桌面widg ...

  6. 如何获取 bing 每日壁纸(超高清版)

    目录 需求描述 实现方式 简单粗暴 如何下载 如何更高清 排坑指南 初级 优点 给有好奇心的孩子 进阶 接口 自动保存 网站集成 爬虫 需求描述 必应作为一个在壁纸圈做搜索引擎最优秀的站点,其每日壁纸 ...

  7. Bing每日壁纸API

    懒人直接出图 https://www.shadow-forum.com/api/bing/bing.php API API地址: https://bing.biturl.top 调用方式: HTTP ...

  8. 一个爬取Bing每日壁纸的python脚本

    1. 背景 Bing搜索每天的背景图片有些比较适合做桌面,但是有的提供下载有的不提供下载.每天去点击下载又不太方便,所以第一次学习了一下python爬虫怎么写,写的很简单. 2. 相关技术 2.1 P ...

  9. 获取Bing每日图片API接口

    bing图片每日更新,对于这一点感觉挺不错的,如果能够把bing每日图片作为博客背景是不是很不错呢?首先我们进入Bing首页,会发现自动转到中国版.不过这没关系,中国版更符合国情,速度也比国际版快一些 ...

随机推荐

  1. 如何使用odoo的compute方法,自动计算odoo字段

    前言 在odoo的ORM创建数据字段的过程中,我们会经常需要定义一些字段用来计算某一些字段只和或其他计算结果. 今天介绍一个很好用的方法compute计算属性,这个方法其实是属于写在odoo fiel ...

  2. js循环修改对象内层元素的值

    问题:存在一个对象,该对象的内部元素也为对象,子对象的元素也为对象,...(即多层对象构成的对象,具体如下),那么应该如何修改最内层元素的值(如 obj.a.a.a = 5)? var obj = { ...

  3. [考试总结]noip18

    发现之前咕掉了这个考试的总结. 今天就把它给补上. 这也是一个炸裂的一场 开局以为 \(T1\) 可做,然而事实证明我又错了... 莽了一个随机化上去,轻松过了所有样例... 以为稳了 然而挂掉了.. ...

  4. c语言学习篇二【基础语法】

    一.定义常量: 使用 #define 预处理器. 使用 const 关键字. #include <stdio.h> int main() { const int LENGTH = 10;/ ...

  5. HCNA Routing&Switching之交换技术基础

    什么是交换机?顾名思义,交换机就是用来数据包交换的:广泛用于终端接入:它的前身是hub(集线器),hub是一个古老的设备,它的作用也是用于终端接入,但hub有一个最大的缺点是它不能隔离冲突域:所谓冲突 ...

  6. 还不了解一下 Java 8 Predicate 函数接口

    同学你好,这里有一份你的未读代码,等你查收. 这篇文章属于 Java 8 教程(LTS)系列教程,点击阅读更多相关文章. Predicate 函数接口同之前介绍的 Function 接口一样,是一个函 ...

  7. 配置 Nvidia GPU 主机的运行环境

    在 Linux 主机上配置了很多次 Cuda/CuDNN 的运行环境,在此记录下用到的脚本命令以复用. 特别提醒,先了解清楚 GPU 卡的型号,查清与主机 Linux 内核兼容的驱动程序.Cuda 和 ...

  8. 精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件

    精进 Spring Boot 03:Spring Boot 的配置文件和配置管理,以及用三种方式读取配置文件 内容简介:本文介绍 Spring Boot 的配置文件和配置管理,以及介绍了三种读取配置文 ...

  9. firewalld防火墙基础

    目录 一.firewalld 概述 二.firewalld与iptables 的区别 三.firewalld 区域概念 四.Firewalld数据处理流程 五.Firewalld检查数据包的源地址的规 ...

  10. 数据库之 MySQL

    MySQL简单入门 数据库这个概念想必大家都听说过,我在这里也简单介绍一下. 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库.每个数据库都有一个或多个不同的 API 用于创建,访 ...