说明
本文示例爬取的是懒人图库>首页>免抠PNG>卡通手绘一栏的列表图片,打开列表页面和下载图片均使用协程处理,图片保存目录在./data/img/(没有时会自动新建)。
代码
安装依赖
#仅需要安装一个,jQeury的go实现,用来获取html节点信息
go get github.com/PuerkitoBio/goquery
爬虫代码
package main
import (
"bytes"
"fmt"
"github.com/PuerkitoBio/goquery"
"io"
"io/ioutil"
"net/http"
"os"
"path"
"strconv"
"strings"
"time"
)
type imgInfo struct {
url string
name string
}
var pageChan = make(chan string, 1000)
var htmlChan = make(chan string, 1000)
var imgChan = make(chan imgInfo, 1000)
var isFinish = make(chan bool)
//抓取某站特定页面的图片
func main() {
//目标地址,页码拼在最后
baseUrl := "https://www.lanrentuku.com/pngsucai/cate_katongshouhui_"
//就爬几页试试水
for i := 1; i <= 2; i++ {
pageChan <- baseUrl + strconv.Itoa(i)
}
//协程获取网页内容,放入通道
go getPage()
//协程获取网页内容中的图片链接,放入通道
go parsePage()
//协程根据通道中的图片链接,下载图片到本地
go downloadImg()
<-isFinish
fmt.Println("全部处理完成!")
}
// checkErr 处理可能的错误信息
func checkErr(err error) {
if err != nil {
panic(err)
}
}
func getPage() {
for page := range pageChan {
fmt.Println("当前正在抓取页面:" + page)
r, _ := http.Get(page)
body, _ := ioutil.ReadAll(r.Body)
htmlChan <- string(body)
r.Body.Close()
time.Sleep(time.Millisecond * 300)
}
}
func parsePage() {
for html := range htmlChan {
dom, err := goquery.NewDocumentFromReader(strings.NewReader(html))
checkErr(err)
//遍历页面的目标图片节点,获取图片url和文件名
dom.Find(".list-pic .item-img img").Each(func(i int, selection *goquery.Selection) {
imgUrl, ok := selection.Attr("src")
if ok {
imgName, ok := selection.Attr("alt")
if !ok {
imgName = path.Base(imgUrl)
} else {
imgName += path.Ext(imgUrl)
}
imgChan <- imgInfo{name: imgName, url: imgUrl}
}
})
}
}
func downloadImg() {
for {
select {
case img := <-imgChan:
//图片url: "https://d1.lanrentuku.com/upload/cover/5fac9b67d30bd.jpg"
//创建目录
err := os.MkdirAll("./data/img", os.ModePerm)
checkErr(err)
//保存图片
fmt.Println("正在保存的图片:", img.url)
f, err := os.Create("./data/img/" + img.name)
checkErr(err)
r, _ := http.Get(img.url)
body, _ := ioutil.ReadAll(r.Body)
io.Copy(f, bytes.NewReader(body))
//释放资源
r.Body.Close()
f.Close()
//取之有度
time.Sleep(time.Millisecond * 300)
case <-time.After(time.Second * 15):
//超时终止程序
isFinish <- true
}
}
}