因为最近工作量有点大,都是反复的做那几个事情,正好之前有用GO语言写的爬虫,现在稍微修改下,然后实现高并发实现快速抓取数据,因为有些属于商业机密,我就写成一个通用的模版以供大家参考。

下面是一个使用Go语言编写的高并发爬虫程序模板,采用工作池模式实现高效并发处理,并包含美观的UI界面展示爬取结果。
go
package main
import (
"fmt"
"io"
"net/http"
"sync"
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/data/binding"
"fyne.io/fyne/v2/widget"
)
// 爬虫任务结构体
type CrawlTask struct {
URL string
}
// 爬取结果结构体
type CrawlResult struct {
URL string
Status int
Title string
Size int
Duration time.Duration
Error error
}
func main() {
// 创建Fyne应用
myApp := app.New()
myWindow := myApp.NewWindow("Go高并发爬虫")
myWindow.Resize(fyne.NewSize(800, 600))
// 创建数据绑定
urls := binding.NewStringList()
results := binding.NewStringList()
activeWorkers := binding.NewInt()
completedTasks := binding.NewInt()
failedTasks := binding.NewInt()
totalSize := binding.NewInt()
// 初始化数据
urls.Set([]string{
"https://golang.org",
"https://golang.org/pkg",
"https://golang.org/pkg/net/http/",
"https://golang.org/doc",
"https://golang.org/blog",
"https://golang.org/pkg/fmt/",
"https://golang.org/pkg/os/",
"https://golang.org/pkg/encoding/json/",
})
activeWorkers.Set(0)
completedTasks.Set(0)
failedTasks.Set(0)
totalSize.Set(0)
// 创建UI组件
title := widget.NewLabelWithStyle("Go高并发爬虫", fyne.TextAlignCenter, fyne.TextStyle{Bold: true})
urlList := widget.NewListWithData(urls,
func() fyne.CanvasObject {
return widget.NewLabel("template")
},
func(i binding.DataItem, o fyne.CanvasObject) {
o.(*widget.Label).Bind(i.(binding.String))
})
addUrlEntry := widget.NewEntry()
addUrlEntry.SetPlaceHolder("输入要爬取的URL...")
addButton := widget.NewButton("添加URL", func() {
if addUrlEntry.Text != "" {
current, _ := urls.Get()
urls.Set(append(current, addUrlEntry.Text))
addUrlEntry.SetText("")
}
})
statusGrid := container.NewGridWithColumns(4,
widget.NewLabel("活跃工作协程:"),
widget.NewLabelWithData(binding.IntToString(activeWorkers)),
widget.NewLabel("已完成任务:"),
widget.NewLabelWithData(binding.IntToString(completedTasks)),
widget.NewLabel("失败任务:"),
widget.NewLabelWithData(binding.IntToString(failedTasks)),
widget.NewLabel("总下载量:"),
widget.NewLabelWithData(binding.IntToStringWithFormat(totalSize, "%.0f KB")),
)
resultList := widget.NewListWithData(results,
func() fyne.CanvasObject {
return widget.NewLabel("result template")
},
func(i binding.DataItem, o fyne.CanvasObject) {
o.(*widget.Label).Bind(i.(binding.String))
})
// 创建控制按钮
startButton := widget.NewButton("开始爬取", nil)
stopButton := widget.NewButton("停止", nil)
clearButton := widget.NewButton("清空结果", func() {
results.Set([]string{})
completedTasks.Set(0)
failedTasks.Set(0)
totalSize.Set(0)
})
// 创建任务通道和控制通道
taskQueue := make(chan CrawlTask, 100)
resultChan := make(chan CrawlResult, 100)
stopChan := make(chan struct{})
var wg sync.WaitGroup
// 更新开始按钮功能
startButton.OnTapped = func() {
// 重置统计
completedTasks.Set(0)
failedTasks.Set(0)
totalSize.Set(0)
results.Set([]string{})
// 获取URL列表
urlList, _ := urls.Get()
// 启动工作池
workerCount := 5
for i := 0; i < workerCount; i++ {
wg.Add(1)
go worker(taskQueue, resultChan, stopChan, &wg, activeWorkers)
}
// 添加任务到队列
go func() {
for _, u := range urlList {
taskQueue <- CrawlTask{URL: u}
}
close(taskQueue)
}()
// 处理结果
go func() {
for result := range resultChan {
// 更新统计信息
if result.Error != nil {
failedTasks.Set(failedTasks.Get() + 1)
} else {
completedTasks.Set(completedTasks.Get() + 1)
totalSize.Set(totalSize.Get() + result.Size/1024) // 转换为KB
}
// 添加结果到列表
current, _ := results.Get()
var status string
if result.Error != nil {
status = fmt.Sprintf("失败: %s", result.Error)
} else {
status = fmt.Sprintf("成功: %d 字节 (耗时: %v)", result.Size, result.Duration)
}
newResult := fmt.Sprintf("[%s] %s", result.URL, status)
results.Set(append(current, newResult))
}
}()
// 等待所有工作完成
go func() {
wg.Wait()
close(resultChan)
}()
}
// 停止按钮功能
stopButton.OnTapped = func() {
close(stopChan)
}
// 布局界面
topBar := container.NewHBox(addUrlEntry, addButton)
controlBar := container.NewHBox(startButton, stopButton, clearButton)
content := container.NewBorder(
container.NewVBox(title, topBar, statusGrid, controlBar),
nil,
nil,
nil,
container.NewHSplit(
container.NewBorder(widget.NewLabel("待爬取URL列表"), nil, nil, nil, urlList),
container.NewBorder(widget.NewLabel("爬取结果"), nil, nil, nil, resultList),
),
)
myWindow.SetContent(content)
myWindow.ShowAndRun()
}
// 工作协程函数
func worker(tasks <-chan CrawlTask, results chan<- CrawlResult, stopCh <-chan struct{}, wg *sync.WaitGroup, active binding.Int) {
defer wg.Done()
active.Set(active.Get() + 1)
defer func() {
active.Set(active.Get() - 1)
}()
for task := range tasks {
select {
case <-stopCh:
return
default:
start := time.Now()
// 执行HTTP请求
resp, err := http.Get(task.URL)
if err != nil {
results <- CrawlResult{
URL: task.URL,
Error: err,
}
continue
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
results <- CrawlResult{
URL: task.URL,
Error: err,
}
continue
}
// 创建结果
duration := time.Since(start)
results <- CrawlResult{
URL: task.URL,
Status: resp.StatusCode,
Size: len(body),
Duration: duration,
}
}
}
}
程序功能说明
这个Go语言爬虫程序具有以下特点:
1、高并发处理:
- 使用工作池模式(worker pool)管理并发
- 通过通道(channel)实现任务队列
- 使用sync.WaitGroup等待所有任务完成
2、用户友好界面:
- 使用Fyne框架构建跨平台GUI
- 实时显示爬取进度和结果
- 清晰的统计信息展示
3、核心功能:
- 添加/管理待爬取的URL列表
- 启动/停止爬取过程
- 显示每个URL的爬取结果(状态、大小、耗时)
- 实时统计:活跃工作协程、已完成任务、失败任务、总下载量
4、错误处理:
- 网络请求错误处理
- 响应读取错误处理
- 通过停止通道实现优雅停止
使用说明
- 在顶部输入框中输入要爬取的URL,点击"添加URL"按钮
- 点击"开始爬取"按钮启动爬虫
- 右侧结果面板将实时显示爬取进度
- 统计信息区域显示当前状态
- 可随时点击"停止"按钮终止爬取过程
- 点击"清空结果"可重置统计信息和结果列表
运行要求
运行此程序需要安装以下依赖:
arduino
go get fyne.io/fyne/v2
此程序可在Windows、macOS和Linux上运行,并自动适配本地主题。
扩展建议
1、增加深度爬取功能(从页面中提取新链接)
2、添加请求延迟控制,避免对目标服务器造成过大压力
3、实现更复杂的结果解析(如提取标题、关键词等)
4、添加结果导出功能
5、实现更精细的错误分类和统计
这个模板提供了高并发爬虫的核心架构,根据自己项目要求可以进一步扩展功能,如果遇到技术上的问题,欢迎随时留言讨论。