美文网首页
GO初体验之爬取情侣头像

GO初体验之爬取情侣头像

作者: 丶或情 | 来源:发表于2019-03-13 13:18 被阅读0次
    最近有想转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!

    相关文章

      网友评论

          本文标题:GO初体验之爬取情侣头像

          本文链接:https://www.haomeiwen.com/subject/cvvzpqtx.html