Bing每日壁纸的RESTful接口实现
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接口实现的更多相关文章
- 【开源小软件 】Bing每日壁纸 让桌面壁纸保持更新
发布一个开源小软件,Bing每日壁纸. 该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 欢迎大家下载使用,点star!有问题请留言或者提issue. 开源地 ...
- 获取Bing每日壁纸用作首屏大图
获取Bing每日壁纸用作首屏大图 Bing 搜索每天都会更换一张精美的图片作为壁纸,除了特殊时候不太好看外(比如春节那几天),没多大问题.移动端还有上每日故事,与图片现配.现在我的博客首屏图片就是Bi ...
- 上班从换一张桌面壁纸开始——开源小工具Bing每日壁纸
发布一个自用的开源小软件,Bing每日壁纸,使用c# winform开发.该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 功能特性 自动获取Bing最新图片 ...
- DzzOffice添加动态壁纸例子-Bing每日壁纸
Bing每日壁纸介绍:bing网站每天会更新一张不同的精选图片. 此压缩包内的程序,可以自动同步更新cn.bing.com网站每天更新的图片,作为dzzoffice的壁纸使用.实现自动每天更换不同的云 ...
- 【开源小软件 】Bing每日壁纸 V1.2.1
Bing每日壁纸发布V1.2版本,下载地址Release V1.2.1 该小软件可以自动获取Bing的精美图片设置为壁纸,并且支持随机切换历史壁纸,查看壁纸故事. 本次新增国际化支持,以及桌面widg ...
- 如何获取 bing 每日壁纸(超高清版)
目录 需求描述 实现方式 简单粗暴 如何下载 如何更高清 排坑指南 初级 优点 给有好奇心的孩子 进阶 接口 自动保存 网站集成 爬虫 需求描述 必应作为一个在壁纸圈做搜索引擎最优秀的站点,其每日壁纸 ...
- Bing每日壁纸API
懒人直接出图 https://www.shadow-forum.com/api/bing/bing.php API API地址: https://bing.biturl.top 调用方式: HTTP ...
- 一个爬取Bing每日壁纸的python脚本
1. 背景 Bing搜索每天的背景图片有些比较适合做桌面,但是有的提供下载有的不提供下载.每天去点击下载又不太方便,所以第一次学习了一下python爬虫怎么写,写的很简单. 2. 相关技术 2.1 P ...
- 获取Bing每日图片API接口
bing图片每日更新,对于这一点感觉挺不错的,如果能够把bing每日图片作为博客背景是不是很不错呢?首先我们进入Bing首页,会发现自动转到中国版.不过这没关系,中国版更符合国情,速度也比国际版快一些 ...
随机推荐
- tp3常量
__MODULE__ __CONTROLLER__
- PS Lite 笔记
本文讲解的 PS Lite 源码版本限定如下: GitHub: https://github.com/dmlc/ps-lite/tree/master Commit: f45e2e78a7430be0 ...
- Salesforce Integration 概览(四) Batch Data Synchronization(批量数据的同步)
本篇参考:https://resources.docs.salesforce.com/sfdc/pdf/integration_patterns_and_practices.pdf 前两篇博客讲了一下 ...
- oracle、postgres、mysql数据库的建库、创建用户、导人导出备份总结
本文包含的内容:使用命令操作oracle.postgres.mysql的导入导出,登录到数据,创建用户 注:我在公司使用的是Center OS操作系统,所以oracle和postgres均是在Linu ...
- ABP框架使用Oracle数据库,并实现从SQLServer中进行数据迁移的处理
ABP框架的数据访问底层是基于EFCore(Entity Framework Core)的,是微软标志性且成熟的ORM,因此它本身是支持多种主流数据库MySQL,SqlServer,Oracle,SQ ...
- Linux线程同步之读写锁(rwlock)
读写锁和互斥量(互斥锁)很类似,是另一种线程同步机制,但不属于POSIX标准,可以用来同步同一进程中的各个线程.当然如果一个读写锁存放在多个进程共享的某个内存区中,那么还可以用来进行进程间的同步, 和 ...
- 5、Python断言及常用断言函数总结
Python断言 Python assert 语句,又称断言语句,可以看做是功能缩小版的 if 语句,它用于判断某个表达式的值,如果值为真,则程序可以继续往下执行:反之,Python 解释器会报 As ...
- 21JavaScript笔记(1)
JavaScript 基于对象和事件驱动 简单描述性语言 函数优先 解释型(即时编译型) 具有安全性的脚本语言 1.js组成 核心语法(ECMAScript):开放的.标准的脚本语言规范,主要包含了语 ...
- 【笔记】衡量线性回归法的指标 MSE,RMS,MAE以及评价回归算法 R Square
衡量线性回归法的指标 MSE,RMS,MAE以及评价回归算法 R Square 衡量线性回归法的指标 对于分类问题来说,我们将原始数据分成了训练数据集和测试数据集两部分,我们使用训练数据集得到模型以后 ...
- MySQL数据库优化(2)
MySQL优化 大批量插入数据优化 1.将数据按照id有序排列 2.使用load关键字(100万条:有序20s,无序1分50秒) 3.插入之前,关闭唯一性校验(SET UNIQUE_CHECKS=0) ...