当前位置: 首页 > news >正文

使用 chromedp 高效爬取 Bing 搜索结果

在数据采集领域,搜索引擎结果是重要的信息来源。但传统爬虫面对现代浏览器渲染的页面时,常因 JavaScript 动态加载、跳转链接加密等问题束手无策。本文将详细介绍如何使用 Go 语言的chromedp库,模拟真实浏览器行为爬取 Bing 搜索结果,并破解其跳转链接加密,最终获取真实目标地址。

一、需求背景与技术选型

1.1 爬取搜索引擎结果的痛点

在尝试获取 Bing 搜索结果时,我们会遇到两个核心问题:

  • 动态渲染障碍:Bing 搜索结果页通过 JavaScript 动态加载内容,传统基于http.Client的爬虫无法获取完整 DOM 结构
  • 跳转链接加密:搜索结果中的链接并非真实地址,而是经过 Base64 编码的 Bing 跳转链接(如https://www.bing.com/ck/a?!...&u=...

1.2 为何选择 chromedp?

chromedp是一个基于 Chrome DevTools Protocol(CDP)的 Go 语言库,相比其他方案有明显优势:

  • 真实浏览器环境:直接控制 Chrome/Chromium 浏览器,完美处理 JS 动态渲染
  • 无需额外依赖:无需安装 Selenium 或 ChromeDriver,简化部署流程
  • 强类型 API:基于 Go 语言的类型安全特性,减少运行时错误
  • 灵活的上下文控制:支持页面导航、元素等待、JS 执行等完整浏览器操作

二、环境准备

2.1 基础依赖

  • Go 1.18+(推荐使用最新稳定版)
  • Chrome/Chromium 浏览器(确保版本与 chromedp 兼容)
  • 依赖库安装:
go get github.com/chromedp/chromedp

2.2 核心配置说明

在代码初始化阶段,我们需要配置浏览器运行参数:

opts := append(chromedp.DefaultExecAllocatorOptions[:],chromedp.Flag("ignore-certificate-errors", true), // 忽略证书错误chromedp.Flag("headless", true),                  // 无头模式(生产环境推荐)
)
  • 无头模式(headless):不显示浏览器窗口,适合服务器环境运行,设为false可用于调试
  • 证书错误忽略:避免因 HTTPS 证书问题导致的爬取失败

三、核心功能实现

3.1 破解 Bing 跳转链接:unwrapBingURL 函数

Bing 搜索结果中的链接格式通常为:
https://www.bing.com/ck/a?!...&u=a1aHR0cHM6Ly93d3cuZ29vZ2xlLmNvbQ==
其中u参数即为加密后的真实地址,解密步骤如下:

  1. 解析 URL 参数:提取u参数值
  2. 去除前缀标识:Bing 会在 Base64 字符串前添加a1前缀,需先移除
  3. Base64 URL 解码:使用 URL 安全的 Base64 解码算法还原真实地址

实现代码:

func unwrapBingURL(bing string) (real string, err error) {u, err := url.Parse(bing)if err != nil {return "", err}// 提取u参数(加密的真实地址)enc := u.Query().Get("u")if enc == "" {return bing, nil // 非跳转链接,直接返回}// 移除Bing添加的a1前缀if strings.HasPrefix(enc, "a1") {enc = enc[2:]}// Base64 URL解码dst := make([]byte, base64.URLEncoding.DecodedLen(len(enc)))n, err := base64.URLEncoding.Decode(dst, []byte(enc))if err != nil {return "", err}return string(dst[:n]), nil
}

3.2 分页爬取与去重机制

为获取更多搜索结果并避免重复,我们设计了分页爬取与去重逻辑:

3.2.1 分页控制

Bing 通过first参数控制分页(first=1为第 1 页,first=11为第 2 页,以此类推),实现代码:

pageSize := 10 // 每页预期结果数
for pageIndex := 0; len(unique) < maxResults; pageIndex++ {start := pageIndex*pageSize + 1searchURL := fmt.Sprintf("https://www.bing.com/search?q=%s&first=%d",url.QueryEscape(keyword), start)// 爬取当前页...
}
3.2.2 结果去重

使用map存储已获取的真实链接,确保最终结果唯一:

seen := make(map[string]bool)   // 记录已发现的链接
var unique []string             // 存储去重后的真实链接// 去重逻辑
if !seen[real] {seen[real] = trueunique = append(unique, real)newCount++
}

3.3 页面元素提取

通过chromedp.Evaluate执行 JavaScript 代码,提取搜索结果中的链接:

var rawLinks []string
if err := chromedp.Run(ctx,chromedp.Navigate(searchURL),                  // 导航到搜索页面chromedp.WaitVisible(`#b_content`, chromedp.ByID), // 等待结果区域加载完成chromedp.Sleep(2*time.Second),                 // 额外等待,确保JS渲染完成// 提取h2标题下的链接chromedp.Evaluate(`Array.from(document.querySelectorAll('#b_content h2 a')).map(a => a.href)`, &rawLinks),
); err != nil {log.Printf("第 %d 页加载失败: %v", pageIndex+1, err)break
}
  • WaitVisible:等待结果容器#b_content可见,避免过早提取导致数据缺失
  • Sleep 延迟:应对 Bing 的动态加载机制,确保结果完全渲染
  • JS 选择器:通过#b_content h2 a精准定位搜索结果链接

四、完整代码解析

4.1 代码结构总览

整个程序分为三个核心部分:

  1. unwrapBingURL:解密 Bing 跳转链接
  2. main 函数初始化:配置 chromedp 上下文、初始化去重容器
  3. 分页爬取循环:控制分页、提取链接、去重存储

4.2 关键细节说明

  • 上下文管理:使用defer cancel()确保资源释放,避免内存泄漏
allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancel()ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()
  • 异常处理

    • 捕获页面加载错误,避免程序崩溃
    • 过滤无效链接(空链接、Microsoft 官方链接)
    • 处理 Base64 解码失败的情况
  • 终止条件

    • 已获取足够数量的结果(达到maxResults
    • 当前页无新结果(newCount == 0),说明已爬取所有结果

五、完整代码

package mainimport ("context""encoding/base64""fmt""log""net/url""strings""time""github.com/chromedp/chromedp"
)// 从 Bing 跳转链中提取真实地址
func unwrapBingURL(bing string) (real string, err error) {u, err := url.Parse(bing)if err != nil {return "", err}// 取 u= 参数enc := u.Query().Get("u")if enc == "" {return bing, nil // 不是跳转链,原样返回}// 去掉前缀if strings.HasPrefix(enc, "a1") {enc = enc[2:]}// base64 解码dst := make([]byte, base64.URLEncoding.DecodedLen(len(enc)))n, err := base64.URLEncoding.Decode(dst, []byte(enc))if err != nil {return "", err}return string(dst[:n]), nil
}func main() {keyword := "印度大幅下调消费税应对经济压力"maxResults := 100 // 你想拿多少条opts := append(chromedp.DefaultExecAllocatorOptions[:],chromedp.Flag("ignore-certificate-errors", true),chromedp.Flag("headless", true), // 调试可改 false)allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), opts...)defer cancel()ctx, cancel := chromedp.NewContext(allocCtx)defer cancel()seen := make(map[string]bool)var unique []stringpageSize := 10for pageIndex := 0; len(unique) < maxResults; pageIndex++ {start := pageIndex*pageSize + 1searchURL := fmt.Sprintf("https://www.bing.com/search?q=%s&first=%d",url.QueryEscape(keyword), start)var rawLinks []stringif err := chromedp.Run(ctx,chromedp.Navigate(searchURL),chromedp.WaitVisible(`#b_content`, chromedp.ByID),chromedp.Sleep(2*time.Second),chromedp.Evaluate(`Array.from(document.querySelectorAll('#b_content h2 a')).map(a => a.href)`, &rawLinks),); err != nil {log.Printf("第 %d 页加载失败: %v", pageIndex+1, err)break}newCount := 0for _, l := range rawLinks {if l == "" || strings.Contains(l, "go.microsoft.com") {continue}real, err := unwrapBingURL(l)if err != nil || real == "" {continue}if !seen[real] {seen[real] = trueunique = append(unique, real)newCount++}if len(unique) >= maxResults {break}}if newCount == 0 { // 没新结果就停break}}fmt.Printf("共拿到 %d 条真实链接:\n", len(unique))for i, u := range unique {fmt.Printf("%2d. %s\n", i+1, u)}
}

六、优化建议与注意事项

6.1 性能优化

  1. 调整 Sleep 时间:2 秒等待可能过长,可根据网络情况调整为 1-1.5 秒
  2. 并发爬取:在合规前提下,可使用chromedp的多上下文特性实现并发爬取
  3. 结果缓存:将已爬取的链接存储到本地文件,避免重复爬取

6.2 反爬应对

  1. 添加随机延迟:在分页请求之间添加随机延迟(1-3 秒),模拟人类操作
  2. 设置 User-Agent:在 chromedp 选项中添加真实的 User-Agent,避免被识别为爬虫
chromedp.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
  1. IP 轮换:若爬取量大,建议使用代理 IP 轮换,避免 IP 被封禁

6.3 合规性提醒

  • 遵守 Robots 协议:查看 Bing 的/robots.txt文件,了解爬取限制
  • 控制爬取频率:避免给服务器造成过大压力
  • 尊重版权:爬取的结果仅用于合法用途,不得侵犯他人权益

文章转载自:

http://DsyItmZG.tdLdh.cn
http://x7cFWVUw.tdLdh.cn
http://PSAGMfTk.tdLdh.cn
http://9Om9iKjr.tdLdh.cn
http://3cecNyYS.tdLdh.cn
http://tFDzO3pF.tdLdh.cn
http://gNFQkXuK.tdLdh.cn
http://7kYcUMZ9.tdLdh.cn
http://0XQPOyIV.tdLdh.cn
http://EoNlviVT.tdLdh.cn
http://N3ztg9yI.tdLdh.cn
http://bxzc9eqS.tdLdh.cn
http://yWroJOaF.tdLdh.cn
http://nqt0Amns.tdLdh.cn
http://Vln1xxnM.tdLdh.cn
http://0IeoO0eL.tdLdh.cn
http://QrX0QOez.tdLdh.cn
http://qisZLJOf.tdLdh.cn
http://LcnnlA2m.tdLdh.cn
http://lO59HI8G.tdLdh.cn
http://26FgFtmp.tdLdh.cn
http://PJNHFpZD.tdLdh.cn
http://KjyaIp31.tdLdh.cn
http://t0j6bW6M.tdLdh.cn
http://kxV8truj.tdLdh.cn
http://8R9afkfy.tdLdh.cn
http://1saiGkKC.tdLdh.cn
http://U9kQ38GA.tdLdh.cn
http://Dy8o8I6X.tdLdh.cn
http://vuwotCy3.tdLdh.cn
http://www.dtcms.com/a/368211.html

相关文章:

  • Linux 命令速查宝典:从入门到高效操作
  • 【科研绘图系列】R语言绘制论文合集图
  • 分类、目标检测、实例分割的评估指标
  • 卷积神经网络进行图像分类
  • Java JVM核心原理与面试题解析
  • 【Flutter】RefreshIndicator 无法下拉刷新问题
  • 基于Django+Vue3+YOLO的智能气象检测系统
  • Flutter的三棵树
  • React 样式隔离核心方法和最佳实践
  • 踩坑实录:Django继承AbstractUser时遇到的related_name冲突及解决方案
  • 【Flutter】flutter_local_notifications并发下载任务通知实践
  • 覆盖Transformer、GAN:掩码重建正在重塑时间序列领域!
  • 数据结构基础之队列:数组/链表
  • 数据可视化工具推荐:5款让图表制作轻松上手的神器
  • 【网安基础】--ip地址与子网掩码
  • spring AI 的简单使用
  • 【yolo】YOLOv8 训练模型参数与多机环境差异总结
  • 算法(keep learning)
  • C/C++中的可变参数 (Variadic Arguments)函数机制
  • 深度学习:CNN 模型训练中的学习率调整(基于 PyTorch)
  • Mattermost教程:用Docker搭建自己的开源Slack替代品 (团队聊天)
  • Electron 性能优化:内存管理和渲染效率
  • 数字隔离器,新能源汽车PTC中的“电气安全卫士”
  • 2025 汽车租赁大会:九识智能以“租赁+运力”革新城市智能配送
  • 云原生部署_Docker入门
  • javaweb(【概述和安装】【tomeat的使用】【servlet入门】).
  • 基于SpringBoot的社区智能垃圾管理系统【2026最新】
  • 基于飞算JavaAI的在线图书借阅平台设计实现
  • dbeaver工具连接inceptor星环数据库
  • Linux内核网络安全序列号生成机制解析