Bootstrap

【Go】基于 Go 1.19 的站点模板爬虫【实战演练版】

0. 前言

Go 语言,也被称为Golang,是由Google开发的一种开源编程语言,它在 2009 年首次发布,并在 2012 年正式开源。Go 语言被设计用来简化大型软件的开发,特别注重并发编程和内存安全。

0.1 特点

  1. 静态类型:Go 是静态类型语言,这意味着类型在编译时已经确定,有助于在编译阶段捕捉错误。
  2. 编译型语言:Go 语言编写的程序会被编译成机器码,直接运行在硬件上,因此执行效率较高。
  3. 并发编程:Go 内置了goroutine(轻量级线程)和channel(用于goroutine之间通信的管道),使得并发编程变得简单。
  4. 内存安全:Go 语言通过垃圾回收(GC)机制来自动管理内存,减少了内存泄露的风险。
  5. 简洁的语法:Go 语言的语法简洁明了,易于学习和理解。
  6. 工具链:Go 语言拥有一套完整的工具链,包括但不限于格式化工具gofmt、包管理工具go get等。
  7. 跨平台:Go 语言支持跨平台编译,可以轻松地编译为不同操作系统和架构的二进制文件。

0.2 优势

  1. 高效的并发处理:通过 goroutine 和 channel,Go 语言使得并发编程变得简单而高效。
  2. 快速编译:Go 语言的编译速度非常快,这使得开发周期缩短,提高了开发效率。
  3. 易于维护:Go 语言的代码结构清晰,易于维护和重构。
  4. 强大的标准库:Go 语言提供了丰富的标准库,覆盖网络、I/O、数据处理等多种功能。
  5. 内存安全:通过垃圾回收机制,减少了内存泄露和指针错误的风险。
  6. 跨平台:可以很容易地为不同的操作系统和架构编译 Go 程序。

0.3 缺点

  1. 垃圾回收:虽然垃圾回收机制减少了内存管理的负担,但有时候 GC 的不可预测性可能会影响性能。
  2. 错误处理:Go语言的错误处理机制(通过返回错误值)与传统try-catch不同,有时会导致代码中错误处理逻辑过于冗长。
  3. 包管理:虽然 Go 的包管理工具在不断改进,但在早期版本中,模块化的支持并不完善。
  4. 泛型支持:直到 Go 1.18 版本才引入了泛型,之前版本的 Go 语言缺少泛型支持,这在某些场景下限制了代码的复用。
  5. 社区和生态系统:虽然 Go 社区活跃,但与一些老牌编程语言相比,其生态系统和第三方库可能还不够丰富。官方社区
  6. 类型系统:Go 的类型系统相对简单,对于某些复杂的编程场景可能不够灵活。

总的来说,Go 语言因其简洁、高效和易于并发编程的特点,在云计算、微服务、网络编程等领域得到了广泛的应用。尽管它有一些缺点,但这些通常不会影响它在特定领域的优势和适用性。

基于 Go 1.19 的站点模板爬虫就是一种使用 Go 语言编写的程序,专门用于从互联网上抓取网页内容。站点模板爬虫通常利用Go 语言的高并发特性来提高爬取效率。下面将简要介绍如何构建一个基于 Go 1.19 的站点模板爬虫。

1. 准备工作

确保你的开发环境已经安装了 Go 1.19 或更高版本的 SDK,并且配置好了 GOPATH 等环境变量。
要验证是否已经安装了 Go 1.19 或更高版本的 SDK,并且配置好了 GOPATH 等环境变量,可以通过以下步骤进行:

1.1 检查Go版本

打开终端(在 Windows 上是命令提示符或 PowerShell ),运行以下命令来检查安装的 Go 版本:

go version

这个命令将输出当前安装的 Go 版本。如果输出的版本是1.19或更高,说明你的 Go SDK 安装正确。

1.2 检查环境变量

要检查是否正确设置了GOPATH和其他相关环境变量,可以运行以下命令:
在Linux或macOS上:

echo $GOPATH
echo $GOROOT

在 Windows 上(命令提示符):

echo %GOPATH%
echo %GOROOT%

或者,如果你使用的是PowerShell

$env:GOPATH
$env:GOROOT

这些命令将显示GOPATHGOROOT环境变量的值。如果它们被正确设置,那么你应该能看到它们的路径。

1.3 验证Go工作区

除了检查环境变量,你还可以通过以下命令来验证 Go 工作区是否配置正确:

go env

这个命令将列出所有与 Go 相关的环境变量。检查以下关键变量:

  • GOPATH: 你的 Go 工作区的路径。
  • GOROOT: Go SDK 的安装路径。
  • GOBIN: 存储编译后二进制文件的路径(通常位于$GOPATH/bin)。
    确保这些路径正确无误。

1.4 示例输出

以下是一个示例输出,显示了一个正确配置的 Go 环境:

$ go version
go version go1.19 darwin/amd64
$ echo $GOPATH
/Users/yourusername/go
$ echo $GOROOT
/usr/local/go
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN="/Users/yourusername/go/bin"
GOCACHE="/Users/yourusername/Library/Caches/go-build"
GOENV="/Users/yourusername/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/yourusername/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/yourusername/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.19"
GCCGO="gccgo"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/dev/null"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/8_/8b4g0v2d2w9g1x8bgj0zyn4c0000gn/T/go-build3570152476=/tmp/go-build -gno-record-gcc-switches -fno-common"

如果以上步骤都没有问题,那么你的Go SDK和相关的环境变量应该已经配置好了。如果有任何问题,你可能需要重新安装 Go 或重新配置环境变量。

2. 选择库

在Go语言中,有几个流行的库可以用来编写爬虫,如:

  • net/http: 用于发起网络请求。
  • golang.org/x/net/html: 用于解析HTML
  • colly: 一个高性能的爬虫框架。

3. 爬虫基本结构

以下是构建站点模板爬虫的基本步骤:

3.1 发起网络请求

使用net/http包来发起GET请求,获取网页内容。

package main
import (
    "fmt"
    "io/ioutil"
    "net/http"
)
func fetch(url string) ([]byte, error) {
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    return ioutil.ReadAll(resp.Body)
}

3.2 解析HTML

使用golang.org/x/net/html包解析HTML文档,提取需要的数据。

func parse(html []byte) {
    doc, err := html.Parse(bytes.NewReader(html))
    if err != nil {
        panic(err)
    }
    // 遍历DOM,提取数据
}

3.3 使用爬虫框架(可选)

如果不想手动处理太多细节,可以使用colly这样的框架。

package main
import (
    "fmt"
    "github.com/gocolly/colly"
)
func main() {
    c := colly.NewCollector()
    c.OnHTML("div.some-class", func(e *colly.HTMLElement) {
        fmt.Println(e.Text)
    })
    c.Visit("http://example.com")
}

4. 实现站点模板

站点模板指的是你希望爬虫遵循的页面结构。你需要定义好如何从页面中提取有用信息,比如:

  • 标题
  • 内容
  • 链接
  • 图片等资源

5. 遵循 robots.txt 规则

在爬取网站之前,应当检查网站的robots.txt文件,确保你的爬虫行为符合网站管理员的规则。

6. 防止被封禁

  • 设置合理的请求间隔。
  • 使用随机的User-Agent
  • 可能的话,使用代理IP

7. 错误处理和日志记录

合理处理可能出现的错误,并记录日志,这对于调试和监控爬虫的状态非常重要。

8. 并发控制

利用 Go 的goroutinechannel实现并发控制,提高爬取效率。

9. 示例代码

以下是一个简单的示例,展示如何使用 Go 语言和colly框架编写一个基本的爬虫:

package main
import (
    "fmt"
    "github.com/gocolly/colly"
)
func main() {
    c := colly.NewCollector()
    // On every a element which has href attribute call callback
    c.OnHTML("a[href]", func(e *colly.HTMLElement) {
        link := e.Attr("href")
        // Visit link found on page
        c.Visit(e.Request.AbsoluteURL(link))
    })
    // Before making a request print "Visiting ..."
    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL.String())
    })
    // Start scraping on https://example.com
    c.Visit("https://example.com")
}

10. 实战演练

注意: 构建爬虫时,应始终遵守相关法律法规,尊重网站所有者的权益,不要进行过度的请求压力,以避免对网站造成不利影响。

以下是一个使用 Go 1.19 编写的简单示例程序,用于爬取豆瓣 Top 250 电影的名称、评分、评论人数、影评和发表年份,并将这些信息保存到CSV文件中。由于直接爬取网页内容可能违反服务条款,请在遵守相关法律法规和服务条款的前提下使用此代码。

首先,请确保安装了以下Go包:

  • github.com/gocolly/colly:用于网页爬取。
  • github.com/tealeg/xlsx:用于生成Excel文件。

使用以下命令安装所需的包:

go get -u github.com/gocolly/colly
go get -u github.com/tealeg/xlsx

CSV 版:

package main
import (
        "encoding/csv"
        "fmt"
        "os"
        "strconv"
        "strings"
        "github.com/gocolly/colly"
)
type Movie struct {
        Title       string
        Rating      string
        ReviewCount string
        Review      string
        Year        string
}
func main() {
        // 创建CSV文件
        file, err := os.Create("Top_250.csv")
        if err != nil {
                fmt.Println("Cannot create file", err)
                return
        }
        defer file.Close()
        writer := csv.NewWriter(file)
        defer writer.Flush()
        // 写入标题行
        writer.Write([]string{"电影名称", "评分", "评论人数", "影评", "发表年份"})
        // 初始化爬虫
        c := colly.NewCollector(
                colly.AllowedDomains("movie.douban.com"),
        )
        // 解析电影信息
        c.OnHTML("div.item", func(e *colly.HTMLElement) {
                var movie Movie
                movie.Title = e.ChildText("span.title")
                movie.Rating = e.ChildText("span.rating_num")
                movie.ReviewCount = strings.Trim(e.ChildText("div.star span"), "人评价")
                movie.Review = e.ChildText("spaninq")
                movie.Year = e.ChildText("div.bd p")
                // 清洗年份信息
                if idx := strings.Index(movie.Year, "上映时间:"); idx != -1 {
                        year := strings.Fields(movie.Year[idx:])
                        if len(year) > 0 {
                                movie.Year = year[1]
                        }
                }
                // 写入数据到CSV
                writer.Write([]string{
                        movie.Title,
                        movie.Rating,
                        movie.ReviewCount,
                        movie.Review,
                        movie.Year,
                })
        })
        // 爬取豆瓣Top 250电影
        for i := 0; i < 10; i++ {
                url := fmt.Sprintf("https://movie.douban.com/top250?start=%d", i*25)
                err := c.Visit(url)
                if err != nil {
                        fmt.Println("Visit error:", err)
                        return
                }
        }
        fmt.Println("爬取完成")
}

Excel 版:

package main

import (
	"fmt"
	"github.com/tealeg/xlsx"
	"github.com/gocolly/colly"
)

type Movie struct {
	Title       string
	Rating      string
	ReviewCount string
	Review      string
	Year        string
}

func main() {
	// 创建一个新的Excel文件
	file := xlsx.NewFile()
	sheet, err := file.AddSheet("Top 250")
	if err != nil {
		fmt.Printf("Error adding sheet: %s\n", err)
		return
	}

	// 创建标题行
	titleRow := sheet.AddRow()
	titleRow.SetHeightCM(1) // 设置行高
	titleRow.AddCell().Value = "电影名称"
	titleRow.AddCell().Value = "评分"
	titleRow.AddCell().Value = "评论人数"
	titleRow.AddCell().Value = "影评"
	titleRow.AddCell().Value = "发表年份"

	// 初始化爬虫
	c := colly.NewCollector(
		colly.AllowedDomains("movie.douban.com"),
	)

	// 解析电影信息
	c.OnHTML("div.item", func(e *colly.HTMLElement) {
		var movie Movie
		movie.Title = e.ChildText("span.title")
		movie.Rating = e.ChildText("span.rating_num")
		movie.ReviewCount = strings.Trim(e.ChildText("div.star span"), "人评价")
		movie.Review = e.ChildText("span.inq")
		movie.Year = e.ChildText("div.bd p")

		// 清洗年份信息
		if idx := strings.Index(movie.Year, "上映时间:"); idx != -1 {
			year := strings.Fields(movie.Year[idx:])
			if len(year) > 1 {
				movie.Year = year[1]
			}
		}

		// 写入数据到Excel
		row := sheet.AddRow()
		row.AddCell().Value = movie.Title
		row.AddCell().Value = movie.Rating
		row.AddCell().Value = movie.ReviewCount
		row.AddCell().Value = movie.Review
		row.AddCell().Value = movie.Year
	})

	// 爬取豆瓣Top 250电影
	for i := 0; i < 10; i++ {
		url := fmt.Sprintf("https://movie.douban.com/top250?start=%d", i*25)
		err := c.Visit(url)
		if err != nil {
			fmt.Println("Visit error:", err)
			return
		}
	}

	// 保存Excel文件
	err = file.Save("Top_250.xls")
	if err != nil {
		fmt.Printf("Error saving file: %s\n", err)
		return
	}

	fmt.Println("Excel file created successfully.")
}

在此代码中,我们首先创建了一个xlsx.File对象,并添加了一个名为 “Top 250” 的工作表。然后,我们添加了一个标题行,并遍历电影数据,将每部电影的信息添加到工作表中。最后,我们保存了 Excel 文件。

此程序执行以下步骤:

  1. 初始化一个Excel文件并添加一个工作表。
  2. 定义一个Movie结构体来存储电影信息。
  3. 使用colly包创建一个爬虫,并设置爬取规则。
  4. 遍历豆瓣Top 250电影的每一页,提取电影信息。
  5. 将提取的信息写入Excel工作表的每一行。
  6. 保存Excel文件。

此外,为了防止对豆瓣服务器造成过大压力,程序中设置了每次爬取 25 部电影,共爬取 10 页(250部电影)。根据需要,可以自行调节这些参数。

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;