目标
- 爬取彼岸图网的精美壁纸
- 爬取非会员版的缩略壁纸
- 爬取会员版的4K壁纸
网页结构解析
选取一个分类,这里选择的是美食,其url长这样:/4kmeishi/
谷歌浏览器,点击F12审查元素,有两个地方值得注意,一个是标黄的a标签与标橙的img标签,后续需要从这两个标签获取要爬取的图片的url与name。a标签的href用于4k壁纸的url拼接,img标签的src用于缩略图url的拼接,img标签的alt用于图片的命名
点进大图,其url为:/tupian/,从上图与下图的对比可知,大图的url由主站的与a标签的fref属性值/tupian/拼接而成。
要想获取4k图片需要点击上图的下载原图按钮,但是根据参考文献可知这是动态获取的,通过获取data-id的属性值再添加随机数向/e/extend/传参即可,仔细观察可发现图二中的a标签的href属性值中就包括data-id这个值,data-id应该是图片的唯一id。但是批量下载原图是需要会员的,也就是说需要传cookie,否则不能下载原图。
所以通过上述方式就能轻易获取图片的url与name,那么完整的url长什么样呢
缩略图长这样,完整url见图
4K壁纸的url:/?id=23196&t=0.1561613,因为这个url输入浏览器直接就下载了(带有cookie的话),所以没有展示图。前文提到是向/e/extend/传参,其实会跳转,但若直接在浏览器输入/e/extend/?id=23196&t=0.1561613反而会出现这种情况
代码
如何在html中提取目标属性值
在浏览器中的html代码对应位置右击,如图选择copy其xpath,然后需要对拷贝的xpath进行一些修改
所以拷贝下来的
- a标签是这样的://*[@id=“main”]/div[3]/ul/li[1]/a
- img标签是这样的: //*[@id=“main”]/div[3]/ul/li[1]/a/img
因为是要获取当前页面的所有图片,所起将其修改为
- a标签://*[@id=“main”]/div[3]/ul/li/a
- img标签: //*[@id=“main”]/div[3]/ul/li/a/img
使用工具为htmlquery,可参考golang:xpath选择器htmlquery简单用法
当然也可使用正则表达式获取,但是好久没用又搞忘了,就懒得折腾了
文件结构
点击下载原图的时候,可通过查看header来查看cookies,复制就行,我最后将其放在文件里,放成一排,每个cookie由;隔开。
文件结构如下,是获取缩略图的代码,是获取4K图片的代码。
代码内均有注释,可按需修改
package main
import (
"bufio"
"fmt"
"/antchfx/htmlquery"
"/x/net/html/charset"
"/x/text/encoding"
"/x/text/encoding/unicode"
"/x/text/transform"
"io/ioutil"
"net/http"
"os"
"strconv"
"strings"
"sync"
)
/**
* @Author: zx
* @Date: 2021.05.18
* @Version: golang1.15
*/
/**
* @Description: 存放图片的url与对应的名称
*/
type imgAttr struct {
url string
name string
}
var (
// 存放图片链接的数据管道
chanImages chan imgAttr
waitGroup sync.WaitGroup
// 用于监控协程
chanTask chan string
// xpath
xp = "//*[@id='main']/div[3]/ul/li/a/img"
// 爬取页面数量
chanNum = 26
// FilePath,存放图片的文件
filePath = "F:/crawler/bian/meishi/"
// urlMainPath,大分类的主地址
mainPath = "/4kmeishi/"
)
/**
* @Description:就是一个打印err的函数
* @param err err类型
* @param why 哪个阶段发生了错误
*/
func HandleError(err error, why string) {
if err != nil {
fmt.Println(why, err)
}
}
/**
* @Description: 爬图片链接到管道
* @param url 传的整页链接
*/
func getImgUrls(url string) {
imgs := getImgs(url)
// 遍历切片里所有链接,存入数据管道
for _, img := range imgs {
chanImages <- img
}
// 标识当前协程完成
// 每完成一个任务,写一条数据
// 用于监控协程知道已经完成了几个任务
chanTask <- url
waitGroup.Done()
}
/**
* @Description: 获取当前页图片链接
* @param url 当前页的链接
* @return urls 当前页所有图片的链接
*/
func getImgs(url string) (imgs []imgAttr) {
pageStr := GetPageStr(url)
root, _ := htmlquery.Parse(strings.NewReader(pageStr))
results := htmlquery.Find(root, xp)
fmt.Printf("共找到%d条结果\n", len(results))
for _, result := range results {
img := imgAttr{
url: "" + result.Attr[0].Val,
name: result.Attr[1].Val + ".jpg",
}
imgs = append(imgs, img)
}
return
}
/**
* @Description: 抽取根据url获取内容
* @param url 当前页的链接
* @return pageStr
*/
func GetPageStr(url string) (pageStr string) {
resp, err := http.