用Golang写一个简单的爬虫

闲来无事写了一个比较简陋的爬虫,跟大家分享一下, 写的可能有点啰嗦,本来写的是文档,但是这里没办法上传文件,只能以这种方式给大家展示了,下面有很清晰的目录,也可以直接复制到文档中查看,方便目录指引。

《教程:使用go语言编写简单爬虫工具》
目录

  1. 在使用go之前首先了解什么是go: 8
  2. 安装go以及相关工具 8
    2.1. 安装go运行环境: 8
    2.2 安装git : 13
    2.3安装编译器VSCode: 18
    安装完成 21
  3. 写一个简单go程序 22
  4. 使用colly 做一个简单的爬虫工具 23
    4.1 最简单的爬虫,爬取网站的a链接内容: 23
  5. 功能扩展 25
    5.1 使用os,io包将爬取到的数据写入到文件中存储 25
    5.2 使用gin包将程序设置为一个web 服务 27
    5.3 使用proxy包设置IP代理池: 30
    5.4 使用sync包实现多线程: 33
    5.5 使用cron包将数据抓取添加为计划任务,每半小时执行一次 37
    5.6 使用gojieba包进分词管理,抓取指定内容 42

1.在使用go之前首先了解什么是go:
Go(又称Golang)是Google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
罗伯特·格瑞史莫(Robert Griesemer),罗勃·派克(Rob Pike)及肯·汤普逊(Ken Thompson)于2007年9月开始设计Go,稍后Ian Lance Taylor、Russ Cox加入项目。Go是基于Inferno操作系统所开发的。Go于2009年11月正式宣布推出,成为开放源代码项目,并在Linux及Mac OS X平台上进行了实现,后来追加了Windows系统下的实现。在2016年,Go被软件评价公司TIOBE 选为“TIOBE 2016 年最佳语言”。 目前,Go每半年发布一个二级版本(即从a.x升级到a.y)。
Go的语法接近C语言,但对于变量的声明有所不同。Go支持垃圾回收功能。Go的并行模型是以东尼·霍尔的通信顺序进程(CSP)为基础,采取类似模型的其他语言包括Occam和Limbo,但它也具有Pi运算的特征,比如通道传输。在1.8版本中开放插件(Plugin)的支持,这意味着现在能从Go中动态加载部分函数。
与C++相比,Go并不包括如枚举、异常处理、继承、泛型、断言、虚函数等功能,但增加了 切片(Slice) 型、并发、管道、垃圾回收、接口(Interface)等特性的语言级支持。Go 2.0版本将支持泛型,对于断言的存在,则持负面态度,同时也为自己不提供类型继承来辩护。
不同于Java,Go内嵌了关联数组(也称为哈希表(Hashes)或字典(Dictionaries)),就像字符串类型一样。
2.安装go以及相关工具
2.1.安装go运行环境:
2.1.1 开始安装:
首先下载go安装包->https://studygolang.com/dl选择自己需要的版本进行下载,下载完成后进行安装
安装MSI文件或者zip压缩包版本解压,我下载的是:
https://studygolang.com/dl/golang/go1.12.5.windows-amd64.msi,下载完成后双击安装即可

点击Next下一步

选择I accept the terms in the License Agreement. 然后Next 下一步

选择安装目录

2.1.2. 安装完成后,设置环境变量:
打开我的电脑:

进入环境变量配置界面:

配置环境变量:
新建环境变量GOROOT(GOROOT变量值为go的安装目录,我安装的是D:\Go),根据自己的安装路径进行配置

配置path路径( win10 ),在后面path最后添加D:\Go\bin路径(必须是go安装目录下的bin文件):

如果是win7系统( 在路径最后面添加D:\Go\bin ),一定要注意,在添加路径之前一定要用“;”与前面的路径分隔(每个路径之间必须有“;”分隔),路径根据自己的安装路径修改:

2.1.3. 创建项目目录的环境变量:

接下来新建文件夹,作为自己的项目目录,我创建的目录是mygo:

然后继续新建环境变量GOPATH(此变量设置项目路径):

到此处环境变量就设置完成了,接下来测试是否全部设置成功,打开黑窗口(ctrl+r -> cmd) 输入 go env ,显示一下内容表示环境变量设置成功:

输入 go version,显示一下内容,go就可以安装完成了:

2.2 安装git :
如果不安装git 直接进行开发的话,在你引入github 包的时候你会发现出现以下错误:
go get github.com/gocolly/colly: git ls-remote -q
https://github.com/gocolly/colly In D:\mygo\pkg\mod\cache\vcs\eca8728ba98ac04ea7ee3526a26f0326ab6bc4f75745fbfbae6f13421595fd59: exec: “git”: executable file not found in %PATH%

因为github的包都是通过git管理,所以在 go get 包之前要先安装git:
下载git win64 安装包 :
https://github.com/git-for-windows/git/releases/download/v2.21.0.windows.1/Git-2.21.0-64-bit.exe下载完成后,双击安装程序,选择git 相关配置(默认即可):

点击install 开始安装程序,等待安装完成即可。

配置环境变量:打开环境变量配置页面(找不到重新看下上文第五页的打开方式),在path变量后面加上git 的安装路径下的bin目录,我的是D:\MyDownloads\Git\bin, 根据自己的安装目录添加即可。

测试是否安装完成, 打开黑窗口,输入git version查看git版本信息,到此git安装完成。

2.3安装编译器VSCode:
一个好的代码编译器可以极大地帮助我们进行很好的开发,比如Vscode.
首先去VsCode官网下载自己需要的版本,然后进行安装https://code.visualstudio.com/Download

选择我同意

选择安装路径

下一步

选择自己需要的功能后,下一步

点击安装

安装完成

3.写一个简单go程序
在开始写爬虫之前先写个小玩意测试一下,安装后的go是否可以正常编译,以确保后面的开发顺利。
首先在你的项目目录,也就是在安装go之后设置的环境变量GOPATH的目录,创建文件夹src,这个文件夹以后将存放你的所有项目。目录结构可以自己随意定义,但是一定要在src目录下

创建好文件夹后,开始写文件,创建文件main.go

文件中写一个简单的字符串输出(复制下面代码到你创建的文件):
逐行分析这段程序:
第一行是必须的。所有Go语言编写的文件都以package <*>开头,对于独立运行的执行文件必须是 package main;
第二行表示将fmt包加入main。一般非main的其他package(包)都被称为库,
第三行就是程序中的主函数。Go程序执行时候,首先调用的函数就是main函数。这个是从C中继承过来的。这里是main函数的函数定义。
第四行调用fmt包的函数打印字符串到屏幕。字符串由””包裹,并且可以包含非ASCII的字符。
一个独立的可执行的golang程序,package main是必须出现,紧跟在是引入的各种库,然后是各个函数,这里必须要有一个main函数。main函数是程序的入口。

package main    // 定义包名

// 引入需要的包
import(
"fmt"
)

// 定义一个方法main
func main(){

// 打印字符串
fmt.Println("go运行环境初次测试,运行正常!")

}

打开黑窗口,进入你的项目目录,比如我项目的文件夹是在D:\mygo\src\test下,输入命令d: , 进入到D盘, 然后 cd D:\mygo\src\test,到项目目录下, 输入 go run main.go

go的一个简单操作就完成啦。接下来就开始正式使用colly实现爬虫吧!!!

4.使用colly 做一个简单的爬虫工具
4.1 最简单的爬虫,爬取网站的a链接内容:

package main		// 定义一个包名

// 引入需要用到的包
import(
    "log"
    "github.com/gocolly/colly"
)

func main(){
    // 调用getHref()方法,传一个字符串类型的参数,进行相关网址的a链接内容抓取
    getHref("https://www.163.com/")

}

// urls 参数为你要爬取的网站的地址
func getHref ( urls string ) {

    	c := colly.NewCollector()
    	visited := false

c.OnResponse(func(r *colly.Response) {
// log.Println(string(r.Body))
if !visited {
visited = true
r.Request.Visit("/get?q=2")
}
    })

   	 c.OnHTML("a[href]", func(e *colly.HTMLElement) {
href := e.Text		// 获取到的a链接的内容
log.Println(href)
})  
c.Visit(urls)	// 访问网站
    
}

打开黑窗口 输入 go run main.go,发现出现了如下问题,这是因为你还未从github拉取到colly包,所以在编译的时候找不到。

打开黑窗口 输入 go get github.com/gocolly/colly,等待抓取完成即可,

抓取完成后,在继续 go run main.go 回车 ,至此,一个简单的通过colly爬取网站a链接内容的爬虫工具就做好了。

5.功能扩展
5.1 使用os,io包将爬取到的数据写入到文件中存储

package main

import(
    "log"
    "github.com/gocolly/colly"
    "os"
    "io"
)

func main(){
    // 调用getHref()方法,传一个字符串类型的参数,进行相关网址的a链接内容抓取
    getHref("https://www.163.com/")

}

func getHref ( urls string ) {

    c := colly.NewCollector()
    visited := false

c.OnResponse(func(r *colly.Response) {
// log.Println(string(r.Body))
if !visited {
visited = true
r.Request.Visit("/get?q=2")
}
    })

    c.OnHTML("a[href]", func(e *colly.HTMLElement) {
href := e.Text
    log.Println(href)

    filename := "hrefText.txt"

    var f *os.File
    /***************************** 第一种方式: 使用 io.WriteString 写入文件 ***********************************************/
    if checkFileIsExist(filename) { //如果文件存在
    f, _ = os.OpenFile(filename, os.O_APPEND, 0666) //打开文件
    }else {
    f, _ = os.Create(filename) //创建文件
    }

    n, _ := io.WriteString(f, href +"\n", ) //写入文件(字符串).

    f.Close()   // 关闭文件

    if n == 0 {
    return
    }

})
c.Visit(urls)   // 访问被抓取的网站url

}

func checkFileIsExist(filename string) bool {
    var exist = true
    if _, err := os.Stat(filename); os.IsNotExist(err) {
    exist = false
    }
    return exist
}

复制代码到main.go ,打开黑窗口,到项目目录,输入 go run main.go,文件写入成功

5.2 使用gin包将程序设置为一个web 服务
实现功能:在exe文件运行后,可在浏览器访问,代码如下:

package main

import(
    "log"
    "github.com/gocolly/colly"
    "os"
    "io"
    "github.com/gin-gonic/gin"
    "net/http"
)

func main(){

    // 初始化引擎
    engine := gin.Default()
    // 注册一个路由和处理函数
    engine.Any("/", WebRoot)
    // 绑定端口,然后启动应用
    engine.Run(":9205")
    // 调用getHref()方法,传一个字符串类型的参数,进行相关网址的a链接内容抓取

}

func WebRoot(context *gin.Context) {
    getHref("https://www.163.com/")
context.String(http.StatusOK, "hello, world")
}

func getHref ( urls string ) {

    c := colly.NewCollector()
    visited := false

    c.OnResponse(func(r *colly.Response) {
        // log.Println(string(r.Body))
        if !visited {
            visited = true
            r.Request.Visit("/get?q=2")
        }
    })

    c.OnHTML("a[href]", func(e *colly.HTMLElement) {
        href := e.Text
        log.Println(href)

        filename := "hrefText.txt"

        var f *os.File
        /***************************** 第一种方式: 使用 io.WriteString 写入文件 ***********************************************/
        if checkFileIsExist(filename) { //如果文件存在
            f, _ = os.OpenFile(filename, os.O_APPEND, 0666) //打开文件
        }else {
            f, _ = os.Create(filename) //创建文件
        }

        n, _ := io.WriteString(f, href +"\n", ) //写入文件(字符串).

        f.Close()   // 关闭文件

        if n == 0 {
            return
        }

    })
    c.Visit(urls)   // 访问被抓取的网站url

}

func checkFileIsExist(filename string) bool {
    var exist = true
    if _, err := os.Stat(filename); os.IsNotExist(err) {
        exist = false
    }
    return exist
}

打开黑窗口,进入项目目录,输入 go run main.go ,发现又出现了如下错误:

因为你没有gin包,所以使用命令 go get github.com/gin-gonic/gin 去github拉取包下来即可,拉取完毕后,

继续 go run main.go 结果如下:
打开浏览器,访问“本机IP:端口”,我设置的端口是9205(127.0.0.1:9205):

查看文件数据是否生成成功:

到此处结束,说明设置生效。

5.3 使用proxy包设置IP代理池:

设置一个数组变量,变量中装有n个IP地址,每次取一个,可用IP执行,不可用IP删除,代码如下:

package main

import(
    "os"
    "io"
    "log"
    "bytes"
    "net/http"
    "github.com/gocolly/colly"
    "github.com/gin-gonic/gin"
    "github.com/gocolly/colly/proxy"
)

func main(){

    // 初始化引擎
    engine := gin.Default()
    // 注册一个路由和处理函数
    engine.Any("/", WebRoot)
    // 绑定端口,然后启动应用
    engine.Run(":9205")
    // 调用getHref()方法,传一个字符串类型的参数,进行相关网址的a链接内容抓取

}

func WebRoot(context *gin.Context) {

    co := colly.NewCollector(colly.AllowURLRevisit())
var ipArray = []string{"http://157.230.232.130:80", "http://213.23.122.170:83", "http://91.205.218.33:80"}

    for i := 0; i < len(ipArray); i++ {

if ipArray == nil {
log.Printf( "无可用IP..." )
break
}

rp, err := proxy.RoundRobinProxySwitcher(ipArray[i])

if err != nil {
log.Fatal(err)
}
co.SetProxyFunc(rp)

co.OnResponse(func(r *colly.Response) {
log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
})

err = co.Visit("https://httpbin.org/ip")
if err != nil {
log.Printf( ipArray[i] )
ipArray = remove( ipArray, i )
}else{
break
}

    }
    
    getHref("https://www.163.com/")
context.String(http.StatusOK, "hello, world")
}

func getHref ( urls string ) {

    c := colly.NewCollector()
    visited := false

    c.OnResponse(func(r *colly.Response) {
        // log.Println(string(r.Body))
        if !visited {
            visited = true
            r.Request.Visit("/get?q=2")
        }
    })

    c.OnHTML("a[href]", func(e *colly.HTMLElement) {
        href := e.Text

        filename := "hrefText.txt"

        var f *os.File
        /***************************** 第一种方式: 使用 io.WriteString 写入文件 ***********************************************/
        if checkFileIsExist(filename) { //如果文件存在
            f, _ = os.OpenFile(filename, os.O_APPEND, 0666) //打开文件
        }else {
            f, _ = os.Create(filename) //创建文件
        }

        n, _ := io.WriteString(f, href +"\n", ) //写入文件(字符串).

        f.Close()   // 关闭文件

        if n == 0 {
            return
        }

    })
    c.Visit(urls)   // 访问被抓取的网站url

}

func checkFileIsExist(filename string) bool {
    var exist = true
    if _, err := os.Stat(filename); os.IsNotExist(err) {
        exist = false
    }
    return exist
}

func remove(ipArray []string, i int) []string {
returnArray := append(ipArray[:i], ipArray[i+1:]...)
return returnArray
}

因为是在网上找的免费的代理IP,所以不稳定,有些IP经常会用不了,运行结果如下图:

5.4 使用sync包实现多线程:

在项目目录创建文件domain.txt, 写入想要抓取的网站URL:比如我想要抓取的是

获取文件中的URL,把每个网站的抓取操作分别加入线程,同时进行抓取操作,并将数据写入不同的文件存储,代码如下:

package main

import(
    "os"
    "io"
    "log"
    "sync"
    "time"
    "bytes"
    "bufio"
    "strings"
    "net/http"
    "github.com/gocolly/colly"
    "github.com/gin-gonic/gin"
    "github.com/gocolly/colly/proxy"
)

var wg sync.WaitGroup

func main(){

    // 初始化引擎
    engine := gin.Default()
    // 注册一个路由和处理函数
    engine.Any("/", WebRoot)
    // 绑定端口,然后启动应用
    engine.Run(":9205")
    // 调用getHref()方法,传一个字符串类型的参数,进行相关网址的a链接内容抓取

}

func WebRoot(context *gin.Context) {

    co := colly.NewCollector(colly.AllowURLRevisit())
var ipArray = []string{"http://157.230.232.130:80", "http://213.23.122.170:83", "http://91.205.218.33:80"}

    for i := 0; i < len(ipArray); i++ {

if ipArray == nil {
log.Printf( "无可用IP..." )
break
}

rp, err := proxy.RoundRobinProxySwitcher(ipArray[i])

if err != nil {
log.Fatal(err)
}
co.SetProxyFunc(rp)

co.OnResponse(func(r *colly.Response) {
log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
})

err = co.Visit("https://httpbin.org/ip")
if err != nil {
log.Printf( ipArray[i] )
ipArray = remove( ipArray, i )
}else{
break
}

    }
    
    checkUrl("./domain.txt");
context.String(http.StatusOK, "hello, world")
}

// 获取文件内的所有url,然后进行数据抓取
func checkUrl( filePath string){
    
    fi, err := os.Open(filePath)
if err != nil {
log.Printf("Error: %s\n", err)
return
    }
    
    defer fi.Close()
    br := bufio.NewReader(fi)
    
for {

a, _, c := br.ReadLine()
if c == io.EOF {
break
        }
Splitstring := strings.Split(string(a), ".")

        // 调用方法将抓取到的数据写入文件
wg.Add(1)
        go getHref( string(a), Splitstring[1])  
}   
wg.Wait()
}

func getHref ( urls string, filename string ) {

    c := colly.NewCollector()
    visited := false

    c.OnResponse(func(r *colly.Response) {
        // log.Println(string(r.Body))
        if !visited {
            visited = true
            r.Request.Visit("/get?q=2")
        }
    })

    c.OnHTML("a[href]", func(e *colly.HTMLElement) {

        var filename string = filename
        var f *os.File

        href := e.Text
        path := ""
        time := time.Now().Format("2006-01-02")
        path = "record/" + filename + "/"
        filename = path + time + "---" + filename + ".txt"
        
        /***************************** 第一种方式: 使用 io.WriteString 写入文件 ***********************************************/
        if !isExist(path) {
            err := os.MkdirAll(path,os.ModePerm)
            f, _ = os.Create(filename) //创建文件
            if err != nil{
                log.Println(err)
            }
        }else{
            if isExist(filename) { //如果文件存在
                f, _ = os.OpenFile(filename, os.O_APPEND, 0666) //打开文件
            }else {
                f, _ = os.Create(filename) //创建文件
            }
        }
        
        n, _ := io.WriteString(f, href +"\n", ) //写入文件(字符串).
        f.Close()
        if n == 0 {
            return
        }
    })
    c.Visit(urls)   // 访问被抓取的网站url
}

// 判断所给路径文件/文件夹是否存在(返回true是存在)
func isExist(path string) bool {
    _, err := os.Stat(path) //os.Stat获取文件信息
    if err != nil {
        if os.IsExist(err) {
            return true
        }
        return false
    }
    return true
}

func remove(ipArray []string, i int) []string {
returnArray := append(ipArray[:i], ipArray[i+1:]...)
return returnArray
}

打开黑窗口,进入项目目录,输入 go run main.go 等程序运行结束,打开项目文件夹 你会发现所有文件已经写入到相关文件内

5.5 使用cron包将数据抓取添加为计划任务,每半小时执行一次
添加定时任务,每半小时执行一次,我为了看效果这里设置的是随意设置的秒,自己根据需求自行修改spec的值,具体参数说明请查看官方文档
https://godoc.org/github.com/robfig/cron
具体实现代码如下:

package main

import(
    "os"
    "io"
    "log"
    "sync"
    "time"
    "bytes"
    "bufio"
    "strings"
    "net/http"
"github.com/robfig/cron"
    "github.com/gocolly/colly"
    "github.com/gin-gonic/gin"
    "github.com/gocolly/colly/proxy"
)

var wg sync.WaitGroup

func main(){

    // 初始化引擎
    engine := gin.Default()
    // 注册一个路由和处理函数
    engine.Any("/", WebRoot)
    // 绑定端口,然后启动应用
    engine.Run(":9205")
    // 调用getHref()方法,传一个字符串类型的参数,进行相关网址的a链接内容抓取

}

func WebRoot(context *gin.Context) {

    co := colly.NewCollector(colly.AllowURLRevisit())
var ipArray = []string{"http://157.230.232.130:80", "http://213.23.122.170:83", "http://91.205.218.33:80"}

    for i := 0; i < len(ipArray); i++ {

if ipArray == nil {
log.Printf( "无可用IP..." )
break
}

rp, err := proxy.RoundRobinProxySwitcher(ipArray[i])

if err != nil {
log.Fatal(err)
}
co.SetProxyFunc(rp)

co.OnResponse(func(r *colly.Response) {
log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
})

err = co.Visit("https://httpbin.org/ip")
if err != nil {
log.Printf( ipArray[i] )
ipArray = remove( ipArray, i )
}else{
break
}

    }
    
    var i int = 0
    c := cron.New()
spec := "*/50 * * * * ?"
c.AddFunc(spec, func() {
        i++
        log.Printf("这是第%d次执行!", i)
checkUrl("./domain.txt");
})
c.Start()

    context.String(http.StatusOK, "hello, world")
}

// 获取文件内的所有url,然后进行数据抓取
func checkUrl( filePath string){
    
    fi, err := os.Open(filePath)
if err != nil {
log.Printf("Error: %s\n", err)
return
    }
    
    defer fi.Close()
    br := bufio.NewReader(fi)
    
for {

a, _, c := br.ReadLine()
if c == io.EOF {
break
        }
Splitstring := strings.Split(string(a), ".")

        // 调用方法将抓取到的数据写入文件
wg.Add(1)
        go getHref( string(a), Splitstring[1])  
}   
wg.Wait()
}

func getHref ( urls string, filename string ) {

    c := colly.NewCollector()
    visited := false

    c.OnResponse(func(r *colly.Response) {
        // log.Println(string(r.Body))
        if !visited {
            visited = true
            r.Request.Visit("/get?q=2")
        }
    })

    c.OnHTML("a[href]", func(e *colly.HTMLElement) {

        var filename string = filename
        var f *os.File

        href := e.Text
        path := ""
        time := time.Now().Format("2006-01-02")
        path = "record/" + filename + "/"
        filename = path + time + "---" + filename + ".txt"
        
        /***************************** 第一种方式: 使用 io.WriteString 写入文件 ***********************************************/
        if !isExist(path) {
            err := os.MkdirAll(path,os.ModePerm)
            f, _ = os.Create(filename) //创建文件
            if err != nil{
                log.Println(err)
            }
        }else{
            if isExist(filename) { //如果文件存在
                f, _ = os.OpenFile(filename, os.O_APPEND, 0666) //打开文件
            }else {
                f, _ = os.Create(filename) //创建文件
            }
        }
        
        n, _ := io.WriteString(f, href +"\n", ) //写入文件(字符串).
        f.Close()
        if n == 0 {
            return
        }
    })
    c.Visit(urls)   // 访问被抓取的网站url
}

// 判断所给路径文件/文件夹是否存在(返回true是存在)
func isExist(path string) bool {
    _, err := os.Stat(path) //os.Stat获取文件信息
    if err != nil {
        if os.IsExist(err) {
            return true
        }
        return false
    }
    return true
}

func remove(ipArray []string, i int) []string {
returnArray := append(ipArray[:i], ipArray[i+1:]...)
return returnArray
}

打开黑窗口,进入项目目录,输入go run main.go提示如下:

提示没有cron包,执行go get github.com/robfig/cron

继续执行 go run main.go 提示如下:

去浏览器输入127.0.0.1:9205

此时计划任务已经成功生成,任务会根据spec 配置在规定时间执行任务

5.6 使用gojieba包进分词管理,抓取指定内容
当前爬取的是所有数据,但很多时候我们并不需要所有数据,gojieba就可以帮我们解决,在使用之前首先要先获取gojieba 包,打开黑窗口,输入 go get github.com/yaniwu/gojieba
具体代码如下:

    package main
    
    import(
        "os"
        "io"
        "log"
        "sync"
        "time"
        "bytes"
        "bufio"
        "strings"
        "net/http"
    "github.com/robfig/cron"
        "github.com/gocolly/colly"
        "github.com/gin-gonic/gin"
        "github.com/yanyiwu/gojieba"
        "github.com/gocolly/colly/proxy"
    )
    
    var wg sync.WaitGroup
    
    func main(){
    
        // 初始化引擎
        engine := gin.Default()
        // 注册一个路由和处理函数
        engine.Any("/", WebRoot)
        // 绑定端口,然后启动应用
        engine.Run(":9205")
        // 调用getHref()方法,传一个字符串类型的参数,进行相关网址的a链接内容抓取
    
    }
    
    func WebRoot(context *gin.Context) {
    
        co := colly.NewCollector(colly.AllowURLRevisit())
    var ipArray = []string{"http://157.230.232.130:80", "http://213.23.122.170:83", "http://91.205.218.33:80"}
    
        for i := 0; i < len(ipArray); i++ {
    
    if ipArray == nil {
    log.Printf( "无可用IP..." )
    break
    }
    
    rp, err := proxy.RoundRobinProxySwitcher(ipArray[i])
    
    if err != nil {
    log.Fatal(err)
    }
    co.SetProxyFunc(rp)
    
    co.OnResponse(func(r *colly.Response) {
    log.Printf("%s\n", bytes.Replace(r.Body, []byte("\n"), nil, -1))
    })
    
    err = co.Visit("https://httpbin.org/ip")
    if err != nil {
    log.Printf( ipArray[i] )
    ipArray = remove( ipArray, i )
    }else{
    break
    }
    
        }
        
        var i int = 0
        c := cron.New()
    spec := "*/50 * * * * ?"
    c.AddFunc(spec, func() {
            i++
            log.Printf("这是第%d次执行!", i)
    checkUrl("./domain.txt");
    })
    c.Start()
    
        context.String(http.StatusOK, "hello, world")
    }
    
    // 获取文件内的所有url,然后进行数据抓取
    func checkUrl( filePath string){
        
        fi, err := os.Open(filePath)
    if err != nil {
    log.Printf("Error: %s\n", err)
    return
        }
        
        defer fi.Close()
        br := bufio.NewReader(fi)
        
    for {
    
    a, _, c := br.ReadLine()
    if c == io.EOF {
    break
            }
    Splitstring := strings.Split(string(a), ".")
    
            // 调用方法将抓取到的数据写入文件
    wg.Add(1)
            go getHref( string(a), Splitstring[1])  
    }   
    wg.Wait()
    }
    
    func getHref ( urls string, filename string ) {
    
        c := colly.NewCollector()
        visited := false
    
        c.OnResponse(func(r *colly.Response) {
            // log.Println(string(r.Body))
            if !visited {
                visited = true
                r.Request.Visit("/get?q=2")
            }
        })
    
        c.OnHTML("a[href]", func(e *colly.HTMLElement) {
    
            var filename string = filename
            var f *os.File
    
            href := e.Text
            path := ""
            time := time.Now().Format("2006-01-02")
            path = "record/" + filename + "/"
            filename = path + time + "---" + filename + ".txt"
    
            var s string
            var words []string
            use_hmm := true
            x := gojieba.NewJieba()
            defer x.Free()
    
            s = "中国美国"
            words = x.Cut(s, use_hmm)
    
            var words_string string
    length := len( words )
    num := 0
    for {
    words_string = words[num]
    if strings.Contains(href, words_string) == true{
                    /***************************** 第一种方式: 使用 io.WriteString 写入文件 ***********************************************/
                    if !isExist(path) {
                        err := os.MkdirAll(path,os.ModePerm)
                        f, _ = os.Create(filename) //创建文件
                        if err != nil{
                            log.Println(err)
                        }
                    }else{
                        if isExist(filename) { //如果文件存在
                            f, _ = os.OpenFile(filename, os.O_APPEND, 0666) //打开文件
                        }else {
                            f, _ = os.Create(filename) //创建文件
                        }
                    }
    
                    n, _ := io.WriteString(f, href +"\n", ) //写入文件(字符串).
                    f.Close()
                    if n == 0 {
                        return
                    }
                }
    
                num++
    if num >= length{
    break
    }
            }
        })
        c.Visit(urls)   // 访问被抓取的网站url
    }
    
    // 判断所给路径文件/文件夹是否存在(返回true是存在)
    func isExist(path string) bool {
        _, err := os.Stat(path) //os.Stat获取文件信息
        if err != nil {
            if os.IsExist(err) {
                return true
            }
            return false
        }
        return true
    }
    
    func remove(ipArray []string, i int) []string {
    returnArray := append(ipArray[:i], ipArray[i+1:]...)
    return returnArray
    }

你会发现获取到的数据都是我们需要的,包含“中国”,“美国”的标题,运行结果如下图:

到此为止,一个简单的爬虫就已经完成搭建完成。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值