Golang诞生已经超过十个年头了,发展得愈发完善,其简单方便的协程并发机制使得其在爬虫领域有着一定的天赋。
首先我们来看一看,Golang相对于Python这个爬虫领域的传统强者,有哪些优点和缺点。
优点:
完善简便的协程并发机制并发数量大占用资源少运行速度更快部署方便
缺点:
数据处理比较繁琐
成熟工具不是很多
资料较少
实现相同逻辑需要的代码更多
由于Golang本身静态语言的特性,和其特别的异常处理方式等等原因,在发起较复杂的请求时需要的代码量自然会比Python多不少,但是其并发的数量也是远超Python的,所以两者应用的场景并不十分相同,我们可以根据需要灵活的选择。
demo1.go代码如下:
package main
import (
"bufio"
"fmt"
"io/ioutil"
"net/http"
"os"
"regexp"
)
var (
//这个\d表示数字
reQQEmail = `\d+@qq.com`
//w代表大小写字母+数字+下划线
reEmail = `\w+@\w+\.\w+`
//s?有或者没有s
//+代表出1次或多次
//\s\S各种字符
//+?代表贪婪模式
reLinke = `href="(https?://[\s\S]+?)"`
rePhone = `1[3456789]\d\s?\d{4}\s?\d{4}`
reImg = `https?://[^"]+?(\.((jpg)|(png)|(jpeg)|(gif)))`
)
//抽取数据根据url获取内容
func GetPageStr(url string) (pageStr string) {
resp, err := http.Get(url)
HandleError(err, "http.Get url")
defer resp.Body.Close()
//读取页面内容
pageBytes, err := ioutil.ReadAll(resp.Body)
HandleError(err, "ioutil.ReadAll")
//字节转字符串
pageStr = string(pageBytes)
return pageStr
}
//爬邮箱方法二
func GetEmail2(url string) {
pageStr := GetPageStr(url)
re := regexp.MustCompile(reEmail)
results := re.FindAllStringSubmatch(pageStr, -1)
for _, result := range results {
fmt.Println(result)
}
}
//爬邮箱
func GetEmail() {
//1.去网站拿数据
resp, err := http.Get("https://tieba.baidu.com/p/6051076813?red_tag=1573533731")
HandleError(err, "Http.Get url")
defer resp.Body.Close()
//2.读取页面内容
pageBytes, err := ioutil.ReadAll(resp.Body)
HandleError(err, "Readall Error")
//字节转字符串
pageStr := string(pageBytes)
// fmt.Println(pageStr)
//3.过滤数据,过滤qq邮箱
re := regexp.MustCompile(reQQEmail)
results := re.FindAllStringSubmatch(pageStr, -1)
// WriteFiles(results[0][0])
// WriteFiles(results[1][0])
// WriteFiles(results[2][0])
//遍历结果
for i := 0; i < len(results); i++ {
// fmt.Println(results[i][0])
WriteFiles(results[i][0])
}
fmt.Println("写入成功")
}
//处理异常
func HandleError(err error, why string) {
if err != nil {
fmt.Println(why, err)
}
}
//爬链接
func GetLink(url string) {
pageStr := GetPageStr(url)
re := regexp.MustCompile(reLinke)
results := re.FindAllStringSubmatch(pageStr, -1)
for _, result := range results {
fmt.Println(result[1])
}
}
//爬手机号
func GetPhone(url string) {
pageStr := GetPageStr(url)
re := regexp.MustCompile(rePhone)
results := re.FindAllStringSubmatch(pageStr, -1)
for _, result := range results {
fmt.Println(result)
}
}
//爬图片
func GetImg(url string) {
pageStr := GetPageStr(url)
re := regexp.MustCompile(reImg)
results := re.FindAllStringSubmatch(pageStr, -1)
for _, result := range results {
fmt.Println(result[0])
}
}
//写文件
func WriteFiles(str string) {
fileName := "/Users/imac/<a href="https://www.qqradios.com/tag/go" title="查看更多关于go的文章" target="_blank">go</a>/src/test/abc.txt"
file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, os.ModePerm)
HandleError(err, "OpenFile")
defer file.Close()
w := bufio.NewWriter(file)
_, err = w.WriteString(str)
HandleError(err, "WriteString")
_, err = w.WriteString("\r\n")
HandleError(err, "Write err")
w.Flush()
}
// func main(){
// //1、爬邮箱
// // GetEmail()
// //2、爬邮箱方法二
// // GetEmail2("https://tieba.baidu.com/p/6051076813?red_tag=1573533731")
// //3、爬链接
// // GetLink("https://www.baidu.com/s?wd=qqhd")
// //4、爬手机号
// // GetPhone("https://www.qqradios.com/")
// //5.爬图片美女
// GetImg("https://www.qqradios.com/meizitu/")
// }
demo2.go代码如下:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
"strings"
"sync"
"time"
)
func myTest() {
//pageStr:=GetPageStr("https://www.qqradios.com/page/2")
//fmt.Println(pageStr)
//获取链接
GetImg("https://www.qqradios.com/page/2")
}
//下载图片
func DownloadFile(url string, filename string) (ok bool) {
resp, err := http.Get(url)
HandleError(err, "Http.get.url")
defer resp.Body.Close()
bytes, err := ioutil.ReadAll(resp.Body)
HandleError(err, "resp.body")
filename = "./pa/img/" + filename
//写出数据
err = ioutil.WriteFile(filename, bytes, 0666)
if err != nil {
return false
} else {
return true
}
}
var (
//存放图片链接的数据管道
chanImageUrls chan string
waitGroup sync.WaitGroup
//用于监控携程
chanTask chan string
)
func main() {
//myTest()
//DownloadFile("https://www.qqradios.com/uploads/allimg/201904/1555256296278615.jpg","1.jpg")
//1、初始化管道
chanImageUrls = make(chan string, 1000000)
chanTask = make(chan string, 26)
//2、爬虫携程
for i := 1; i < 27; i++ {
waitGroup.Add(1)
<a href="https://www.qqradios.com/tag/go" title="查看更多关于go的文章" target="_blank">go</a> getImgUrls("https://www.qqradios.com/page/" + strconv.Itoa(i))
}
//3、任务统计协程,统计26个任务是否都完成,完成了就关闭管道
waitGroup.Add(1)
<a href="https://www.qqradios.com/tag/go" title="查看更多关于go的文章" target="_blank">go</a> CheckOK()
//4、下载协程,从管道中读取链接并下载
for i := 0; i < 5; i++ {
waitGroup.Add(1)
<a href="https://www.qqradios.com/tag/go" title="查看更多关于go的文章" target="_blank">go</a> DownloadImg()
}
waitGroup.Wait()
}
//下载图片
func DownloadImg() {
for url := range chanImageUrls {
filename := GetFilenameFromUrl(url)
ok := DownloadFile(url, filename)
if ok {
fmt.Printf("%s下载成功\n", filename)
} else {
fmt.Printf("%s下载失败\n", filename)
}
}
waitGroup.Done()
}
//截取url名字
func GetFilenameFromUrl(url string) (filename string) {
//返回最后一个/的位置
lastIndex := strings.LastIndex(url, "/")
//切出来
filename = url[lastIndex+1:]
//时间戳解决重名
timePrefix := strconv.Itoa(int(time.Now().UnixNano()))
filename = timePrefix + "_" + filename
return
}
//任务统计协程
func CheckOK() {
var count int
for {
url := <-chanTask
fmt.Printf("%s完成了爬娶任务\n", url)
count++
if count == 26 {
close(chanImageUrls)
break
}
}
waitGroup.Done()
}
//爬图片链接到管道
func getImgUrls(url string) {
urls := getImgs(url)
//遍历切片里所有链接,存入数据管道
for _, url := range urls {
chanImageUrls <- url
}
//标知当前协程完成,每完成一个任务,写一条数据,用于监控协程知道已经完成了几个任务
chanTask <- url
waitGroup.Done()
}
//获取当前页图片链接
func getImgs(url string) (urls []string) {
pageStr := GetPageStr(url)
re := regexp.MustCompile(reImg)
results := re.FindAllStringSubmatch(pageStr, -1)
fmt.Printf("共找到%d条结果\n", len(results))
for _, result := range results {
url := result[0]
urls = append(urls, url)
}
return
}
Golang爬虫代码攻略运行效果如下
原文:https://www.qqradios.com/2020/1250.html
