在数据采集领域,搜索引擎结果是重要的信息来源。但传统爬虫面对现代浏览器渲染的页面时,常因 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://www.pswp.cn/diannao/98524.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/98524.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/98524.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

遗漏的需求

“编写执行者的目的&#xff0c;仅用别名来表达需要传递的数据”&#xff0c;就如客户信息用名字和地址表示一样&#xff0c;这是一个很好的建议。然而&#xff0c;对程序员来说&#xff0c;这没有提供软件开发所必需的详细信息。程序设计人员和用户界面设计者需要准确地知道地…

《云原生故障诊疗指南:从假活到配置漂移的根治方案》

当云原生架构成为企业数字化转型的标配,系统故障的形态也随之发生了根本性变化。曾经那些“一目了然”的报错信息逐渐消失,取而代之的是“指标正常却服务不可用”“偶发故障无规律可循”等隐性问题。这些故障如同架构中的“暗物质”,看不见却持续影响着系统的稳定性,其排查…

“从零到一:使用GitLab和Jenkins实现自动化CI/CD流水线”

GitLab仓库 简单的来说就是开发人员提交代码的仓库&#xff0c;用于团队开发&#xff0c;GitLab 上托管的仓库通常作为远程仓库使用&#xff0c;开发人员可以将本地的 Git 仓库推送到 GitLab 上&#xff0c;也可以从 GitLab 克隆仓库到本地进行开发。 Jenkins Jenkins 是一个开…

3D开发工具HOOPS助力造船业数字化转型,打造更高效、更智能的船舶设计与协作!

造船业是一个高度复杂且竞争激烈的行业&#xff0c;涵盖船体设计、结构分析、生产制造到运维管理的完整生命周期。面对庞大的CAD数据、多方协作的复杂流程以及数字化转型的迫切需求&#xff0c;传统工具往往显得力不从心。 Tech Soft 3D的HOOPS SDK系列&#xff0c;正以其卓越…

Python调用MCP:无需重构,快速为现有应用注入AI与外部服务能力!

文章目录 📖 介绍 📖 🏡 演示环境 🏡 ✨ MCP核心概念:AI世界的“USB-C” ✨ 🛠️ MCP安装与基础使用 🛠️ 🚀 安装模块 📝 创建第一个MCP服务端 📞 Python中MCP客户端的调用方案 📞 📖 概述 📑 深度解析 🔖 参数详情 🔖 常用方法 🚀 不同传输协…

【链表】3.重排链表(medium)

重排链表&#xff08;medium&#xff09;题⽬描述&#xff1a;解法&#xff1a;算法思路&#xff1a;算法代码&#xff1a;题⽬链接&#xff1a;143. 重排链表 题⽬描述&#xff1a; 给定⼀个单链表 L 的头节点 head &#xff0c;单链表 L 表⽰为&#xff1a; L(0) → L(1) →…

蜜罐平台-Hfish部署

Hfish简介&#xff1a; HFish是一款社区型免费蜜罐&#xff0c;侧重企业安全场景&#xff0c;从内网失陷检测、外网威胁感知、威胁情报生产三个场景出发&#xff0c;为用户提供可独立操作且实用的功能&#xff0c;通过安全、敏捷、可靠的中低交互蜜罐增加用户在失陷感知和威胁…

docker-容器

安装docker yum install -y docker查看版本 docker version安装docker-compose yum install -y docker-compose查看版本 docker-compose --version基础镜像构建 tar --exclude/var/lib -cvf euler.tar /etc /boot /var /tmp /usr /mnt /bin /sbin /lib /lib64将JDK等需要的中间…

ESP32开发:ubuntu22.04 下esp-idf开发环境搭建

ubuntu22.04 下 esp-idf 开发环境搭建1.安装编译 ESP-IDF 需要以下软件包2.获取 ESP-IDF3.设置工具下载工具备选方案4.设置环境变量5.编译工程并烧录配置工程编译工程烧录固件到设备6.其他指令监视输出擦除 flash清除编译1.安装编译 ESP-IDF 需要以下软件包 编译 ESP-IDF 需要…

汇编基础2

1.函数调用fun0mov r4, #100bx lrget_MaxNumcmp r0, r1stmfd sp!, {r0-r12, lr} //入栈bl fun0 //调用fun0函数ldmfd sp!, {r0-r12, lr} //出栈movge r3, r0movlt r3, r1bx lr mainldr sp, 0x40001000mov r0, #100mov r1, #200mov r2, #100stmfd sp!,…

20250909的学习笔记

HTML 基础笔记1. HTML 基本格式<!DOCTYPE html> <html> <head><meta charset"utf-8"><title>中文测试</title> </head> <body>这里是测试body测试内容。 </body> </html>2. HTML 标签常用标签 - <h1…

Linux 安全加固;Windows 安全设置

一、Linux 安全加固1. 账户与权限管理最小权限原则禁用 root 远程登录&#xff1a;修改 /etc/ssh/sshd_config&#xff0c;设置 PermitRootLogin no。使用 sudo 替代直接 root 操作&#xff0c;并通过 /etc/sudoers 限制命令范围&#xff08;如仅允许 apt 和 systemctl&#xf…

条码打印检测一体机是什么?

在工业4.0和智能制造的大背景下&#xff0c;数据的准确性和实时性是构建高效追溯系统。条码/二维码作为物理世界与数字世界连接的桥梁&#xff0c;其打印质量直接决定了数据链路的可靠性。传统“打印-人工抽检/离线全检”的模式存在流程割裂、效率低下、无法100%覆盖的弊端&…

Javaweb - 14.6 - Vue3 数据交互 Axios

目录 Promise 普通函数和回调函数 Promise 简介 Promise 基本用法 async 和 await 的使用 Axios 介绍 Axios 入门案例 Axios 的 get 和 post 方法 Axios 拦截器 完&#xff01; Promise 普通函数和回调函数 普通函数&#xff1a;正常调用的函数&#xff0c;一般函数…

怎么选适合企业的RPA财务机器人?

对于大多数财务人来说&#xff0c;“月初月末就是噩梦”已经成了常态&#xff1a;一边要面对堆积如山的单据和报表&#xff0c;一边还要应付领导不断加码的工作&#xff0c;常常忙到深夜&#xff0c;却总觉得自己陷在重复事务中难有成长。其实&#xff0c;这并不是个体问题&…

html css js网页制作成品——HTML+CSS无穷网页设计(5页)附源码

目录 一、👨‍🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML

AUTOSAR进阶图解==>AUTOSAR_SWS_PDURouter

AUTOSAR PDU Router详解文档 AUTOSAR通信架构中的核心路由模块目录 1. 概述2. PDU Router模块架构3. PDU Router配置模型4. PDU Router路由流程5. PDU Router状态机6. 总结 1. 概述 PDU Router模块是AUTOSAR通信架构中的核心组件&#xff0c;负责在AUTOSAR软件组件之间路由I-…

RHEL7.9、RHEL9.3——源码安装MySQL

目录 一、环境部署 1. 克隆rhel7.9虚拟机 二、源码安装MySQL 1. 准备工作 2. 源码部署mysql8.0.40 1&#xff09;安装编译mysql所需软件包 2&#xff09;编译安装mysql8.0.40 3&#xff09;生成启动脚本 一、环境部署 1. 克隆rhel7.9虚拟机 改名为 “RHEL79_mysql_master” 并…

解决Win11 安全中心删掉存在隐患的工具

打开设置&#xff0c; 找到Windows安全中心&#xff0c;找到病毒和威胁防护&#xff0c;选择排除项&#xff0c;点 添加或删除排除项添加文件&#xff0c;文件夹&#xff0c;工具按照自己需求选择。或&#xff0c;删除文件注意&#xff1a;隐患的工具或者文件安装或者用完&…

通过URI Scheme实现从Web网页上打开本地C++应用程序(以腾讯会议为例,附完整实现源码)

目录 1、需求描述 2、选择URI Scheme实现 3、何为URI Scheme&#xff1f; 4、将自定义的URL Scheme信息写入注册表的C源码实现 5、如何实现最开始的3种需求 6、后续需要考虑的细节问题 之前陆续收到一些从Web页面上启动我们C客户端软件的需求&#xff0c;希望我们能提供一…