0. 前言
Go 语言,也被称为Golang
,是由Google
开发的一种开源编程语言,它在 2009 年首次发布,并在 2012 年正式开源。Go 语言被设计用来简化大型软件的开发,特别注重并发编程和内存安全。
0.1 特点
- 静态类型:Go 是静态类型语言,这意味着类型在编译时已经确定,有助于在编译阶段捕捉错误。
- 编译型语言:Go 语言编写的程序会被编译成机器码,直接运行在硬件上,因此执行效率较高。
- 并发编程:Go 内置了
goroutine
(轻量级线程)和channel
(用于goroutine
之间通信的管道),使得并发编程变得简单。 - 内存安全:Go 语言通过垃圾回收(GC)机制来自动管理内存,减少了内存泄露的风险。
- 简洁的语法:Go 语言的语法简洁明了,易于学习和理解。
- 工具链:Go 语言拥有一套完整的工具链,包括但不限于格式化工具
gofmt
、包管理工具go get
等。 - 跨平台:Go 语言支持跨平台编译,可以轻松地编译为不同操作系统和架构的二进制文件。
0.2 优势
- 高效的并发处理:通过 goroutine 和 channel,Go 语言使得并发编程变得简单而高效。
- 快速编译:Go 语言的编译速度非常快,这使得开发周期缩短,提高了开发效率。
- 易于维护:Go 语言的代码结构清晰,易于维护和重构。
- 强大的标准库:Go 语言提供了丰富的标准库,覆盖网络、I/O、数据处理等多种功能。
- 内存安全:通过垃圾回收机制,减少了内存泄露和指针错误的风险。
- 跨平台:可以很容易地为不同的操作系统和架构编译 Go 程序。
0.3 缺点
- 垃圾回收:虽然垃圾回收机制减少了内存管理的负担,但有时候 GC 的不可预测性可能会影响性能。
- 错误处理:Go语言的错误处理机制(通过返回错误值)与传统try-catch不同,有时会导致代码中错误处理逻辑过于冗长。
- 包管理:虽然 Go 的包管理工具在不断改进,但在早期版本中,模块化的支持并不完善。
- 泛型支持:直到 Go 1.18 版本才引入了泛型,之前版本的 Go 语言缺少泛型支持,这在某些场景下限制了代码的复用。
- 社区和生态系统:虽然 Go 社区活跃,但与一些老牌编程语言相比,其生态系统和第三方库可能还不够丰富。官方社区
- 类型系统: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
这些命令将显示GOPATH
和GOROOT
环境变量的值。如果它们被正确设置,那么你应该能看到它们的路径。
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 的goroutine
和channel
实现并发控制,提高爬取效率。
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 文件。
此程序执行以下步骤:
- 初始化一个Excel文件并添加一个工作表。
- 定义一个
Movie
结构体来存储电影信息。 - 使用
colly
包创建一个爬虫,并设置爬取规则。 - 遍历豆瓣Top 250电影的每一页,提取电影信息。
- 将提取的信息写入Excel工作表的每一行。
- 保存Excel文件。
此外,为了防止对豆瓣服务器造成过大压力,程序中设置了每次爬取 25 部电影,共爬取 10 页(250部电影)。根据需要,可以自行调节这些参数。