前言
最近遭遇了图片荒(每次写文章都要附上一些图片心里才踏实)。之前去shopify上一张一张下载了一些图片,但是很快就用完了。刚好最近和go+社区小伙伴一起学习go+,就想着用go+写个批量下载图片的工具,晚上回来找了一下资料没想到百来行代码就写好了。
最后成果:
工具说明:该工具用于从https://pixabay.com/zh/网站免费下载无版权高清图片(通过网站注册获取api key,通过填写想要下载的图片特征、图片类型即可将图片保存到指定的路径)。
该工具最初的构想
刚好早上在网上找哪里有免费无版权的图片时找到https://pixabay.com/zh/可以提供大量高清无版权图片,并有api尽心调用下载。
构想:用go+构建一个web应用,通过web图形化界面配置将图片下载到本地。
最终:用go+与html实现
先说一下https://pixabay.com/zh/这个网站提供的免费图片的调用方式,使用get调用如下请求,请求参数包括:key(网站注册之后获取的api key),q(需要下载的图片特征),image_type(图片类型)。而返回参数也如下所示。更具体的可注册上述网站之后访问https://pixabay.com/api/docs/查看。
定义返回参数数据结构
所以说,首先第一步我要做的就是用在go+中定义一个数据结构,用来表示返回参数:
如下所示:
type Hit struct {
ID int `json:"id"`
PageURL string `json:"pageURL"`
Type string `json:"type"`
Tags string `json:"tags"`
PreviewURL string `json:"previewURL"`
PreviewWidth int `json:"previewWidth"`
PreviewHeight int `json:"previewHeight"`
WebformatURL string `json:"webformatURL"`
WebformatWidth int `json:"webformatWidth"`
WebformatHeight int `json:"webformatHeight"`
LargeImageURL string `json:"largeImageURL"`
FullHDURL string `json:"fullHDURL"`
ImageURL string `json:"imageURL"`
ImageWidth int `json:"imageWidth"`
ImageHeight int `json:"imageHeight"`
ImageSize int `json:"imageSize"`
Views int `json:"views"`
Downloads int `json:"downloads"`
Likes int `json:"likes"`
Comments int `json:"comments"`
UserID int `json:"user_id"`
User string `json:"user"`
UserImageURL string `json:"userImageURL"`
}
type Result struct {
Total int `json:"total"`
TotalHits int `json:"totalHits"`
Hits []Hit `json:"hits"`
}
通过动态请求获取图片下载地址
接着就可以调用上述接口对图片进行下载啦,但是请注意,我们要做的是图形化界面配置要,所以上述说到的请求参数当然不可以在代码中写死,而是通过downloadImages.gtpl前端界面表单请求的方式来获取
func DownloadImages(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
t, _ := template.ParseFiles("downloadImages.gtpl")
log.Println(t.Execute(w, nil))
} else {
err := r.ParseForm()
if err != nil {
log.Fatal("ParseForm: ", err)
}
println("key:", r.Form["key"])
println("q:", r.Form["q"])
println("image_type:", r.Form["image_type"])
println("save_path:", r.Form["save_path"])
_, keyOk := r.Form["key"]
_, qOk := r.Form["q"]
_, imageTypeOk := r.Form["image_type"]
_, savePathOk := r.Form["save_path"]
if !keyOk || !qOk || !imageTypeOk || !savePathOk {
fmt.Fprintf(w, "请输入正确的参数")
return
}
resp, err := http.Get("https://pixabay.com/api/?key=" + r.Form["key"][0] + "&q=" + r.Form["q"][0] + "&image_type=" + r.Form["image_type"][0])
if err != nil {
panic(err)
}
var res Result
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(body, &res)
for i, hit := range res.Hits {
println(i, hit.LargeImageURL)
var iURLSplit = strings.Split(hit.LargeImageURL, "/")
imageResp, err := http.Get(hit.LargeImageURL)
defer resp.Body.Close()
CreatePathIfNotExists(r.Form["save_path"][0])
if err != nil {
panic(err)
}
out, err := os.Create(r.Form["save_path"][0] + iURLSplit[len(iURLSplit)-1])
if err != nil {
panic(err)
}
defer out.Close()
_, err = io.Copy(out, imageResp.Body)
if err != nil {
panic(err)
}
}
fmt.Fprintf(w, "图片下载完成,下载路径为:"+r.Form["save_path"][0])
}
}
保存路径
当然还有一点,我们可以动态配置我们想要下载的图片,那当然也要能够动态配置保存的路径啦,所以我们还开放了参数,用于图片保存的路径。
下述代码表示如果配置的路径不存在,则新建文件夹
func CreatePathIfNotExists(path string) {
_, err := os.Stat(path)
if os.IsNotExist(err) {
os.Mkdir(path, os.ModePerm)
}
}
构建http服务
基于上述逻辑,我们就可以启用一个http服务
http.HandleFunc("/", DownloadImages) // 设置访问的路由
err := http.ListenAndServe(":8888", nil) // 设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
完整代码
最后附上完整的gop代码和html代码:
main.gop
import (
"encoding/json"
"fmt"
"html/template"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
)
type Hit struct {
ID int `json:"id"`
PageURL string `json:"pageURL"`
Type string `json:"type"`
Tags string `json:"tags"`
PreviewURL string `json:"previewURL"`
PreviewWidth int `json:"previewWidth"`
PreviewHeight int `json:"previewHeight"`
WebformatURL string `json:"webformatURL"`
WebformatWidth int `json:"webformatWidth"`
WebformatHeight int `json:"webformatHeight"`
LargeImageURL string `json:"largeImageURL"`
FullHDURL string `json:"fullHDURL"`
ImageURL string `json:"imageURL"`
ImageWidth int `json:"imageWidth"`
ImageHeight int `json:"imageHeight"`
ImageSize int `json:"imageSize"`
Views int `json:"views"`
Downloads int `json:"downloads"`
Likes int `json:"likes"`
Comments int `json:"comments"`
UserID int `json:"user_id"`
User string `json:"user"`
UserImageURL string `json:"userImageURL"`
}
type Result struct {
Total int `json:"total"`
TotalHits int `json:"totalHits"`
Hits []Hit `json:"hits"`
}
func CreatePathIfNotExists(path string) {
_, err := os.Stat(path)
if os.IsNotExist(err) {
os.Mkdir(path, os.ModePerm)
}
}
func DownloadImages(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
t, _ := template.ParseFiles("downloadImages.gtpl")
log.Println(t.Execute(w, nil))
} else {
err := r.ParseForm()
if err != nil {
log.Fatal("ParseForm: ", err)
}
println("key:", r.Form["key"])
println("q:", r.Form["q"])
println("image_type:", r.Form["image_type"])
println("save_path:", r.Form["save_path"])
_, keyOk := r.Form["key"]
_, qOk := r.Form["q"]
_, imageTypeOk := r.Form["image_type"]
_, savePathOk := r.Form["save_path"]
if !keyOk || !qOk || !imageTypeOk || !savePathOk {
fmt.Fprintf(w, "请输入正确的参数")
return
}
resp, err := http.Get("https://pixabay.com/api/?key=" + r.Form["key"][0] + "&q=" + r.Form["q"][0] + "&image_type=" + r.Form["image_type"][0])
if err != nil {
panic(err)
}
var res Result
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
json.Unmarshal(body, &res)
for i, hit := range res.Hits {
println(i, hit.LargeImageURL)
var iURLSplit = strings.Split(hit.LargeImageURL, "/")
imageResp, err := http.Get(hit.LargeImageURL)
defer resp.Body.Close()
CreatePathIfNotExists(r.Form["save_path"][0])
if err != nil {
panic(err)
}
out, err := os.Create(r.Form["save_path"][0] + iURLSplit[len(iURLSplit)-1])
if err != nil {
panic(err)
}
defer out.Close()
_, err = io.Copy(out, imageResp.Body)
if err != nil {
panic(err)
}
}
fmt.Fprintf(w, "图片下载完成,下载路径为:"+r.Form["save_path"][0])
}
}
http.HandleFunc("/", DownloadImages) // 设置访问的路由
err := http.ListenAndServe(":8888", nil) // 设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
downloadImages.gtpl
<html>
<head>
<title>Pixabay图片下载器</title>
<meta charset="UTF-8">
<style>
.a{
width: 300px;
height: 30px;
margin-top: 20px;
}
#table{
width: 500px;
}
#des{
text-align: center;
}
#sub-title{
text-align: center;
font-size: 20px;
}
</style>
</head>
<body>
<br/>
<br/>
<br/>
<br/>
<br/>
<h1>Pixabay图片下载工具</h1>
<div id = "sub-title">
还在为找不到图片而发愁吗,本工具帮助你免费批量从Pixabay下载高清图片。
<br/>
<br/>
</div>
<HR align=center width=300 color=#987cb9 SIZE=1>
<div id = "table">
<form action="/" method="post">
<br/>
Pixabay申请的key
<br/>
<input type="text" class="a" name="key">
<br/>
<br/>
搜索图片关键词
<br/>
<input type="text" class="a" name="q">
<br/>
<br/>
图片类型
<br/>
<input type="text" class="a" name="image_type">
<br/>
<br/>
本机保存路径
<br/>
<input type="text" class="a" name="save_path">
<br/>
<br/>
<input type="submit" class="a" value="下载">
</form>
</div>
<br/>
<HR align=center width=300 color=#987cb9 SIZE=1>
<div id = "des">
</br>
</br>
用法:
</br>
</br>
要使用本工具,请先到Pixabay网站注册账号,注册地址:https://pixabay.com/zh/
</br>
注册完之后即可在页面:https://pixabay.com/api/docs/ 查看给你分配的 API key
</br>
</br>
申请到API key之后,将其填入上述"Pixabay申请的key"方框中。
</br>
</br>
"搜索图片关键词"中输入您想要找的图片关键词,关键词与关键词之间用+连接,例如:yellow+flower
</br>
</br>
"图片类型"中输入您想要找的图片的类型,可选类型为:all, photo, illustration, vector
</br>
</br>
最后输入下载的图片的本机保存路径,该工具会判断您输入的文件夹目录是否存在,如果不存在则会自动创建,例如,您可以输入windows系统中的合法地址 c:\\User\\xiaozhch5\\images\\
</div>
</body>
</html>
一步一步看成果
运行代码:
gop run .\main.gop
浏览器打开http://localhost:8888,可以看到
将从https://pixabay.com/zh/注册获取到的API key、你想要下载的图片的关键词(注意是英文类型)、图片类型(可选类型为:all, photo, illustration, vector)以及图片的保存路径填入上述表单中
点击下载即可在vscode终端看到相关下载信息:
再查看刚刚我们输入的下载路径
备注:默认情况下最多会下载20张图片,如果你输入的关键词匹配的图片小于20张,那么会下载所找到的所有图片(小于20张),上述关键词(yello+flowers)找到的为5张,因为黄色拼错了哈哈哈,本来是yellow,拼成了yello了。
打成exe文件
最后,可以把这个工程打包成一个exe项目,这样子我每次抓取图片就不需要打开vscode就可以啦。
执行:
gop build .
在当前目录下生成go+.exe文件,双击可直接运行。
然后同样在浏览器中输入http://localhost:8888可同样进入如下页面
下载体验
有兴趣的同学可以直接下载我构建好的exe文件,文件链接为:
https://github.com/xiaozhch5/gop-image-capture/releases/download/1.0-release/1.0-release.zip
或者访问我的完整代码: