GO初体验之爬取情侣头像

最近有想转golang的想法,所以抽了点时间学习了一波golang的基本语法,感觉上这个静态解析语言用起来的确没有php那么舒服,但感觉上用起来还是很爽的。
刚好女票要换个情侣头像,那就用Go爬一波情侣头像吧~
Let's Code
image
本次使用的工具

golang go1.11.4

编辑器 vscode1.31.1

浏览器 Chrome72.0.3626.119

1.找到目标网站 分析一波

目标网站模样

F12看一波结构:

发现每个 li 对应跳转一个目标网站,估计是图片的集合

页面结构

翻到底部,看到有分页标记(感觉是模版生成的。。。),看一波链接,这个链接遵循只是最后资源地址不一样,其他是一样的,所以可以使用正则匹配一波,可以利用分页爬取比较大量的起始链接。

分页链接

1.分页正则:

http:\/\/www.wxcha.com\/touxiang\/qinglv\/hot_\d+.html

2.详情页正则:

http://www.wxcha.com/touxiang/\d+.html

接下来去详情页看看:

详情页

F12定位下图片元素,不难得到图片资源地址的正则:

3.图片地址正则:

http://img.wxcha.com/file/\d+/\d+/\w+.jpg

好了,目前看来这个网站的图片结构还是很明朗的,并没有用JS加载,应该是模版同步渲染的,接下来就是研究下怎么设计这个爬虫。

2.设计爬虫

页面有3层,所以可以有以下思路:

1.先用第一页,按一定深度爬取分页,拿到一定数量基础页面链接

2.利用这些基础链接,获取详情页的链接

3.循环获取图片地址,并下载

思路理顺了,接下来就是如何设计这个爬虫了。


为了熟悉Golang,所以没有用其他的爬虫框架,考虑到复用性需要写一个爬虫的方法用于一些基本的数据爬取,所以先定义一个结构体(可以简单理解为PHP的类):


type Spider struct {

Types string

Url string

Regex string

Deep int64

Client *http.Client

Req *http.Request

ResponseBody []byte

Headers map[string]string

}

Types是Request Method,像GetPost

Url 是目标链接

Regex 是正则,用于Url页面内容的解析(不知道可以用Xpath不,没研究过,Todo下)

Deep 是爬虫深度,用于获取分页内容或者其他有深度的请求页面,定义深度防止无限抓取

Client 是net/http包的自定义client方法(用它主要是能自定义header,一些网站有反爬机制,需要定义header)

Req 是http.NewRequest主要用于一些设置一些自定义请求

ResponseBody 顾名思义是请求返回的body体,因为返回内容是[]byte所以是定义为[]byte

Headers 自定义的header头,提供外部定义方法,方便爬取数据

目前想到的能通用的只有这些了,然后在需要函数具体实现这些:

1.需要一个SetHeader方法设置Headers


func (sp *Spider) setHeader(header map[string]string) {}

入参用map key-value形式比较适合~

2.需要一个SetParam方法设置一些基本sp的一些参数


func (sp *Spider) SetParam(param map[string]string) {}

比如修改Url,Deep,Regex,Types这类需要重复赋值的参数

3.需要一个getContent方法用于获取请求的内容(核心方法)


func (sp *Spider) getContent() {}

4.需要一个Find方法用于正则匹配getContent获取的内容(可以考虑多种方式匹配内容,Todo)


func (sp *Spider) Find() []string {}

5.需要一个Download方法用于保存返回内容(图片啊,网页啊之类的)


func (sp *Spider) Download(fileNamePath string, fileTypes string, downloadUrl string, fileName string) string {}

考虑到要保存的可能不止只有图片,所以就定义下入参,方便灵活下载内容

所以基本的爬虫方法已经定义好,就差内部具体实现了,具体实现就看代码吧~这里就不贴出来了,这里只记录下要点。

  • 获取到数据后,需要调用 resp.Body.Close() 关闭body

  • 使用包 io/ioutil 来获取返回内容 ioutil.ReadAll(resp.Body)

  • 因为 SetParam 方法定义了入参为 map[string]string 所以 Deep参数不能使用int,需要在 SetParam 使用 strconv.ParseInt() 来string to int

  • 正则使用的是 regexp 包,定义好 MustCompile 后 执行 regexpFindAllString 方法

  • 下载内容没有定义fileName时,fileName使用的是Unix时间戳,但如果用 go func 可能会遇到文件名称一样的问题,可以加上随机字符串

  • 写入文件调用 io/ioutilioutil.WriteFile

大多数的包可以在 go语言标准库 中找到,而且每个方法都有详细说明,如果调包参数未知可以上这个网站上查看,也可以使用 go doc命令 来查看相关命令的文档。

3.设计目标网站的爬虫

前面的已经设计了3层,爬虫类也实现了,那么接下来就实现这个qinglv爬虫。

基本的,为了防止反爬拦截,事先定义下headers:


var Url string = "http://www.wxcha.com/touxiang/qinglv/hot_1.html"

headers := map[string]string{

"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1",

"Referer": Url,

}

定义下UA和referer。

3.1 按深度获取分页下的页面链接

根据第一页,获取所有符合正则条件的页面链接,核心代码如下:


resp := sp.Find()

result := resp

if len(resp) > 0 {

//有数据才继续

i := sp.Deep

var flag int64 = 0

maxFor := len(resp)

forLen := 0

for flag < i {

flag++

for len(resp) > 0 && forLen <= maxFor {

target := resp[0]

if readySearch[target] == 0 {

readySearch[target] = 1

sp.Url = target

resp = resp[1 : len(resp)]

_resp := sp.Find()

resp = append(resp, _resp...)

result = append(result, _resp...)

} else {

resp = resp[1 : len(resp)]

}

forLen++

}

forLen = 0

}

}

获取到第一条页面的所有内容后,再for循环获取resp下所有页面链接下的分页链接,并去重,深度需要自己实现,这里比较简单暴力,根据第一次获取的结果的长度作为深度标志,循环获取的结果长度超过这个长度就进行下一深度循环,虽然会获取重复数据,但本人也没有更好的解决方法,就简单的实现下了。

去重方法:


    //去重 想下是否可以想个办法简化这个去重

var finalResultMap map[string]int

finalResultMap = make(map[string]int)

for _, v := range result {

finalResultMap[v] = 1

}

var finalResultSlice []string

for k, _ := range finalResultMap {

finalResultSlice = append(finalResultSlice, k)

}

去重方法度娘谷歌没有好的解决方案,就临时实现下了。

3.2 根据前面获取的链接slice获取详情页的链接

获取3.1的链接后,获取详情页的链接,方法与3.1雷同,只是变更了正则。

3.3 最后一步根据详情页获取图片地址并下载

在3.2的基础上,获取到图片链接后,调用 sp.Download() 下载图片


func DownloadImg(){

fmt.Printf("开始下载图片...\n")

var downLoadFile string = "/Users/lins/Desktop/spider/"

var param map[string]string

param = make(map[string]string)

param["Regex"] = `http://img.wxcha.com/file/\d+/\d+/\w+.jpg`

param["Types"] = "get"

sp := &lsp.Spider{}

headers := map[string]string{

"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1",

"Referer": Url,

}

lsp.SetHeaders(headers)

sp.SetParam(param)

var result []string

detailUrls := GetDetailUrls()

for _,v := range detailUrls {

sp.Url = v

resp := sp.Find()

result = append(result, resp...)

}

//去重 想下是否可以想个办法简化这个去重

var finalResultMap map[string]int

finalResultMap = make(map[string]int)

for _, v := range result {

finalResultMap[v] = 1

}

var finalResultSlice []string

for k, _ := range finalResultMap {

downLoadPath := sp.Download(downLoadFile, "jpg", k, "")

if downLoadPath == "" {

continue

}

fmt.Printf("result %s\n", downLoadPath)

finalResultSlice = append(finalResultSlice, downLoadPath)

}

}

4.调用DownloadImg下载

以上都写好后就在main 中调用DownloadImg 设置deep = 1, 命令行 cd 到 mian.go go run main.go 启动下载。经过十多分钟的疯狂输出终于下好了,1深度就下了5000+,刺激

下载图片
下载内容简介

至此,go初体验之爬取情侣头像就到这里结束啦,虽然有个bug在于找不到情侣头像的对应(希望别被女票打死),但是这个过程还是对Golang这个语言有了很大程度的理解,收获满满,

总结下这次爬取的Coding之旅:

  • 一定要注意类型!!!!PHP弱类型的通病让我突然"强迫症"式地用强类型就遇到好多次的类型异常;

  • 还有就是 map定义没赋值 记得用之前make一下哦~

  • go的标准包还是很nice的,要用的时候多翻翻文档哦

  • go官网的tour很棒,可以一边看文档一边coding(慕课网的golang 基础课也有)

  • go还有很多牛逼的特性,是PHP这类脚本型语言所不能及的,计划以后coding一波

本项目代码地址: https://github.com/LinsVert/Golang

以上。

溜了

本人博客 : 博客

本人GayHub : Github 欢迎Start Follow!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容

  • 父母是孩子的第一任老师。但很多家长认为教育孩子是老师的任务,自己只需要把孩子送去学校。 殊不知,孩子的很多行为都是...
    刘昊妈妈阅读 116评论 0 1
  • 小时候,我觉得我不会变,至少是对感情方面,我会很专一。 小时候,我觉得我很乖巧,耐性很好,会等待,表现在什么方面呢...
    c826ff490f8f阅读 256评论 1 1
  • 吴蕙兰阅读 488评论 0 0