如何使用Golang实现并发网络爬虫_Golang goroutine与HTTP请求技巧
技术百科
P粉602998670
发布时间:2026-01-21
浏览: 次 Go并发爬虫关键在于控并发、防崩、防封;需用带缓冲channel实现信号量限流,归一化URL并用sync.Map去重,限制响应体大小并确保resp.Body.Close()。
Go 语言实现并发爬虫的关键不在“能不能并发”,而在于“怎么控并发、怎么防崩、怎么不被封”。盲目开成百上千个 goroutine 发 http.Get,大概率触发连接耗尽、DNS 超时、服务端限流或本地文件描述符不足(too many open files)。
用 semaphore 控制并发请求数量
别靠 time.Sleep 或空 for 循环压节奏。标准做法是用带缓冲的 channel 模拟信号量,限制同时活跃的 HTTP 请求数量。
常见错误:直接对每个 URL 启一个 go fetch(url),没节制 —— 1000 个 URL 就起 1000 个 goroutine,底层 TCP 连接、DNS 查询、TLS 握手全堆在一起,系统先扛不住。
- 设一个
sem := make(chan struct{}, 10),表示最多 10 个并发请求 - 每次发请求前写入:
sem - 请求结束(无论成功失败)后必须释放:
- 这个 channel 不要 close,也不用 defer —— 它是长期复用的资源
http.Client 必须复用并配置超时
每个 http.Client 实例自带连接池;反复 new http.Client 会导致连接泄漏、TIME_WAIT 爆满、DNS 缓存失效。
默认的 http.DefaultClient 虽可用,但超时为 0(无限等待),极易卡死整个 goroutine。
- 定义全局或包级变量:
var client = &http.Client{Timeout: 10 * time.Second} - 设置
Transport复用连接:client.Transport = &http.Transport{ MaxIdleConns: 100, MaxIdleConnsPerHost: 100, IdleConnTimeout: 30 * time.Second, } - 不要在 handler 或循环里反复 new
http.Client,哪怕只改 Timeout
解析 HTML 前先检查 resp.StatusCode 和 Content-Type
很多爬虫一拿到 *http.Response 就直接丢给 golang.org/x/net/html 解析,结果遇到 404 页面、JSON 接口、重定向响应、二进制文件(PDF/图片),轻则 panic(invalid character),重则静默跳过关键错误。
- 务必检查:
if resp.StatusCode = 300 - 检查 Content-Type 是否含
text/html或application/xhtml+xml,否则跳过解析 - 用
io.LimitReader(resp.Body, 1024*1024)防止下载超大响应体(如视频页面嵌了 100MB 日志文件) - 记得
resp.Body.Close()—— 不关会泄漏连接,尤其在复用 client 时
URL 去重与避免重复抓取要用 sync.Map + 归一化
原始 URL 可能带不同 query 参数(?utm_source=xx)、大小写路径、末尾斜杠差异,直接字符串比较会导致重复抓取或漏抓。
并发环境下用普通 map[st 会 panic,必须线程安全。
- 用
var visited = sync.Map{}存已抓 URL(key 是归一化后的字符串) - 归一化至少做三件事:转小写、移除 fragment(# 后内容)、标准化 query(按 key 排序再拼)
- 判断是否已访问:
if _, ok := visited.Load(normalizedURL); ok { continue } - 成功解析后存入:
visited.Store(normalizedURL, struct{}{})
真正难的不是并发本身,而是当 50 个 goroutine 同时在解析、去重、写磁盘、重试 429 响应时,哪条路径没加锁、哪个 error 被忽略、哪个 body 忘了 close —— 这些细节才决定爬虫跑一天后是稳如磐石,还是凌晨三点开始疯狂报 dial tcp: lookup xxx: no such host。
# 它是
# 网络爬虫
# 最多
# 爬虫
# 能带
# 跳过
# 自带
# 三点
# 不被
# 要用
# app
# 复用
# http
# go
# golang
# dns
# 循环
# Error
# 并发
# 堆
# String
# if
# html
# xml
# 字符串
# 并发请求
# 线程
# var
# Struct
# map
# channel
# for
# bool
# continue
# 信号量
# xhtml
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Django密码修改后会话失效的解决方案
- Win11文件扩展名怎么显示_Win11查看文件后
- Go 中 := 短变量声明的类型推导机制详解
- VSC怎么创建PHP项目_从零开始搭建项目的步骤【
- Win11怎么恢复旧版开始菜单_通过软件还原Win
- Windows 11无法安全删除U盘提示设备正在使
- Windows10如何删除Windows.old_
- 如何使用Golang捕获测试日志_Golang t
- php接口返回数据乱码怎么办_php接口调试编码问
- php删除数据怎么加限制_带where条件删除避免
- c++输入输出流 c++ cin与cout格式化输
- Windows11怎样开启游戏模式_Windows
- 如何在Golang中验证模块完整性_Golangg
- 为什么本地php环境运行php脚本卡顿_php执行
- Windows11怎么自定义任务栏_Windows
- MySQL 中使用 IF 和 CASE 实现查询字
- LINUX如何开放防火墙端口_Linux fire
- Win11怎么设置默认输入法 Win11固定中文输
- Win11 C盘满了怎么清理 Win11磁盘清理和
- 如何使用Golang sync.Map实现并发安全
- Win10系统字体模糊怎么办_Windows10高
- 如何使用Golang写入二进制文件_Golang
- Win11怎么开启移动热点_Windows11共享
- MAC如何快速搜索大文件_MAC磁盘空间分析与冗余
- Win10怎样设置多显示器_Win10多显示器扩展
- c++如何实现一个高性能的环形队列(Ring Bu
- Windows服务启动类型恢复方法_错误修改导致的
- Win11怎么更改盘符_Win11磁盘管理修改驱动
- PyTorch DDP 多进程训练在 Kaggle
- Win11系统更新后黑屏怎么办 Win11更新黑屏
- Win11如何设置ipv6 Win11开启IPv6
- Mac如何修改Hosts文件?(本地开发与屏蔽网站
- 如何用正则与预处理高效拦截带干扰符的恶意域名
- Win11怎么关闭搜索历史 Win11清除搜索框最
- 用lighttpd能运行php吗_lighttpd
- Win11怎么关闭系统声音_Win11系统提示音静
- 如何使用 Selenium 正确获取篮球参考网站球
- php本地部署后session无法保存_sessi
- Windows10如何更改任务栏高度_Win10解
- 如何使用Golang实现负载均衡_分发请求到多个服
- 如何在 Go 中正确初始化结构体中的 map 字段
- 如何使用Golang实现云原生应用弹性伸缩_自动应
- php485能和物联网模块通信吗_php485对接
- Windows10怎么备份注册表_Windows1
- c++怎么设置线程优先级与cpu亲和性_c++ 多
- Win10 BitLocker加密教程 Win10
- c# 在高并发场景下,委托和接口调用的性能对比
- PHP cURL GET请求:正确设置认证与自定义
- 如何在 Go 同包不同文件中正确引用结构体
- Win11怎么设置默认邮件应用_Windows11

QQ客服