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. Vmware 虚拟机网络通讯

    VMware Workstation(中文名"威睿工作站")是一款功能强大的桌面虚拟计算机软件,提供用户可在单一的桌面上同时运行不同的操作系统,和进行开发.测试 .部署新的应用程序 ...

  2. 迈达斯midas Gen 2019 2.1 中文汉化安装教程

    midas Gen 2019 v2.1 for win是一款关于结构设计有限元分享的工具,分为建筑领域.桥梁领域.岩土领域.仿真领域四个大类.具有人性化的操作界面,且采用了优秀的的计算机显示技术,是建 ...

  3. 【Azure 应用服务】App Service 运行状况健康检查功能简介 (Health check)

    通过Azure App Service门户,启用Health Check来监视应用服务的实例,当发现其中一个实例处于不健康(unhealthy)状态时,通过重新路由(即把有问题的实例从负载均衡器中移除 ...

  4. noip模拟测试20

    考试总结:这次考试,我非常真实地感觉到了自己能力的提高,具体来说,在之前的考试中,读完题之后我只会想到暴力的思路,甚至有的题连暴力都打不出来,但是这次在考场上我已经有了自己的一些想法,有了一个深入思考 ...

  5. Spring Cloud 专题之六:bus

    书接上回: SpringCloud专题之一:Eureka Spring Cloud专题之二:OpenFeign Spring Cloud专题之三:Hystrix Spring Cloud 专题之四:Z ...

  6. 接口管理效率神器Apifox

    前言 你是一个测试,你们团队目前开发模式是前后端分离. 某一天,版本V1.0接口评审完,发布在了swagger上,前后端各自进行开发.此时你根据接口文档将新接口迁移到JMeter上,然后开始编写接口测 ...

  7. 时间-i春秋

    记一道跑脚本的题 进入页面拿到一段代码. <?php header("content-type:text/html;charset=utf-8"); '天下武功唯快不破'; ...

  8. python之(mysql数据库操作)

    前言:关心3步骤(此文章只针对python自动化根基展开描述) 什么是mysql数据库操作?  答:利用python对mysql数据库进行增, 删, 改, 查 操作 为什么要用python对mysql ...

  9. 【Lua篇】静态代码扫描分析(二)词法分析

    一.词法分析 词法分析(英语:lexical analysis)是计算机科学中将字符序列转换为单词(Token)序列的过程.进行词法分析的程序或者函数叫作词法分析器(Lexical analyzer, ...

  10. OOP面向对象程序设计原则

    OOP面向对象程序设计原则 开闭原则(Open Close Principle) 对扩展开放,对修改关闭 里氏代换原则(Liskov Substitution Principle) 继承必须确保超累所 ...