关于后端:分享一波GO的爬虫

88次阅读

共计 6457 个字符,预计需要花费 17 分钟才能阅读完成。

[TOC]

分享一波 GO 的爬虫

咱们一起来回顾一下上一次咱们说到的 应用 GOLANG 发送邮件

Golang+chromedp+goquery 简略爬取动态数据 |Go 主题月

  • 分享了邮件,电子邮件是什么
  • 邮件协定有哪些
  • 如何应用 GOLANG 发送电子邮件
  • 发送电子邮件如何携带纯文本,HTML内容,附件等
  • 发送邮件,如何抄送,如何密送
  • 如何进步发送邮件的性能

想看看 如何应用 GOLANG 发送邮件 的,欢送查看文章如何应用 GOLANG 发送邮件

还记得之前咱们简略分享了一篇 golang 爬取网页动态数据的文章 Golang+chromedp+goquery 简略爬取动态数据 |Go 主题月

要是有敌人感兴趣的话,咱们能够具体的钻研一下这个 chromedp 框架 的应用形式

明天咱们来分享一下应用 GO 来爬取网页的静态数据

啥是动态网页和动静网页呢

什么是动态网页数据呢?

  • 指的是网页中没有程序代码,只有HTML,也就是只有超文本标记语言,后缀名个别是.html , htm , xml 等等
  • 动态网页还有一个特点就是,用户能够间接点击关上,不论任何人任何工夫关上的页面的内容都是不变的,html代码固定,成果就固定了

那么顺便说一下什么是动静网页吧

  • 动静网页是一种网页编程技术

    动静网页的网页文件中除了 HTML 标记以外,还包含一些特定性能的程序代码

    这些代码次要是用来浏览器和服务器能够交互的,服务器端能够依据客户端的不同申请动静的生成网页内容,很灵便了

也就是说,动静网页的页面代码尽管没有变,可是显示的内容是能够随着工夫的流逝、不同的环境,数据库的变动而变动的

GO 来爬取网页的静态数据

咱们爬取动态网页的数据,例如咱们爬取这个网站上的静态数据,爬取网页上的账号和明码信息

http://www.ucbug.com/jiaocheng/63149.html?_t=1582307696

咱们爬取这个网站的步骤:

  • 指定一个明确须要爬取的网站
  • 通过 HTTP GET的形式拿到数据
  • 将字节数组转换成字符串
  • 应用正则表达式匹配出咱们冀望的内容(这里很重要,其实爬取动态网页,解决数据和筛选数据消耗的工夫比拟多
  • 筛选数据,去重等操作(这一步依据集体需要,因人而异,因指标网站而异)

咱们来写一个 DEMO,爬上上述网址的 账号和明码信息,网页上咱们的要的信息是这样的,咱们仅仅是供学习应用,切莫用来做一些不好的事件

package main

import (
   "io/ioutil"
   "log"
   "net/http"
   "regexp"
)

const (
   // 正则表达式,匹配出 XL 的账号密码
   reAccount = `(账号 | 迅雷账号)(;|:)[0-9:]+(|)明码:[0-9a-zA-Z]+`
)

// 获取网站 账号密码
func GetAccountAndPwd(url string) {
   // 获取网站数据
   resp, err := http.Get(url)
   if err !=nil{log.Fatal("http.Get error :",err)
   }
   defer resp.Body.Close()

   // 去读数据内容为 bytes
   dataBytes, err := ioutil.ReadAll(resp.Body)
   if err !=nil{log.Fatal("ioutil.ReadAll error :",err)
   }

   // 字节数组 转换成 字符串
   str := string(dataBytes)

   // 过滤 XL 账号和明码
   re := regexp.MustCompile(reAccount)

   // 匹配多少次,-1 默认是全副
   results := re.FindAllStringSubmatch(str, -1)

   // 输入后果
   for _, result := range results {log.Println(result[0])
   }
}

func main() {
   // 简略设置 log 参数
   log.SetFlags(log.Lshortfile | log.LstdFlags)
   // 传入网站地址,爬取开始爬取数据
   GetAccountAndPwd("http://www.ucbug.com/jiaocheng/63149.html?_t=1582307696")
}

运行上述代码的后果如下:

2021/06/xx xx:05:25 main.go:46: 账号:357451317 明码:110120a
2021/06/xx xx:05:25 main.go:46: 账号:907812219 明码:810303
2021/06/xx xx:05:25 main.go:46: 账号:797169897 明码:zxcvbnm132
2021/06/xx xx:05:25 main.go:46: 迅雷账号:792253782:1 明码:283999
2021/06/xx xx:05:25 main.go:46: 迅雷账号:147643189:2 明码:344867
2021/06/xx xx:05:25 main.go:46: 迅雷账号:147643189:1 明码:267297

能够看出,账号:结尾的数据 和 迅雷账号:结尾的数据都被咱们爬取下来,其实爬取动态网页的内容不难,工夫基本上是花在正则表达式匹配和数据处理下面

根据上述爬取网页的步骤,咱们能够列一下:

  • 拜访网站 http.Get(url)
  • 读数据内容 ioutil.ReadAll
  • 数据转换成字符串
  • 设置正则匹配的规定 regexp.MustCompile(reAccount)
  • 开始过滤数据,能够设置过滤的数量 re.FindAllStringSubmatch(str, -1)

当然理论工作中,必定不会那么简略,

例如本人爬取的数据在网站上 格局不够对立 ,特殊字符比拟多且比拟杂 没有法则 ,甚至 数据是动静的 ,没有方法通过Get 的形式拿到

不过上述的问题都是能够解决的,依据不同的问题,设计出不同的计划和数据的解决,置信这一点遇到的敌人肯定可能解决掉,面对问题,咱们要有解决问题的信心

爬取图片

看了下面的例子,咱们再来试试爬取网页上的图片数据吧,例如在某度上搜寻 柴犬

是这样的一个页面

咱们把 url 地址栏的 url 复制粘贴过去,用来爬取数据

https://image.baidu.com/search/index?tn=baiduimage&ps=1&ct=201326592&lm=-1&cl=2&nc=1&ie=utf-8&word=%E6%9F%B4%E7%8A%AC

因为图片比拟多,咱们设置只匹配 2 张图片的数据

一起来看看 DEMO

  • 顺便将Get url 数据,并转换成 字符串的性能,提取进去,封装成一个小函数
  • GetPic,专门应用正则表达式进行匹配,能够设置匹配的次数,咱们这里就设置 匹配 2
package main

import (
   "io/ioutil"
   "log"
   "net/http"
   "regexp"
)

const (
   // 正则表达式,匹配出 XL 的账号密码
   reAccount = `(账号 | 迅雷账号)(;|:)[0-9:]+(|)明码:[0-9a-zA-Z]+`
   // 正则表达式,匹配出 图片
   rePic = `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
)

func getStr(url string)string{resp, err := http.Get(url)
   if err !=nil{log.Fatal("http.Get error :",err)
   }
   defer resp.Body.Close()

   // 去读数据内容为 bytes
   dataBytes, err := ioutil.ReadAll(resp.Body)
   if err !=nil{log.Fatal("ioutil.ReadAll error :",err)
   }

   // 字节数组 转换成 字符串
   str := string(dataBytes)
   return str
}

// 获取网站 账号密码
func GetAccountAndPwd(url string,n int) {str := getStr(url)
   // 过滤 XL 账号和明码
   re := regexp.MustCompile(reAccount)

   // 匹配多少次,-1 默认是全副
   results := re.FindAllStringSubmatch(str, n)

   // 输入后果
   for _, result := range results {log.Println(result[0])
   }
}

// 获取网站 账号密码
func GetPic(url string,n int) {str := getStr(url)

   // 过滤 图片
   re := regexp.MustCompile(rePic)

   // 匹配多少次,-1 默认是全副
   results := re.FindAllStringSubmatch(str, n)

   // 输入后果
   for _, result := range results {log.Println(result[0])
   }
}


func main() {
   // 简略设置 l og 参数
   log.SetFlags(log.Lshortfile | log.LstdFlags)
   //GetAccountAndPwd("http://www.ucbug.com/jiaocheng/63149.html?_t=1582307696", -1)
   GetPic("https://image.baidu.com/search/index?tn=baiduimage&ps=1&ct=201326592&lm=-1&cl=2&nc=1&ie=utf-8&word=%E6%9F%B4%E7%8A%AC",2)
}

运行上述代码,后果如下(没有做去重):

2021/06/xx xx:06:39 main.go:63: https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=4246005838,1103140037&fm=26&gp=0.jpg
2021/06/xx xx:06:39 main.go:63: https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=4246005838,1103140037&fm=26&gp=0.jpg

果然是咱们想要的货色,可是光是打印进去,爬取的都是图片链接,必定不能满足咱们实在爬虫的需要,必定还是要将爬取到图片下载下来,供咱们所用,才是咱们想要的

咱们这里为了演示不便,咱们就在上述代码下面,加上下载文件的 小性能,咱们就下载第一张

package main

import (
   "fmt"
   "io/ioutil"
   "log"
   "net/http"
   "regexp"
   "strings"
   "time"
)

const (
   // 正则表达式,匹配出 图片
   rePic = `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)|(bmp)))`
)

// 获取网页数据,且把数据转成 字符串
func getStr(url string) string {resp, err := http.Get(url)
   if err != nil {log.Fatal("http.Get error :", err)
   }
   defer resp.Body.Close()

   // 去读数据内容为 bytes
   dataBytes, err := ioutil.ReadAll(resp.Body)
   if err != nil {log.Fatal("ioutil.ReadAll error :", err)
   }

   // 字节数组 转换成 字符串
   str := string(dataBytes)
   return str
}

// 获取图片数据
func GetPic(url string, n int) {str := getStr(url)

   // 过滤 图片
   re := regexp.MustCompile(rePic)

   // 匹配多少次,-1 默认是全副
   results := re.FindAllStringSubmatch(str, n)

   // 输入后果
   for _, result := range results {
      // 获取具体的图片名字
      fileName := GetFilename(result[0])
     // 下载图片
      DownloadPic(result[0], fileName)
   }
}

// 获取到 文件的 名字
func GetFilename(url string) (filename string) {
   // 找到最初一个 = 的索引
   lastIndex := strings.LastIndex(url, "=")
   // 获取 / 后的字符串,这就是源文件名
   filename = url[lastIndex+1:]

   // 把工夫戳 加 在原来名字前,拼一个新的名字
   prefix := fmt.Sprintf("%d",time.Now().Unix())
   filename = prefix + "_" + filename

   return filename
}

func DownloadPic(url string, filename string) {resp, err := http.Get(url)
   if err != nil {log.Fatal("http.Get error :", err)
   }
   defer resp.Body.Close()

   bytes, err := ioutil.ReadAll(resp.Body)
   if err != nil {log.Fatal("ioutil.ReadAll error :", err)
   }

   // 文件寄存的门路
   filename = "./" + filename

   // 写文件 并设置文件权限
   err = ioutil.WriteFile(filename, bytes, 0666)
   if err != nil {log.Fatal("wirte failed !!", err)
   } else {log.Println("ioutil.WriteFile successfully , filename =", filename)
   }
}

func main() {
   // 简略设置 l og 参数
   log.SetFlags(log.Lshortfile | log.LstdFlags)
   GetPic("https://image.baidu.com/search/index?tn=baiduimage&ps=1&ct=201326592&lm=-1&cl=2&nc=1&ie=utf-8&word=%E6%9F%B4%E7%8A%AC", 1)
}

上述代码,咱们加了 2 个函数,辅助咱们进行图片名的批改 和 图片的下载

  • 获取到文件的名字,并应用工夫戳重命名,GetFilename
  • 下载具体的图片,下载到当前目录下,DownloadPic

运行上述代码,能够看到如下成果

2021/06/xx xx:50:04 main.go:91: ioutil.WriteFile successfully , filename =  ./1624377003_0.jpg

且当前目录下,曾经下载胜利了一张图片,名字为 1624377003_0.jpg

如下是具体图片的形象照

有大兄弟们会说,我一个协程去下载图片太慢了,可不可以下载快一点,多个协程一起去下载呗

并发爬取咱们的小柴犬

咱们还记之前说过的 GO 通道 和 sync 包 吗?GO 通道和 sync 包的分享,正好能够实际一下,这个小性能是比较简单的,说一下大体的思路,大家感兴趣的话,能够去实现一波,

  • 咱们读取上述网址的数据,并转换成 字符串
  • 应用正则匹配出对应的一系列图片链接
  • 将每一张图片链接放到一个有缓冲的通道中,暂且设置缓冲区为 100
  • 咱们再开 3 个协程去并发的读取通道的数据,并下载到本地,文件名的批改形式能够参考上述的编码

怎么样,大兄弟们,小伙伴们,感兴趣的话,能够实际一波,要是对爬取动态数据有想法的话,咱们能够沟通和探讨一下,一起提高

总结

  • 分享动态网页和动静网页的简要阐明
  • GO 爬取动态网页简略数据
  • GO 爬取网页上的图片
  • 并发爬取网页上的资源

欢送点赞,关注,珍藏

敌人们,你的反对和激励,是我保持分享,提高质量的能源

好了,本次就到这里,下一次 GO 中 gjson 的利用和分享

技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。

我是 小魔童哪吒,欢送点赞关注珍藏,下次见~

正文完
 0