---
title: "如何用 Golang 搭建网络数据抓取工具：分步教程"
description: "学习 Golang 数据抓取的基础与进阶方法，从搭建基础抓取工具到优化性能与效率。"
url: "https://marsproxies.cn/blog/golang-web-scraper/"
---

- [EN ](https://marsproxies.com/blog/golang-web-scraper/ "英语")
  [CN](https://marsproxies.cn/blog/golang-web-scraper/ "语言切换器")

[联系销售](https://marsproxies.cn/contact-sales/) [注册](https://dashboard.marsproxies.com/register/) [登录](https://dashboard.marsproxies.com/login/)

[联系销售](https://marsproxies.cn/contact-sales/) [注册](https://dashboard.marsproxies.com/register/) [登录](https://dashboard.marsproxies.com/login/)

[联系销售](https://marsproxies.cn/contact-sales/)

[登录](https://dashboard.marsproxies.com/login/) [注册](https://dashboard.marsproxies.com/register/)

- [EN ](https://marsproxies.com/blog/golang-web-scraper/ "英语")
  [CN](https://marsproxies.cn/blog/golang-web-scraper/ "语言切换器")

[返回博客](https://marsproxies.cn/blog/) 2024年10月29日

# 如何用 Golang 搭建网络数据抓取工具：分步教程

[操作指南](https://marsproxies.cn/blog/category/how-to/)

\-

目录

1. [什么是 Golang 网络数据抓取？](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%BB%80%E4%B9%88%E6%98%AF-golang-%E7%BD%91%E7%BB%9C%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96)
2. [Golang 网络数据抓取的使用场景](https://marsproxies.cn/blog/golang-web-scraper/#b-golang-%E7%BD%91%E7%BB%9C%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E7%9A%84%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)
3. [搭建 Go 数据抓取工具需要准备什么？](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%90%AD%E5%BB%BA-go-%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E5%B7%A5%E5%85%B7%E9%9C%80%E8%A6%81%E5%87%86%E5%A4%87%E4%BB%80%E4%B9%88)
4. [搭建 Golang 开发环境](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%90%AD%E5%BB%BA-golang-%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83)
5. [所需的库与依赖项](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%89%80%E9%9C%80%E7%9A%84%E5%BA%93%E4%B8%8E%E4%BE%9D%E8%B5%96%E9%A1%B9)
6. [如何用 Golang 搭建简单的数据抓取工具（分步教程）](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A6%82%E4%BD%95%E7%94%A8-golang-%E6%90%AD%E5%BB%BA%E7%AE%80%E5%8D%95%E7%9A%84%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E5%B7%A5%E5%85%B7%E5%88%86%E6%AD%A5%E6%95%99%E7%A8%8B)
7. [步骤 1：声明包](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4-1%E5%A3%B0%E6%98%8E%E5%8C%85)
8. [步骤 2：导入所需的包](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4-2%E5%AF%BC%E5%85%A5%E6%89%80%E9%9C%80%E7%9A%84%E5%8C%85)
9. [步骤 3：编写主函数](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4-3%E7%BC%96%E5%86%99%E4%B8%BB%E5%87%BD%E6%95%B0)
10. [修改 Go 代码以抓取多页数据](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%BF%AE%E6%94%B9-go-%E4%BB%A3%E7%A0%81%E4%BB%A5%E6%8A%93%E5%8F%96%E5%A4%9A%E9%A1%B5%E6%95%B0%E6%8D%AE)
11. [步骤一：定义基础 URL](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E4%B8%80%E5%AE%9A%E4%B9%89%E5%9F%BA%E7%A1%80-url)
12. [步骤二：修改 main 函数](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E4%BA%8C%E4%BF%AE%E6%94%B9-main-%E5%87%BD%E6%95%B0)
13. [步骤三：添加一个用于抓取单页的函数](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E4%B8%89%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E7%94%A8%E4%BA%8E%E6%8A%93%E5%8F%96%E5%8D%95%E9%A1%B5%E7%9A%84%E5%87%BD%E6%95%B0)
14. [步骤四：用于抓取多页的新代码](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E5%9B%9B%E7%94%A8%E4%BA%8E%E6%8A%93%E5%8F%96%E5%A4%9A%E9%A1%B5%E7%9A%84%E6%96%B0%E4%BB%A3%E7%A0%81)
15. [Golang 网络爬取进阶技巧](https://marsproxies.cn/blog/golang-web-scraper/#b-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E5%8F%96%E8%BF%9B%E9%98%B6%E6%8A%80%E5%B7%A7)
16. [处理大量使用 JavaScript 的网站](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A4%84%E7%90%86%E5%A4%A7%E9%87%8F%E4%BD%BF%E7%94%A8-javascript-%E7%9A%84%E7%BD%91%E7%AB%99)
17. [在 Golang 中处理 API 和 JSON 响应](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%9C%A8-golang-%E4%B8%AD%E5%A4%84%E7%90%86-api-%E5%92%8C-json-%E5%93%8D%E5%BA%94)
18. [规避检测：如何绕过数据抓取拦截](https://marsproxies.cn/blog/golang-web-scraper/#b-%E8%A7%84%E9%81%BF%E6%A3%80%E6%B5%8B%E5%A6%82%E4%BD%95%E7%BB%95%E8%BF%87%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E6%8B%A6%E6%88%AA)
19. [如何优化 Golang 网络爬虫以提升性能](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A6%82%E4%BD%95%E4%BC%98%E5%8C%96-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%E4%BB%A5%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD)
20. [使用 Goroutine 实现并发与并行](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%BD%BF%E7%94%A8-goroutine-%E5%AE%9E%E7%8E%B0%E5%B9%B6%E5%8F%91%E4%B8%8E%E5%B9%B6%E8%A1%8C)
21. [添加速率限制与休眠间隔](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%B7%BB%E5%8A%A0%E9%80%9F%E7%8E%87%E9%99%90%E5%88%B6%E4%B8%8E%E4%BC%91%E7%9C%A0%E9%97%B4%E9%9A%94)
22. [大规模数据抓取项目的内存管理](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A4%A7%E8%A7%84%E6%A8%A1%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E9%A1%B9%E7%9B%AE%E7%9A%84%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86)
23. [整合代码：优化后的 Go 爬虫程序](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%95%B4%E5%90%88%E4%BB%A3%E7%A0%81%E4%BC%98%E5%8C%96%E5%90%8E%E7%9A%84-go-%E7%88%AC%E8%99%AB%E7%A8%8B%E5%BA%8F)
24. [如何测试与维护您的 Golang 网络爬虫？](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A6%82%E4%BD%95%E6%B5%8B%E8%AF%95%E4%B8%8E%E7%BB%B4%E6%8A%A4%E6%82%A8%E7%9A%84-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB)
25. [为网络爬虫设置自动化测试](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%B8%BA%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%E8%AE%BE%E7%BD%AE%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95)
26. [设置定期抓取任务（使用 Cron 任务或任务计划程序）](https://marsproxies.cn/blog/golang-web-scraper/#b-%E8%AE%BE%E7%BD%AE%E5%AE%9A%E6%9C%9F%E6%8A%93%E5%8F%96%E4%BB%BB%E5%8A%A1%E4%BD%BF%E7%94%A8-cron-%E4%BB%BB%E5%8A%A1%E6%88%96%E4%BB%BB%E5%8A%A1%E8%AE%A1%E5%88%92%E7%A8%8B%E5%BA%8F)
27. [常见 Golang 网络爬虫库对比与使用场景](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%B8%B8%E8%A7%81-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%E5%BA%93%E5%AF%B9%E6%AF%94%E4%B8%8E%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)
28. [结语](https://marsproxies.cn/blog/golang-web-scraper/#b-%E7%BB%93%E8%AF%AD)

\-

当今世界日益依赖数据驱动决策，因此每位开发者都应当了解[网络数据抓取技术](https://marsproxies.com/blog/web-scraping-techniques/)，以便从互联网提取数据。本文将带您学习：

- 安装 Golang 开发环境，包括所需的库与依赖项。

* 从零开始搭建一个简单的 Go 数据抓取机器人。

- 运用进阶的 Golang 数据抓取技术。

* 优化抓取工具以实现更高性能。

- 测试并维护您的 Golang 数据抓取机器人。

马上开始吧！

## 什么是 Golang 网络数据抓取？

**网络数据抓取是指自动从网站提取数据的过程。**&#x50CF;[Walmart](https://marsproxies.cn/shopping-proxies/walmart-proxies/)这样的公司，必须确保其商品价格低于或至少持平于 Amazon 等竞争对手，才能保持竞争力。如果 Amazon 是您的抓取目标，可以参考我们的[Amazon 网络数据抓取指南](https://marsproxies.cn/blog/amazon-web-scraping/)，其中介绍了针对该平台的具体技术及需要应对的反爬虫挑战。不过，若要人工收集成千上万甚至数百万件商品的价格数据，将非常耗时。

为了获取这些信息，企业会使用**能够快速高效地从成千上万个商品页面自动提取数据的抓取工具**。这些工具依据预先编写的指令执行特定任务。

如**果您正考虑部署一款数据抓取工具，有两种选择：购买现成的抓取机器人，或自行开发。**&#x524D;者固然便捷，但您将因此失去定制化功能——而这些功能只有自主开发的抓取工具才能提供。

因此，更好的做法是学习如何自行搭建数据抓取工具。为此，**您需要使用一种编程语言**。可选项众多，各有优劣。[**Golang**](https://go.dev/)**就是不错的选择**，原因如下：

- **性能出色**

Golang 会被编译为机器码，因此比 Python 和[JavaScript](https://marsproxies.cn/blog/javascript-fetch-api/)等解释型语言运行速度更快。

- **原生支持并发**

Golang 通过 Goroutine 提供原生并发机制，让数据抓取机器人能够同时处理多项任务。

- **轻量且内存利用率高**

Golang 设计精简、内存管理高效，这有助于在处理大型数据集时减少[内存膨胀](https://scoutapm.com/blog/video-identifying-memory-bloat-and-monitoring-memory-usage/)问题。

- **标准库丰富**

Golang 自带功能完善的标准库，包括用于发送网络请求的 net/http、用于解析 HTML 的 html/template，以及用于处理 JSON 数据的 encoding/json。

如果您想知道 Golang 数据抓取方法能实现哪些功能，请继续阅读。

## Golang 网络数据抓取的使用场景

以下是 2024 年网络数据抓取的几种常见应用：

- **市场分析**

[网络数据抓取](https://marsproxies.cn/use-cases/web-scraping/)可以帮助企业提取竞争对手的定价、产品及整体营销策略信息，并据此调整自身策略以保持竞争力。

- **情感分析**

品牌声誉是决定企业成功与否的关键因素之一。企业会从 Trustpilot 等点评网站和 Instagram 等社交媒体平台提取客户评价与情感数据，并据此指导品牌建设工作。

- **获客线索挖掘**

数据抓取工具还能从 LinkedIn、Yelp 等各类商业名录中提取联系方式，企业可借助这些信息识别潜在客户、挖掘销售线索。

- **AI 模型数据挖掘**

数据抓取机器人还会收集训练数据，用于支撑 ChatGPT、Gemini 等主流聊天机器人背后大型 AI 模型的训练。

以上只是[网络数据抓取](https://marsproxies.cn/blog/is-web-scraping-legal/)助力各行各业企业的部分示例。只要拥有正确的数据，无论是训练 AI 模型实现任务自动化、收集市场情报，还是满足其他数据驱动需求，几乎都能实现。

## 搭建 Go 数据抓取工具需要准备什么？

读到这里，您应该已经明白为什么[网络数据抓取](https://marsproxies.cn/blog/best-web-scraping-languages/)如此重要——归根结底在于所提取数据的价值。**那么，该如何从零开始搭建一个 Go 数据抓取工具呢？**&#x8FD9;一过程比想象中简单，在开始之前，请确保已安装好所有必备环境。

### 搭建 Golang 开发环境

首先，请确认设备上已安装 Golang。如果尚未安装，请参考以下针对各主流操作系统的分步指南：

#### macOS

- **步骤 1：**&#x6253;开浏览器，访问[Go 官方网站](https://go.dev/dl/)，下载适用于 macOS 的 .pkg 安装包：

* **步骤 2：**&#x6253;开下载的文件，按照安装提示操作，点击“**继续**”以接受条款。

- **步骤 3：**&#x6839;据您使用的 shell 编辑其配置文件，以下是 bash 用户的代码：

```javascript
nano ~ / .bash_profile
```

- **步骤 4：**&#x7F16;辑 zsh 配置文件（使用 bash 的用户可跳过此步）：

```javascript
nano ~ / .zshrc
```

**步骤 5：**&#x5728;文件中添加以下这一行：

```javascript
export PATH =$PATH : /usr/local/go/bin
```

**步骤 6：**&#x6267;行以下代码以重新加载配置文件：

```javascript
source ~ / .zshrc // for zsh
```

```javascript
source ~ / .zshrc //for bash
```

**步骤 7：**&#x6267;行以下命令验证安装：

```javascript
go version
```

此时屏幕上应会显示已安装的 Go 版本号。

#### Windows

- **步骤 1：**&#x8BBF;问[Go 官方网站](https://go.dev/dl/)，下载如下图所示的 .msi 安装程序：

**步骤 2：**&#x6253;开下载的文件，接受许可协议，然后点击“下一步”。

**步骤 3：**&#x9009;择安装文件夹，点击“下一步”开始安装：

**步骤 4：**&#x6253;开命令提示符，输入以下命令以验证安装：

```javascript
go version
```

若安装成功，屏幕上会显示 Go 版本号。

#### Linux

Linux 用户可以通过官方二进制文件或包管理器两种方式安装 Go。

##### 方式一：使用官方二进制文件

- **步骤 1：**&#x8BBF;问[Go 官方网站](https://go.dev/dl/)，下载与您系统架构相匹配的 .tar.gz 文件：

* **步骤 2：**&#x6253;开终端，执行以下代码解压文件：

```javascript
sudo tar - C /usr/local -xzf go<version> .linux-amd64.tar.gz
```

- **步骤 3：**&#x5982;果使用 bash，请在 shell 配置文件中添加以下这一行以设置环境变量：

```javascript
nano ~ / .bashrc
```

- **步骤 4：**&#x7A;sh 用户请在 shell 配置文件中添加以下这一行（若已完成步骤 3 可跳过）：

```javascript
nano ~ / .zshrc
```

**步骤 5：**&#x5728;配置文件中添加以下命令：

```javascript
export PATH =$PATH : /usr/local/go/bin
```

- **步骤 6：**&#x91CD;新加载配置文件：

```javascript
source ~ / .bashrc  # Or use ~ / .zshrc if using zsh
```

- **步骤 7：**&#x6267;行以下命令验证安装：

```javascript
go version
```

屏幕上应会显示 Go 版本号。

##### 方式二：使用包管理器（Ubuntu/Debian）

- **步骤 1：**&#x6267;行以下命令更新软件包列表：

```javascript
sudo apt update
```

- **步骤 2：**&#x5B89;装 Go：

```javascript
sudo apt install golang-go
```

- **步骤 3：**&#x9A8C;证安装：

```javascript
go version
```

若安装成功，屏幕上会显示 Go 版本号。

### 所需的库与依赖项

设备上安装好 Go 之后，接下来需要配置数据抓取工具所需的库与依赖项，请先安装以下几个库：

- **Goquery**

这是一个功能强大的 Go 库，可以轻松处理和查询 HTML 文档，支持通过 CSS 选择器从网页中选取、筛选并提取信息。请在终端中执行以下命令安装 Goquery：

```javascript
go get github.com/PuerkitoBio/goquery
```

- **Colly**

Colly 是一个用 Go 编写的高层级数据抓取框架，能够简化发送请求、处理响应以及管理抓取流程的工作。请执行以下命令在系统中安装 Colly：

```javascript
go get github.com/gocolly/colly/v2
```

至此，您已在系统中安装好 Go 及基础依赖项，可以正式开始搭建 Golang 数据抓取工具了，下面进入本文的核心内容：

## 如何用 Golang 搭建简单的数据抓取工具（分步教程）

接下来我们将编写一个简单的数据抓取工具，从 books.toscrape.com 收集书名。需要说明的是，这是一个虚构的在线书店，专门为网络数据抓取爱好者提供练习场景。下面开始动手：

### 步骤 1：声明包

每个 Go 程序都必须以包声明开头，以下是声明包的代码：

```go
package main
```

### 步骤 2：导入所需的包

以下代码会导入抓取工具提取数据所需的各个包：

```javascript
import (
```

```javascript
    "fmt"
```

```javascript
    "log"
```

```javascript
    "net/http"
```

```javascript
    "github.com/PuerkitoBio/goquery"
```

```javascript
)
```

各部分含义如下：

- **fmt：**&#x63D0;供将输出打印到控制台的函数。

* **log：**&#x7528;于记录日志消息与错误信息。

- **net/http：**&#x63D0;供发送 HTTP 请求并接收响应的工具。

* **github.com/PuerkitoBio/goquery：**&#x4E00;个第三方库，提供类似 jQuery 的语法，使 HTML 解析更加简便。

### 步骤 3：编写主函数

main() 函数是 Golang 代码的入口，下面我们逐部分进行拆解：

首先，需要**声明主函数**，代码如下：

```go
func main ( ) {
```

接下来，需要**声明要抓取的目标 URL**：

```javascript
url : = "http://books.toscrape.com"
```

接下来，需要**让抓取机器人向该 URL 发送 HTTP 请求并获取响应**。如果程序遇到诸如网站宕机之类的错误，会自动终止运行并打印错误信息，代码如下：

```javascript
resp, err : = http. Get (url)
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Error fetching URL %s: %v" , url, err)
```

```javascript
    }
```

```javascript
    defer resp.Body. Close ( )
```

抓取机器人获取到 HTML 内容之后，需要**使用 goquery 文档读取该内容**。同样，如果该过程失败，机器人应终止程序并返回错误信息，代码如下：

```javascript
    doc, err : = goquery. NewDocumentFromReader (resp.Body)
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Error parsing HTML: %v" , err)
```

```javascript
    }
```

为了让输出更易于阅读，需要**打印“Book Titles”作为标题信息**，代码如下：

```go
    fmt. Println ( "Book Titles:" )
```

接下来，需要**编写自动选取书名并作为输出打印的代码部分**，以下代码会选取 h3 标签并打印其内容：

```javascript
    doc. Find ( ".product_pod h3 a" ) . Each ( func ( index int, item *goquery.Selection ) {
```

```javascript
        title, exists : = item. Attr ( "title" )
```

```javascript
        if exists {
```

```go
            fmt. Printf ( "Book %d: %s\n" , index+ 1 , title)
```

```javascript
        }
```

```javascript
    } )
```

最后，需要编写回调函数，以实现异步执行并打印书名：

```javascript
func ( index int, item *goquery.Selection ) {
```

```javascript
    title, exists : = item. Attr ( "title" )
```

```javascript
    if exists {
```

```go
        fmt. Printf ( "Book %d: %s\n" , index+ 1 , title)
```

```javascript
    }
```

```javascript
}
```

#### 步骤 4：整合完整代码

现在将各部分代码整合为完整脚本：

```go
package main
```

```javascript
import (
```

```javascript
    "fmt"
```

```javascript
    "log"
```

```javascript
    "net/http"
```

```javascript
    "github.com/PuerkitoBio/goquery"
```

```javascript
)
```

```go
func main ( ) {
```

```javascript
    url : = "http://books.toscrape.com"
```

```javascript
    // Send HTTP GET request
```

```javascript
    resp, err : = http. Get (url)
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Error fetching URL %s: %v" , url, err)
```

```javascript
    }
```

```javascript
    defer resp.Body. Close ( )
```

```javascript
    // Parse the HTML response using goquery
```

```javascript
    doc, err : = goquery. NewDocumentFromReader (resp.Body)
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Error parsing HTML: %v" , err)
```

```javascript
    }
```

```go
    fmt. Println ( "Book Titles:" )
```

```javascript
    // Find book titles inside <h3> tags
```

```javascript
    doc. Find ( ".product_pod h3 a" ) . Each ( func ( index int, item *goquery.Selection ) {
```

```javascript
        title, exists : = item. Attr ( "title" )
```

```javascript
        if exists {
```

```go
            fmt. Printf ( "Book %d: %s\n" , index+ 1 , title)
```

```javascript
        }
```

```javascript
    } )
```

```javascript
}
```

如果按照上述步骤正确操作，输出结果应如下所示，显示 Books to Scrape 网站上的书名：

## 修改 Go 代码以抓取多页数据

我们刚刚构建的数据抓取机器人只能从 Books to Scrape 的第一页采集书名。如果您想采集前五页的数据，就需要先定义基础 URL，并对上文步骤 3 中的 main 函数做一些修改。下面是具体的修改步骤：

### 步骤一：定义基础 URL

导入包之后（使用与之前相同的包），您需要定义一个基础 URL，机器人将在翻页时依据它构造下一页的 URL：

```javascript
const baseURL = "http://books.toscrape.com"
```

### 步骤二：修改 main 函数

您需要让 main 函数指示机器人从网站的第一页开始抓取，以下是新的 main 函数：

```go
func main ( ) {
```

```javascript
	scrapePage (baseURL, 1 ) // Start scraping from page 1
```

```javascript
}
```

### 步骤三：添加一个用于抓取单页的函数

接下来，您需要添加一个函数，用于先抓取单个 URL，再转到下一个，代码如下：

func scrapePage(url string, pageNumber int) {

```go
	fmt. Printf ( "\nScraping Page %d: %s\n" , pageNumber, url)
```

```javascript
	// Send HTTP request
```

```javascript
	resp, err : = http. Get (url)
```

```javascript
	if err != nil {
```

```javascript
		log. Fatalf ( "Error fetching URL %s: %v" , url, err)
```

```javascript
	}
```

```javascript
	defer resp.Body. Close ( )
```

```javascript
	// Parse the HTML response
```

```javascript
	doc, err : = goquery. NewDocumentFromReader (resp.Body)
```

```javascript
	if err != nil {
```

```javascript
		log. Fatalf ( "Error parsing HTML: %v" , err)
```

```javascript
	}
```

```javascript
	// Extract and print book titles for the current page
```

```javascript
	doc. Find ( ".product_pod h3 a" ) . Each ( func ( index int, item *goquery.Selection ) {
```

```javascript
		title, exists : = item. Attr ( "title" )
```

```javascript
		if exists {
```

```go
			fmt. Printf ( "Book %d: %s\n" , index+ 1 , title)
```

```javascript
		}
```

```javascript
	} )
```

```javascript
	// Find the link to the next page
```

```javascript
	nextPage, exists : = doc. Find ( ".next a" ) . Attr ( "href" )
```

```javascript
	if exists {
```

```javascript
		nextURL : = baseURL + "/" + nextPage // Build the full URL for the next page
```

```javascript
		scrapePage (nextURL, pageNumber+ 1 )   // Recursively scrape the next page with incremented page number
```

```javascript
	} else {
```

```go
		fmt. Println ( "\nNo more pages to scrape." )
```

```javascript
	}
```

```javascript
}
```

下面解释这段代码的含义：

- 该函数首先会**打印正在抓取的页码和该页的 URL**

* 接着它会**向该 URL 发送 HTTP 请求**，如果出现问题，就会记录[错误信息](https://marsproxies.cn/blog/parse-errors-python/)并终止执行

- 如果机器人成功获取了 HTML 内容，**就会使用 Goquery 解析该响应**

* 解析完成后，**会查找包含书名的特定 HTML 元素，并连同其索引一起打印出来**

- 完成一页抓取后，**机器人会通过查找 .next a 元素来寻找下一页的链接**，然后递归调用 scrapePage 函数抓取下一页

如果没有更多页面可抓取，机器人就会打印该消息并终止程序。

### 步骤四：用于抓取多页的新代码

把目前为止所做的修改合并起来，完整代码如下：

```go
package main
```

```javascript
import (
```

```javascript
    "fmt"
```

```javascript
    "log"
```

```javascript
    "net/http"
```

```javascript
    "github.com/PuerkitoBio/goquery"
```

```javascript
)
```

```javascript
// Base URL for the website
```

```javascript
const baseURL = "http://books.toscrape.com"
```

```go
func main ( ) {
```

```javascript
    scrapePage (baseURL, 1 ) // Start scraping from page 1
```

```javascript
}
```

```javascript
// Function to scrape a single page and handle pagination
```

```go
func scrapePage (url string , pageNumber int ) {
```

```go
    fmt. Printf ( "\nScraping Page %d: %s\n" , pageNumber, url)
```

```javascript
    // Send HTTP GET request
```

```javascript
    resp, err : = http. Get (url)
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Error fetching URL %s: %v" , url, err)
```

```javascript
    }
```

```javascript
    defer resp.Body. Close ( )
```

```javascript
    // Parse the HTML response
```

```javascript
    doc, err : = goquery. NewDocumentFromReader (resp.Body)
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Error parsing HTML: %v" , err)
```

```javascript
    }
```

```javascript
    // Extract and print book titles for the current page
```

```javascript
    doc. Find ( ".product_pod h3 a" ) . Each ( func ( index int, item *goquery.Selection ) {
```

```javascript
        title, exists : = item. Attr ( "title" )
```

```javascript
        if exists {
```

```go
            fmt. Printf ( "Book %d: %s\n" , index+ 1 , title)
```

```javascript
        }
```

```javascript
    } )
```

```javascript
    // Find the link to the next page
```

```javascript
    nextPage, exists : = doc. Find ( ".next a" ) . Attr ( "href" )
```

```javascript
    if exists {
```

```javascript
        nextURL : = baseURL + "/" + nextPage // Build the full URL for the next page
```

```javascript
        scrapePage (nextURL, pageNumber+ 1 )   // Recursively scrape the next page with incremented page number
```

```javascript
    } else {
```

```go
        fmt. Println ( "\nNo more pages to scrape." )
```

```javascript
    }
```

```javascript
}
```

运行这段 Go 数据抓取代码后，返回结果如下：

## Golang 网络爬取进阶技巧

到目前为止，您已经掌握了如何编写能够从 Books to Scrape 提取数据的基础数据抓取代码。接下来，我们将更进一步，介绍可以优化代码性能的具体步骤。

### 处理大量使用 JavaScript 的网站

您是否遇到过带有无限滚动的网站？**随着页面滚动，内容会实时更新，带来更快速、更流畅的用户体验**，例如[Twitter](https://marsproxies.cn/blog/twitter-scraping/)和 Facebook 等平台。这类网站使用 JavaScript 来渲染动态内容。

如果用上面的代码抓取这类网站，就会遇到问题，因为 net/http 库只能获取服务器最初加载的 HTML 内容。**这是一项挑战，因为 JavaScript 网站的服务器初始响应中往往不包含完整内容。**

那么，该如何用 Golang 从这些网站[抓取数据](https://marsproxies.cn/blog/how-to-scrape-data-from-a-website/)呢？

让我们通过采集一个 JavaScript 网站的数据来一探究竟，一个不错的例子是[Quotes to Scrape](https://quotes.toscrape.com/)，它是[Books to Scrape](https://books.toscrape.com/)中大量使用 JavaScript 的版本——下面我们一起开发一个机器人来采集该网站的数据：

#### 步骤一：安装 Chromedp

net/http 和 Goquery 等传统工具只能获取静态 HTML。**相比之下，Chromedp 的行为与真实浏览器一致：它会加载 JavaScript、完整渲染页面，并与按钮、滚动页面等动态元素进行交互**。使用以下命令在您的系统中安装 Chromedp：

```javascript
go get -u github.com/chromedp/chromedp
```

安装完成后，您就可以编写 Go 数据抓取代码来获取 Quotes to Scrape 网站的数据了，我们继续：

#### 步骤二：声明所需的包

使用以下命令声明机器人将要使用的包：

```go
package main
```

#### 步骤三：导入包

使用我们一直在用的相同的包：

```javascript
import (
```

```javascript
	"context"
```

```javascript
	"fmt"
```

```javascript
	"log"
```

```javascript
	"time"
```

```javascript
	"github.com/chromedp/chromedp"
```

```javascript
)
```

#### ```javascript
Step 4 : Write Your Main Function
```

```javascript
The main function for this scraper has several components. We will explain them below. Here is the code:
```

```go
func main ( ) {
```

```javascript
	// Create a context with timeout to manage the browser session
```

```javascript
	ctx, cancel : = chromedp. NewContext (context. Background ( ) )
```

```javascript
	defer cancel ( )
```

```javascript
	ctx, cancel = context. WithTimeout (ctx, 15 *time.Second)
```

```javascript
	defer cancel ( )
```

```javascript
	var bookTitles [ ]string
```

```javascript
	// Navigate to the target site and scrape book titles
```

```javascript
	err : = chromedp. Run (ctx,
```

```javascript
		chromedp. Navigate ( "https://quotes.toscrape.com/js/" ) , // Change to your dynamic page URL
```

```javascript
		chromedp. WaitVisible ( ".quote" , chromedp.ByQuery) ,     // Wait for elements to load
```

```javascript
		chromedp. Evaluate (`
```

```javascript
            [ ...document. querySelectorAll ( '.quote .text' ) ] . map ( el => el.innerText) ;
```

```javascript
        `, &bookTitles) , // Collect all book titles or quotes into bookTitles slice
```

```javascript
	)
```

```javascript
	if err != nil {
```

```javascript
		log. Fatalf ( "Failed to scrape the page: %v" , err)
```

```javascript
	}
```

```javascript
	// Print the collected titles
```

```go
	fmt. Println ( "Book Titles / Quotes:" )
```

```javascript
	for i, title : = range bookTitles {
```

```go
		fmt. Printf ( "Item %d: %s\n" , i+ 1 , title)
```

```javascript
	}
```

```javascript
}
```

下面解释这段代码的含义：

- **创建一个带超时的 context**

如果动态内容页面加载时间过长，就需要设置超时以防止抓取程序卡死，这正是 main 函数第一部分的作用。

- **使用 Chromedp 的 Navigate 打开网页**

接下来，我们使用 chromedp.Navigate() 打开一个无头浏览器会话（您的设备上可能会出现一个空白窗口）。

- **使用 WaitVisible 确保内容加载完成**

在处理大量使用 JavaScript 的网站时，内容通常不会一次性加载完成，因此我们必须等待目标内容完全加载后再尝试抓取。

- **用 JavaScript Evaluation 替代 Goquery 来提取数据**

在尝试采集数据之前，必须留出时间让 JavaScript 完成页面加载后 HTML 内容的获取。为此，我们使用 chromedp.Evaluate() 函数在浏览器会话中执行 JavaScript，在内容加载完成后将其提取出来。

如果执行过程中出现问题，机器人会停止运行并打印错误信息；如果成功，抓取程序则会打印采集到的条目标题。

#### 步骤五：运行完整代码

以下是从[Quotes to Scrape](https://quotes.toscrape.com/)采集 JavaScript 内容的完整代码：

```go
package main
```

```javascript
import (
```

```javascript
    "context"
```

```javascript
    "fmt"
```

```javascript
    "log"
```

```javascript
    "time"
```

```javascript
    "github.com/chromedp/chromedp"
```

```javascript
)
```

```go
func main ( ) {
```

```javascript
    // Create a context with timeout to manage the browser session
```

```javascript
    ctx, cancel : = chromedp. NewContext (context. Background ( ) )
```

```javascript
    defer cancel ( )
```

```javascript
    ctx, cancel = context. WithTimeout (ctx, 15 *time.Second)
```

```javascript
    defer cancel ( )
```

```javascript
    var bookTitles [ ]string
```

```javascript
    // Navigate to the target site and scrape book titles
```

```javascript
    err : = chromedp. Run (ctx,
```

```javascript
        chromedp. Navigate ( "https://quotes.toscrape.com/js/" ) , // Change to your dynamic page URL
```

```javascript
        chromedp. WaitVisible ( ".quote" , chromedp.ByQuery) , // Wait for elements to load
```

```javascript
        chromedp. Evaluate (`
```

```javascript
            [ ...document. querySelectorAll ( '.quote .text' ) ] . map ( el => el.innerText) ;
```

```javascript
        `, &bookTitles) , // Collect all book titles or quotes into bookTitles slice
```

```javascript
    )
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Failed to scrape the page: %v" , err)
```

```javascript
    }
```

```javascript
    // Print the collected titles
```

```go
    fmt. Println ( "Book Titles / Quotes:" )
```

```javascript
    for i, title : = range bookTitles {
```

```go
        fmt. Printf ( "Item %d: %s\n" , i+ 1 , title)
```

```javascript
    }
```

```javascript
}
```

如果一切顺利，您应该会得到如下响应：

### 在 Golang 中处理 API 和 JSON 响应

一些网站提供 API，让用户可以请求特定数据。一个不错的例子是 Twitter（几乎没人真的叫它 X）：您可以通过 Twitter 的 API 获取推文、用户信息和热门趋势，不过近年来此类信息的访问方式已有所变化。**无论如何，从提供 JSON 响应的 API 中抓取数据效率更高，因为无需解析 HTML 内容。**&#x540C;时，由于大多数 API 都有公开的服务条款来规范使用方式，涉及法律风险的可能性也更低。

**让我们修改数据抓取脚本，从**[**JSONPlaceholder API**](https://jsonplaceholder.typicode.com/)采集数据，该 API 提供用于练习 API 数据提取方法的模拟数据，其中包含六类常用资源可供使用：

- **帖子（100 条）：**&#x68;ttps\://jsonplaceholder.typicode.com/posts

* **评论（500 条）：**&#x68;ttps\://jsonplaceholder.typicode.com/comments

- **相册（100 个）：**&#x68;ttps\://jsonplaceholder.typicode.com/albums

* **照片（5000 张）：**&#x68;ttps\://jsonplaceholder.typicode.com/photos

- **待办事项（200 条）：** https\://jsonplaceholder.typicode.com/todos

* **用户（10 个）：**&#x68;ttps\://jsonplaceholder.typicode.com/users

具体如何操作呢？让我们编写代码，从 /posts 中获取前 10 篇博客文章的标题。

#### 步骤一：使用以下命令声明 main 包

```go
package main
```

#### 步骤二：使用以下代码导入所需的包

i

```javascript
mport (
```

```javascript
    "encoding/json"
```

```javascript
    "fmt"
```

```javascript
    "log"
```

```javascript
    "net/http"
```

```javascript
)
```

下面解释这段代码的含义：

- encoding/json：处理**JSON 数据**与 Go 结构体之间的相互转换

* **fmt**：提供 Printf、Println 等函数，用于将文本打印到控制台

- **log**：用于**记录错误**和调试信息

* **net/http：**&#x53D1;送**HTTP** **请求**并获取 API 的响应

#### 步骤三：定义 Post 结构体

使用以下代码定义 Post 结构体：

```javascript
type Post struct {
```

```javascript
    UserID int    ` json:"userId" `
```

```javascript
    ID     int    ` json:"id" `
```

```javascript
    Title  string ` json:"title" `
```

```javascript
    Body   string ` json:"body" `
```

```javascript
}
```

该结构体用于对应 API 返回的每条帖子的结构，其中 UserID、ID 等字段名对应 API 响应中的 JSON 键。

#### 步骤四：编写 main 函数

main 函数由以下几个部分组成：

```go
func main ( ) {
```

```javascript
	url : = "https://jsonplaceholder.typicode.com/posts"
```

```javascript
	resp, err : = http. Get (url)
```

```javascript
	if err != nil {
```

```javascript
		log. Fatalf ( "Error fetching posts: %v" , err)
```

```javascript
	}
```

```javascript
	defer resp.Body. Close ( )
```

```javascript
	var posts [ ]Post
```

```javascript
	if err : = json. NewDecoder (resp.Body) . Decode ( &posts) ; err != nil {
```

```javascript
		log. Fatalf ( "Error decoding JSON: %v" , err)
```

```javascript
	}
```

```go
	fmt. Println ( "First 10 Blog Post Titles:" )
```

```javascript
	for i : = 0 ; i < 10 ; i++ {
```

```go
		fmt. Printf ( "%d. %s\n" , i+ 1 , posts[i] .Title)
```

```javascript
	}
```

```javascript
}
```

让我们仔细看看这个 main 函数：

- 它首先定义了我们要采集数据的 URL 端点。

* 接着向该端点发送 HTTP GET 请求，如果请求失败，就会中止逻辑并打印错误信息。

- 然后机器人将提取到的 JSON 数据解码为结构体，并依次打印前 10 篇博客文章的标题。

#### 步骤五：整合所有代码

以下是 JSON API 抓取程序的完整代码：

```go
package main
```

```javascript
import (
```

```javascript
    "encoding/json"
```

```javascript
    "fmt"
```

```javascript
    "log"
```

```javascript
    "net/http"
```

```javascript
)
```

```javascript
// Struct to map the JSON structure of each post
```

```javascript
type Post struct {
```

```javascript
    UserID int    ` json:"userId" `
```

```javascript
    ID     int    ` json:"id" `
```

```javascript
    Title  string ` json:"title" `
```

```javascript
    Body   string ` json:"body" `
```

```javascript
}
```

```go
func main ( ) {
```

```javascript
    url : = "https://jsonplaceholder.typicode.com/posts"
```

```javascript
    resp, err : = http. Get (url)
```

```javascript
    if err != nil {
```

```javascript
        log. Fatalf ( "Error fetching posts: %v" , err)
```

```javascript
    }
```

```javascript
    defer resp.Body. Close ( )
```

```javascript
    var posts [ ]Post
```

```javascript
    if err : = json. NewDecoder (resp.Body) . Decode ( &posts) ; err != nil {
```

```javascript
        log. Fatalf ( "Error decoding JSON: %v" , err)
```

```javascript
    }
```

```go
    fmt. Println ( "First 10 Blog Post Titles:" )
```

```javascript
    for i : = 0 ; i < 10 ; i++ {
```

```go
        fmt. Printf ( "%d. %s\n" , i+ 1 , posts[i] .Title)
```

```javascript
    }
```

```javascript
}
```

运行这段代码后，应该会打印出前 10 篇博客标题的列表，如下所示：

### 规避检测：如何绕过数据抓取拦截

到这一步，您已经可以算是初步掌握了网络爬取技术。**您已经学会了如何用 Golang 从传统 HTML 网站、使用 JavaScript 的动态网站，以及提供 JSON 格式数据的 API 中提取静态数据。**

但在将这些知识付诸实践之前，有一点您需要了解：**大多数网站对数据抓取程序都持谨慎态度**。即使 robots.txt 文件允许抓取，当某个 IP 发送过多抓取请求时，网站仍可能对其进行封禁或限制。

要解决这个问题，可以使用一个[住宅代理](https://marsproxies.cn/proxies/residential-proxies/)池，让 Golang 抓取程序在其中轮换发送数据提取请求。接下来，我们修改之前用于从 JSON API 的 Posts 资源获取前 10 个标题的代码，回到您的代码并按以下步骤修改：

#### 修改步骤三：导入用于代理轮换的额外库

需要额外导入的包如下：

```javascript
import (
```

```javascript
	"encoding/json"
```

```javascript
	"fmt"
```

```javascript
	"log"
```

```javascript
	"net/http"
```

```javascript
	"net/url"
```

```javascript
	"strings"
```

```javascript
	"time"
```

```javascript
)
```

- **net/url：**&#x7528;于解析和处理 URL

* **strings：**&#x7528;于字符串操作

- **time：**&#x7528;于设置超时和速率限制

#### 新增步骤四：添加代理列表

步骤三中定义 Post 结构体的部分保持不变。不过，在声明 main 函数之前，您需要先添加一个代理列表变量，以便将请求通过新的 IP 池进行转发，您可以自由地将列表中的内容替换为自己的代理 IP：

```javascript
var proxies = [ ]string{
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=2n39i3ia&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=7ric72rl&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=jvtuogsy&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=po3xt1w7&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=88gijk21&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=wes8szpl&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=ntsyd9ux&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=5myzrqnk&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=80ow4rs7&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=i7f4cx3y&_lifetime=30m" ,
```

```javascript
}
```

#### 新增步骤五：定义一个用于创建带代理的 HTTP 客户端的函数

使用以下代码创建该函数：

```go
func createProxyClient (proxyURL string ) ( *http.Client, string ) {
```

```javascript
	parsedURL, err : = url. Parse (proxyURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Fatalf ( "Failed to parse proxy URL %s: %v" , proxyURL, err)
```

```javascript
	}
```

```javascript
	proxyIP : = strings. Split (parsedURL.Host, ":" ) [ 0 ]
```

```javascript
	transport : = &http.Transport{
```

```typescript
		Proxy: http. ProxyURL (parsedURL) ,
```

```javascript
	}
```

```javascript
	return &http.Client{
```

```typescript
		Transport: transport,
```

```javascript
		Timeout :   15 * time.Second,
```

```javascript
	} , proxyIP
```

```javascript
}
```

由于我们希望每个请求都通过[不同的 IP 地址](https://marsproxies.cn/blog/generate-random-ip-address/)发送，这个函数会通过创建 HTTP 客户端来确保代理生效

#### 新增步骤六：创建一个用于抓取单个 URL 的函数

接下来，您需要编写一个用于抓取单个 URL 的函数，代码如下：

```javascript
// Function to scrape a single URL
```

```go
func scrapeURL (targetURL string , proxy string ) {
```

```javascript
	client, proxyIP : = createProxyClient (proxy) // Create an HTTP client with the proxy
```

```javascript
	resp, err : = client. Get (targetURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Printf ( "Failed to fetch %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```javascript
	defer resp.Body. Close ( )
```

```javascript
	var post Post
```

```javascript
	if err : = json. NewDecoder (resp.Body) . Decode ( &post) ; err != nil {
```

```javascript
		log. Printf ( "Error decoding JSON from %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
}
```

**该函数会使用我们创建的 HTTP 客户端及其对应的代理 IP 抓取目标 URL。**&#x7136;后将 JSON 响应解码为 Post 结构体，并连同所使用的代理一起打印出标题。

#### 新增步骤七（原步骤四）：修改 main 函数

此前，main 函数只会获取博客文章的标题，并对前 10 篇文章重复这一提取过程。**现在，我们希望借助代理，让每个请求都使用从步骤四定义的代理池中随机选取的不同 IP 地址发送。**&#x4F7F;用以下代码：

```go
func main ( ) {
```

```javascript
	urls : = [ ]string{
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/1" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/2" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/3" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/4" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/5" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/6" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/7" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/8" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/9" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/10" ,
```

```javascript
	}
```

```javascript
// Scrape each URL using a different proxy
```

```javascript
	for i, url : = range urls {
```

```javascript
		proxy : = proxies[i% len (proxies) ] // Rotate proxies
```

```javascript
		scrapeURL (url, proxy)            // Scrape the URL
```

```javascript
	}
```

```go
	fmt. Println ( "All requests completed." )
```

```javascript
}
```

这段代码首先列出要抓取的 URL，然后通过调用步骤五和步骤六中定义的函数，让请求在步骤四定义的代理池中的不同 IP 之间轮换。

#### 步骤八：运行完整的修改后代码

以下是需要运行的完整修改后代码：

```go
package main
```

```javascript
import (
```

```javascript
	"encoding/json"
```

```javascript
	"fmt"
```

```javascript
	"log"
```

```javascript
	"net/http"
```

```javascript
	"net/url"
```

```javascript
	"strings"
```

```javascript
	"time"
```

```javascript
)
```

```javascript
// Struct to map the API JSON response
```

```javascript
type Post struct {
```

```javascript
	UserID int    ` json:"userId" `
```

```javascript
	ID     int    ` json:"id" `
```

```javascript
	Title  string ` json:"title" `
```

```javascript
	Body   string ` json:"body" `
```

```javascript
}
```

```javascript
// List of proxies with username-password authentication
```

```javascript
var proxies = [ ]string{
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=2n39i3ia&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=7ric72rl&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=jvtuogsy&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=po3xt1w7&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=88gijk21&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=wes8szpl&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=ntsyd9ux&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=5myzrqnk&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=80ow4rs7&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=i7f4cx3y&_lifetime=30m" ,
```

```javascript
}
```

```javascript
// Function to create an HTTP client with a proxy
```

```go
func createProxyClient (proxyURL string ) ( *http.Client, string ) {
```

```javascript
	parsedURL, err : = url. Parse (proxyURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Fatalf ( "Failed to parse proxy URL %s: %v" , proxyURL, err)
```

```javascript
	}
```

```javascript
	// Extract the proxy IP from the parsed URL's Host
```

```javascript
	proxyIP : = strings. Split (parsedURL.Host, ":" ) [ 0 ]
```

```javascript
	transport : = &http.Transport{
```

```typescript
		Proxy: http. ProxyURL (parsedURL) ,
```

```javascript
	}
```

```javascript
	return &http.Client{
```

```typescript
		Transport: transport,
```

```javascript
		Timeout :   15 * time.Second,
```

```javascript
	} , proxyIP
```

```javascript
}
```

```javascript
// Scrape a URL using the proxy-enabled client
```

```javascript
// Function to scrape a single URL
```

```go
func scrapeURL (targetURL string , proxy string ) {
```

```javascript
	client, proxyIP : = createProxyClient (proxy) // Create an HTTP client with the proxy
```

```javascript
	resp, err : = client. Get (targetURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Printf ( "Failed to fetch %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```javascript
	defer resp.Body. Close ( )
```

```javascript
	var post Post
```

```javascript
	if err : = json. NewDecoder (resp.Body) . Decode ( &post) ; err != nil {
```

```javascript
		log. Printf ( "Error decoding JSON from %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```go
	fmt. Printf ( "Fetched post %d: %s (via proxy: %s)\n" , post.ID, post.Title, proxyIP)
```

```javascript
}
```

```go
func main ( ) {
```

```javascript
	urls : = [ ]string{
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/1" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/2" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/3" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/4" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/5" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/6" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/7" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/8" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/9" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/10" ,
```

```javascript
	}
```

```javascript
	for i, url : = range urls {
```

```javascript
		proxy : = proxies[i% len (proxies) ] // Rotate proxies
```

```javascript
		scrapeURL (url, proxy)            // Scrape the URL
```

```javascript
	}
```

```go
	fmt. Println ( "All requests completed." )
```

```javascript
}
```

以下是预期输出：

让你的 Golang 爬虫在 2026 年不再被封禁

[获取住宅代理](https://dashboard.marsproxies.com/products/ultra-residential-proxies#buy-now)

## 如何优化 Golang 网络爬虫以提升性能

现在，您已能创建 Golang 数据抓取程序，根据可用资源从网站或 API 中提取静态或动态数据。接下来让我们更进一步，看看如何提升爬虫的性能。具体来说，我们将对之前用于从 JSON Placeholder API 抓取数据的代码进行三项重要升级：

- **使用 Goroutine 实现并发与并行：**&#x63D0;升速度与效率。

* **速率限制与休眠间隔：**&#x63A7;制请求频率，避免服务器过载并被封禁。

- **内存管理：**&#x9AD8;效处理大规模数据集。

下面我们来逐一介绍。

### 使用 Goroutine 实现并发与并行

要借助 Goroutine 提升爬虫的速度与效率，您需要执行以下操作：

- **修改第 3 步以导入 Sync 包**

在 Golang 中，Goroutine 可让您并发执行函数。要使用它，首先需要在第 3 步中导入 sync 包。以下是第 3 步修改后的代码：

```javascript
import (
```

```javascript
	"encoding/json"
```

```javascript
	"fmt"
```

```javascript
	"log"
```

```javascript
	"net/http"
```

```javascript
	"net/url"
```

```javascript
	"strings"
```

```javascript
	"sync"  // Added sync package
```

```javascript
	"time"
```

```javascript
)
```

- **修改第 6 步，添加一个 WaitGroup 参数以跟踪所有 Goroutine 的完成情况**

以下是修改后的函数：

```javascript
// Add WaitGroup as a parameter to track completion of all Goroutines
```

```go
func scrapeURL (targetURL string , proxy string , wg *sync.WaitGroup) {
```

```javascript
	defer wg. Done ( ) // Ensure the WaitGroup counter is decremented
```

```javascript
	client, proxyIP : = createProxyClient (proxy)
```

```javascript
	resp, err : = client. Get (targetURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Printf ( "Failed to fetch %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```javascript
	defer resp.Body. Close ( )
```

```javascript
	var post Post
```

```javascript
	if err : = json. NewDecoder (resp.Body) . Decode ( &post) ; err != nil {
```

```javascript
		log. Printf ( "Error decoding JSON from %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```go
	fmt. Printf ( "Fetched post %d: %s (via proxy: %s)\n" , post.ID, post.Title, proxyIP)
```

```javascript
}
```

### 添加速率限制与休眠间隔

添加速率限制与休眠间隔可确保目标服务器不会过载，并使爬虫的行为更接近真人以避免被检测。升级代码的方法如下：

- **修改第 3 步以添加新包**

您需要导入 math/rand 包以实现随机休眠间隔。以下是第 3 步修改后的代码：

```go
package main
```

```javascript
import (
```

```javascript
	"encoding/json"
```

```javascript
	"fmt"
```

```javascript
	"log"
```

```javascript
	"math/rand" // For random sleep intervals
```

```javascript
	"net/http"
```

```javascript
	"net/url"
```

```javascript
	"strings"
```

```javascript
	"sync"
```

```javascript
	"time"
```

- **修改第 5 步，创建一个每两秒发送一次请求的通道**

每个 Goroutine 在发送请求前都会等待两秒。在同一代码块中加入随机等待时间，以使行为更接近真实用户，如下方代码所示：

```javascript
var limiter = time. Tick ( 2 * time.Second) // 1 request every 2 seconds
```

```go
func scrapeURL (targetURL string , proxy string , wg *sync.WaitGroup) {
```

```javascript
    defer wg. Done ( )
```

```javascript
    < -limiter // Wait for the tick to proceed
```

```javascript
    client, proxyIP : = createProxyClient (proxy)
```

```javascript
    resp, err : = client. Get (targetURL)
```

```javascript
    if err != nil {
```

```javascript
        log. Printf ( "Failed to fetch %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
        return
```

```javascript
    }
```

```javascript
    defer resp.Body. Close ( )
```

```javascript
    var post Post
```

```javascript
    if err : = json. NewDecoder (resp.Body) . Decode ( &post) ; err != nil {
```

```javascript
        log. Printf ( "Error decoding JSON from %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
        return
```

```javascript
    }
```

```go
    fmt. Printf ( "Fetched post %d: %s (via proxy: %s)\n" , post.ID, post.Title, proxyIP)
```

```javascript
    // Random sleep interval between 1-3 seconds
```

```javascript
    time. Sleep (time. Duration ( 1 + rand. Intn ( 3 ) ) * time.Second)
```

```javascript
}
```

### 大规模数据抓取项目的内存管理

在处理大规模数据集时，应使用带缓冲的通道来临时存储采集到的信息，以实现内存管理。您需要修改第 5 步，**创建一个带缓冲的通道来存储结果，并将采集到的信息发送到该缓冲区。**

```javascript
// Create a rate limiter to enforce delay between requests
```

```javascript
var limiter = time. Tick ( 2 * time.Second) // 1 request every 2 seconds
```

```javascript
// Buffered channel to collect scraping results
```

```javascript
var results = make (chan Post, 10 ) // Store up to 10 posts
```

```javascript
// Function to scrape a URL and send the result to the buffered channel
```

```go
func scrapeURL (targetURL string , proxy string , wg *sync.WaitGroup) {
```

```javascript
	defer wg. Done ( ) // Decrement WaitGroup counter when done
```

```javascript
	< -limiter // Wait for the tick to proceed
```

```javascript
	client, proxyIP : = createProxyClient (proxy)
```

```javascript
	resp, err : = client. Get (targetURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Printf ( "Failed to fetch %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```javascript
	defer resp.Body. Close ( )
```

```javascript
	var post Post
```

```javascript
	if err : = json. NewDecoder (resp.Body) . Decode ( &post) ; err != nil {
```

```javascript
		log. Printf ( "Error decoding JSON from %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```javascript
	// Send the result to the buffered channel
```

```javascript
	results < - post
```

```go
	fmt. Printf ( "Fetched post %d: %s (via proxy: %s)\n" , post.ID, post.Title, proxyIP)
```

```javascript
	// Random sleep interval to simulate human behavior (1-3 seconds)
```

```javascript
	time. Sleep (time. Duration ( 1 +rand. Intn ( 3 ) ) * time.Second)
```

```javascript
}
```

### 整合代码：优化后的 Go 爬虫程序

完成我们讨论过的所有改动后，您的爬虫脚本应如下所示：

```go
package main
```

```javascript
import (
```

```javascript
	"encoding/json"
```

```javascript
	"fmt"
```

```javascript
	"log"
```

```javascript
	"math/rand" // For random sleep intervals
```

```javascript
	"net/http"
```

```javascript
	"net/url"
```

```javascript
	"strings"
```

```javascript
	"sync"
```

```javascript
	"time"
```

```javascript
)
```

```javascript
// Struct to map the API JSON response
```

```javascript
type Post struct {
```

```javascript
	UserID int    ` json:"userId" `
```

```javascript
	ID     int    ` json:"id" `
```

```javascript
	Title  string ` json:"title" `
```

```javascript
	Body   string ` json:"body" `
```

```javascript
}
```

```javascript
// List of proxies with username-password authentication
```

```javascript
var proxies = [ ]string{
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=2n39i3ia&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=7ric72rl&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=jvtuogsy&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=po3xt1w7&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=88gijk21&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=wes8szpl&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=ntsyd9ux&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=5myzrqnk&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=80ow4rs7&_lifetime=30m" ,
```

```typescript
	"http://mr45184psO8:MKRYlpZgSa@ultra.marsproxies.com:44443?_country=us&_state=alaska&_session=i7f4cx3y&_lifetime=30m" ,
```

```javascript
}
```

```javascript
// Create a rate limiter to enforce delay between requests
```

```javascript
var limiter = time. Tick ( 2 * time.Second) // 1 request every 2 seconds
```

```javascript
// Buffered channel to collect scraping results
```

```javascript
var results = make (chan Post, 10 ) // Store up to 10 posts
```

```javascript
// Function to create an HTTP client with a proxy
```

```go
func createProxyClient (proxyURL string ) ( *http.Client, string ) {
```

```javascript
	parsedURL, err : = url. Parse (proxyURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Fatalf ( "Failed to parse proxy URL %s: %v" , proxyURL, err)
```

```javascript
	}
```

```javascript
	proxyIP : = strings. Split (parsedURL.Host, ":" ) [ 0 ]
```

```javascript
	transport : = &http.Transport{
```

```typescript
		Proxy: http. ProxyURL (parsedURL) ,
```

```javascript
	}
```

```javascript
	return &http.Client{
```

```typescript
		Transport: transport,
```

```javascript
		Timeout :   15 * time.Second,
```

```javascript
	} , proxyIP
```

```javascript
}
```

```javascript
// Function to scrape a URL and send the result to the buffered channel
```

```go
func scrapeURL (targetURL string , proxy string , wg *sync.WaitGroup) {
```

```javascript
	defer wg. Done ( ) // Decrement WaitGroup counter when done
```

```javascript
	< -limiter // Wait for the tick to proceed
```

```javascript
	client, proxyIP : = createProxyClient (proxy)
```

```javascript
	resp, err : = client. Get (targetURL)
```

```javascript
	if err != nil {
```

```javascript
		log. Printf ( "Failed to fetch %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```javascript
	defer resp.Body. Close ( )
```

```javascript
	var post Post
```

```javascript
	if err : = json. NewDecoder (resp.Body) . Decode ( &post) ; err != nil {
```

```javascript
		log. Printf ( "Error decoding JSON from %s using proxy %s: %v" , targetURL, proxyIP, err)
```

```javascript
		return
```

```javascript
	}
```

```javascript
	// Send the result to the buffered channel
```

```javascript
	results < - post
```

```go
	fmt. Printf ( "Fetched post %d: %s (via proxy: %s)\n" , post.ID, post.Title, proxyIP)
```

```javascript
	// Random sleep interval to simulate human behavior (1-3 seconds)
```

```javascript
	time. Sleep (time. Duration ( 1 +rand. Intn ( 3 ) ) * time.Second)
```

```javascript
}
```

```go
func main ( ) {
```

```javascript
	// URLs to scrape
```

```javascript
	urls : = [ ]string{
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/1" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/2" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/3" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/4" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/5" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/6" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/7" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/8" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/9" ,
```

```javascript
		"https://jsonplaceholder.typicode.com/posts/10" ,
```

```javascript
	}
```

```javascript
	var wg sync.WaitGroup // WaitGroup to manage Goroutines
```

```javascript
	for i, url : = range urls {
```

```javascript
		proxy : = proxies[i% len (proxies) ] // Rotate proxies
```

```javascript
		wg. Add ( 1 ) // Increment WaitGroup counter
```

```javascript
		go scrapeURL (url, proxy, &wg) // Launch a Goroutine for each URL
```

```javascript
	}
```

```javascript
	// Launch a Goroutine to close the results channel after all requests complete
```

```javascript
	go func ( ) {
```

```javascript
		wg. Wait ( ) // Wait for all Goroutines to complete
```

```javascript
		close (results) // Close the channel when done
```

```javascript
	} ( )
```

```javascript
	// Process and print results from the buffered channel
```

```javascript
	for post : = range results {
```

```go
		fmt. Printf ( "Processed post %d: %s\n" , post.ID, post.Title)
```

```javascript
	}
```

```go
	fmt. Println ( "All requests completed." )
```

```javascript
}
```

以下是预期输出：

从返回结果的随机性可以看出，该爬虫能够同时处理多个请求。但为了避免被检测，它会在发送下一个请求前等待两分钟。为增强内存管理能力，采集到的数据会存储在一个临时的缓冲通道中。

## 如何测试与维护您的 Golang 网络爬虫？

现在您已拥有一个能够绕过大多数检测机制的升级版爬虫。即便做了这些改动，仍有许多方法可以进一步提升爬虫的效率，例如在网站更改 HTML 结构时及早发现错误，以及设置定期抓取任务。

### 为网络爬虫设置自动化测试

网站经常会更改其 HTML 结构，这可能会打乱数据抓取的流程。为缓解这一问题，您应设置自动化测试，确保爬虫按预期运行。以下是几种可用的测试类型：

- **单元测试：**&#x6D4B;试单个函数的功能，例如 URL 解析和 JSON 配置。

* **集成测试：**&#x786E;认爬虫能够连接到真实或模拟网站。

- **模拟服务器：**&#x4F7F;用模拟 HTTP 服务器来模拟网站响应。

### 设置定期抓取任务（使用 Cron 任务或任务计划程序）

除了自动化测试，实际的数据采集过程同样可以实现自动化。对于 Linux/Mac 设备用户，可以使用 Cron 任务按特定时间间隔调度爬虫运行。示例如下：

```javascript
crontab -e
```

```javascript
# Run scraper every day at midnight
```

```javascript
0 0 * * * /usr/local/go/bin/go run /path/to/scraper.go
```

Windows 用户可以使用任务计划程序在特定时间运行爬虫。

## 常见 Golang 网络爬虫库对比与使用场景

## 结语

希望本文为您提供了有关如何用 Golang 构建网络爬虫的实用技巧。正如您所见，您既可以构建一个简单的爬虫来提取静态 HTML 数据，也可以进一步升级以采集动态 JavaScript 数据，还可以通过轮换代理来规避检测。

要提升 Go 网络爬虫的性能，您可以实现并发与并行以提高效率、实现速率限制以避免被检测、并进行内存管理以节省资源。

最后，您还可以设置自动化测试，并安排爬虫在指定时间自动采集数据，无需人工干预。您也已经了解了三种常见网络爬虫库的对比，以及各自适用的场景。祝您挖掘更多数据！

[返回博客](https://marsproxies.cn/blog/)

\-

作者

[Dalius Pamedytis 软件工程师](https://marsproxies.cn/blog/authors/dalius-pamedytis/)

目录

1. [什么是 Golang 网络数据抓取？](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%BB%80%E4%B9%88%E6%98%AF-golang-%E7%BD%91%E7%BB%9C%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96)
2. [Golang 网络数据抓取的使用场景](https://marsproxies.cn/blog/golang-web-scraper/#b-golang-%E7%BD%91%E7%BB%9C%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E7%9A%84%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)
3. [搭建 Go 数据抓取工具需要准备什么？](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%90%AD%E5%BB%BA-go-%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E5%B7%A5%E5%85%B7%E9%9C%80%E8%A6%81%E5%87%86%E5%A4%87%E4%BB%80%E4%B9%88)
4. [搭建 Golang 开发环境](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%90%AD%E5%BB%BA-golang-%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83)
5. [所需的库与依赖项](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%89%80%E9%9C%80%E7%9A%84%E5%BA%93%E4%B8%8E%E4%BE%9D%E8%B5%96%E9%A1%B9)
6. [如何用 Golang 搭建简单的数据抓取工具（分步教程）](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A6%82%E4%BD%95%E7%94%A8-golang-%E6%90%AD%E5%BB%BA%E7%AE%80%E5%8D%95%E7%9A%84%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E5%B7%A5%E5%85%B7%E5%88%86%E6%AD%A5%E6%95%99%E7%A8%8B)
7. [步骤 1：声明包](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4-1%E5%A3%B0%E6%98%8E%E5%8C%85)
8. [步骤 2：导入所需的包](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4-2%E5%AF%BC%E5%85%A5%E6%89%80%E9%9C%80%E7%9A%84%E5%8C%85)
9. [步骤 3：编写主函数](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4-3%E7%BC%96%E5%86%99%E4%B8%BB%E5%87%BD%E6%95%B0)
10. [修改 Go 代码以抓取多页数据](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%BF%AE%E6%94%B9-go-%E4%BB%A3%E7%A0%81%E4%BB%A5%E6%8A%93%E5%8F%96%E5%A4%9A%E9%A1%B5%E6%95%B0%E6%8D%AE)
11. [步骤一：定义基础 URL](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E4%B8%80%E5%AE%9A%E4%B9%89%E5%9F%BA%E7%A1%80-url)
12. [步骤二：修改 main 函数](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E4%BA%8C%E4%BF%AE%E6%94%B9-main-%E5%87%BD%E6%95%B0)
13. [步骤三：添加一个用于抓取单页的函数](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E4%B8%89%E6%B7%BB%E5%8A%A0%E4%B8%80%E4%B8%AA%E7%94%A8%E4%BA%8E%E6%8A%93%E5%8F%96%E5%8D%95%E9%A1%B5%E7%9A%84%E5%87%BD%E6%95%B0)
14. [步骤四：用于抓取多页的新代码](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%AD%A5%E9%AA%A4%E5%9B%9B%E7%94%A8%E4%BA%8E%E6%8A%93%E5%8F%96%E5%A4%9A%E9%A1%B5%E7%9A%84%E6%96%B0%E4%BB%A3%E7%A0%81)
15. [Golang 网络爬取进阶技巧](https://marsproxies.cn/blog/golang-web-scraper/#b-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E5%8F%96%E8%BF%9B%E9%98%B6%E6%8A%80%E5%B7%A7)
16. [处理大量使用 JavaScript 的网站](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A4%84%E7%90%86%E5%A4%A7%E9%87%8F%E4%BD%BF%E7%94%A8-javascript-%E7%9A%84%E7%BD%91%E7%AB%99)
17. [在 Golang 中处理 API 和 JSON 响应](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%9C%A8-golang-%E4%B8%AD%E5%A4%84%E7%90%86-api-%E5%92%8C-json-%E5%93%8D%E5%BA%94)
18. [规避检测：如何绕过数据抓取拦截](https://marsproxies.cn/blog/golang-web-scraper/#b-%E8%A7%84%E9%81%BF%E6%A3%80%E6%B5%8B%E5%A6%82%E4%BD%95%E7%BB%95%E8%BF%87%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E6%8B%A6%E6%88%AA)
19. [如何优化 Golang 网络爬虫以提升性能](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A6%82%E4%BD%95%E4%BC%98%E5%8C%96-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%E4%BB%A5%E6%8F%90%E5%8D%87%E6%80%A7%E8%83%BD)
20. [使用 Goroutine 实现并发与并行](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%BD%BF%E7%94%A8-goroutine-%E5%AE%9E%E7%8E%B0%E5%B9%B6%E5%8F%91%E4%B8%8E%E5%B9%B6%E8%A1%8C)
21. [添加速率限制与休眠间隔](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%B7%BB%E5%8A%A0%E9%80%9F%E7%8E%87%E9%99%90%E5%88%B6%E4%B8%8E%E4%BC%91%E7%9C%A0%E9%97%B4%E9%9A%94)
22. [大规模数据抓取项目的内存管理](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A4%A7%E8%A7%84%E6%A8%A1%E6%95%B0%E6%8D%AE%E6%8A%93%E5%8F%96%E9%A1%B9%E7%9B%AE%E7%9A%84%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86)
23. [整合代码：优化后的 Go 爬虫程序](https://marsproxies.cn/blog/golang-web-scraper/#b-%E6%95%B4%E5%90%88%E4%BB%A3%E7%A0%81%E4%BC%98%E5%8C%96%E5%90%8E%E7%9A%84-go-%E7%88%AC%E8%99%AB%E7%A8%8B%E5%BA%8F)
24. [如何测试与维护您的 Golang 网络爬虫？](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%A6%82%E4%BD%95%E6%B5%8B%E8%AF%95%E4%B8%8E%E7%BB%B4%E6%8A%A4%E6%82%A8%E7%9A%84-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB)
25. [为网络爬虫设置自动化测试](https://marsproxies.cn/blog/golang-web-scraper/#b-%E4%B8%BA%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%E8%AE%BE%E7%BD%AE%E8%87%AA%E5%8A%A8%E5%8C%96%E6%B5%8B%E8%AF%95)
26. [设置定期抓取任务（使用 Cron 任务或任务计划程序）](https://marsproxies.cn/blog/golang-web-scraper/#b-%E8%AE%BE%E7%BD%AE%E5%AE%9A%E6%9C%9F%E6%8A%93%E5%8F%96%E4%BB%BB%E5%8A%A1%E4%BD%BF%E7%94%A8-cron-%E4%BB%BB%E5%8A%A1%E6%88%96%E4%BB%BB%E5%8A%A1%E8%AE%A1%E5%88%92%E7%A8%8B%E5%BA%8F)
27. [常见 Golang 网络爬虫库对比与使用场景](https://marsproxies.cn/blog/golang-web-scraper/#b-%E5%B8%B8%E8%A7%81-golang-%E7%BD%91%E7%BB%9C%E7%88%AC%E8%99%AB%E5%BA%93%E5%AF%B9%E6%AF%94%E4%B8%8E%E4%BD%BF%E7%94%A8%E5%9C%BA%E6%99%AF)
28. [结语](https://marsproxies.cn/blog/golang-web-scraper/#b-%E7%BB%93%E8%AF%AD)

分类

- [产品动态](https://marsproxies.cn/blog/category/product-news/)

  [5](https://marsproxies.cn/blog/category/product-news/)
- [产品对比](https://marsproxies.cn/blog/category/comparisons/)

  [10](https://marsproxies.cn/blog/category/comparisons/)
- [反检测解决方案](https://marsproxies.cn/blog/category/antidetect-solution/)

  [3](https://marsproxies.cn/blog/category/antidetect-solution/)
- [操作指南](https://marsproxies.cn/blog/category/how-to/)

  [123](https://marsproxies.cn/blog/category/how-to/)
- [球鞋抢购机器人](https://marsproxies.cn/blog/category/sneaker-bots/)

  [2](https://marsproxies.cn/blog/category/sneaker-bots/)
- [资讯](https://marsproxies.cn/blog/category/insights/)

  [89](https://marsproxies.cn/blog/category/insights/)

## 相关文章

[资讯](https://marsproxies.cn/blog/category/insights/) [操作指南](https://marsproxies.cn/blog/category/how-to/)

### [OnlyFans 数据抓取：公开数据采集实用指南](https://marsproxies.cn/blog/onlyfans-scraping-the-essential-guide-to-public-data-collection/)

了解如何安全使用 OnlyFans 数据抓取工具，探索适合的工具与方法，以及为什么代理对 2026 年抓取 OnlyFans 数据至关重要。

[Dalius Pamedytis 软件工程师](https://marsproxies.cn/blog/authors/dalius-pamedytis/)

2026年1月22日
