使用goquery和chromedp写爬虫

server/2024/12/15 14:50:11/

在本文中,我们将探索如何利用两个强大的Go语言包——goquerychromedp——来爬取网页文章。goquery是一个轻量级且易于使用的库,它提供了基本的HTTP请求功能,允许我们直接向目标URL发起请求并获取页面内容。相比之下,chromedp则提供了更为高级的功能,它能够模拟一个完整的Chrome浏览器实例,支持后台运行,并能够执行复杂的用户交互操作,如鼠标点击和页面滚动。

通过结合使用这两个包,我们不仅能够高效地获取网页数据,还能够模拟用户行为,深入挖掘那些仅通过静态请求无法触及的网页内容。

使用 goquery 爬取静态网页内容

goquery包提供了一种方便的方式来处理HTML文档,它借鉴了jQuery的用法,使得DOM选择和操作变得简单直观。所以说如果想要会爬虫,需要知道什么是css选择器。用它告诉浏览器哪些元素需要应用样式或者在像goquery这样的库中用来选取特定的DOM元素。以下是几个例子

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Selector Examples</title><style>/* 元素选择器:选择所有的<p>元素 */p {color: blue;}/* 类选择器:选择所有class="highlight"的元素 */.highlight {background-color: yellow;}/* ID选择器:选择id="special"的元素 */#special {font-weight: bold;color: red;}/* 属性选择器:选择所有type="email"的<input>元素 */input[type="email"] {border: 2px solid green;}</style>
</head>
<body><!-- 元素选择器示例:所有<p>元素都将显示为蓝色文本 --><p>This paragraph is styled by an element selector.</p><!-- 类选择器示例:这个段落有一个黄色背景 --><p class="highlight">This paragraph is styled by a class selector.</p><!-- ID选择器示例:这个段落将显示为红色加粗文本 --><p id="special">This paragraph is styled by an ID selector.</p><!-- 属性选择器示例:这个输入框将有绿色边框 --><form><input type="email" placeholder="Enter your email"></form></body>
</html>

在这个HTML文档中:

  • 所有的<p>元素将显示为蓝色文本,这是通过元素选择器实现的。
  • 具有class="highlight"<p>元素将有一个黄色背景,这是通过类选择器实现的。
  • 具有id="special"<p>元素将显示为红色加粗文本,这是通过ID选择器实现的。
  • 类型为email<input>元素将有绿色边框,这是通过属性选择器实现的。

当你将这个HTML代码保存为文件并在浏览器中打开时,你将看到不同选择器对元素样式的影响。

打开后通过 1、鼠标右键+点击检查。2、点击元素

即可看到该页面的html文件

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里一个小tips就是将鼠标指针置于检查的元素中通过Ctrl+f可以输入css选择器来查找对应的dom对象的位置。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以下是使用goquery获取网页内容的基本步骤:

  1. 发送HTTP请求:使用net/http包向目标URL发送请求。
  2. 解析HTML:将响应的HTML内容解析为goquery文档对象。
  3. 选择元素:使用刚才教学的CSS选择器选取所需的HTML元素。
  4. 提取数据:从选定的元素中提取文本或属性。
package mainimport ("fmt""log""net/http""strings""github.com/PuerkitoBio/goquery"
)func main() {// 发起HTTP GET请求到指定的URLresp, err := http.Get("http://example.com")if err != nil {log.Fatal(err) // 如果请求失败,记录错误并退出}defer resp.Body.Close() // 确保在函数结束时关闭响应体// 使用goquery.NewDocumentFromReader解析HTML文档doc, err := goquery.NewDocumentFromReader(resp.Body)if err != nil {log.Fatal(err) // 如果解析失败,记录错误并退出}// 使用CSS选择器找到class为highlight的元素// 这里假设我们只关心body中的第一个匹配元素content := ""doc.Find("body .highlight").Each(func(i int, s *goquery.Selection) {// .Text() 获取当前选择器的纯文本内容content = s.Text()return // 因为我们只关心第一个匹配元素,所以找到后就返回})// 输出获取到的内容fmt.Println("Extracted content:", content)
}

使用 chromedp 模拟浏览器操作

chromedp包提供了一种方式来控制Chrome浏览器,执行复杂的用户交互操作。以下是使用chromedp执行浏览器自动化的基本步骤:

  1. 创建上下文chromedp.NewContext(context.Background()) 创建一个新的浏览器上下文,这个上下文用于控制浏览器实例。defer cancel() 确保在函数结束时释放资源。
  2. 定义动作序列:使用 chromedp.Run(ctx, ...) 定义一系列自动化操作。
    • chromedp.Navigate(https://example.com):导航到指定的URL。
    • chromedp.WaitVisible(#someElementID, chromedp.ByQuery):等待页面上的某个元素变得可见。这里的 #someElementID 需要替换成你想要获取内容的元素的实际ID。
    • chromedp.Text(#someElementID, &text, chromedp.NodeVisible):获取该元素的文本内容,并将其存储在变量 text 中。
  3. 错误处理if err != nil 检查执行过程中是否出现错误,并在出现错误时终止程序。
  4. 输出结果log.Printf("Element text: %s\n", text) 输出获取到的元素文本内容。
package mainimport ("context""log""time""github.com/chromedp/chromedp"
)func main() {// 创建上下文,用于控制浏览器实例ctx, cancel := chromedp.NewContext(context.Background())defer cancel() // 确保在函数结束时释放资源// 定义要执行的动作序列// 1. 导航到指定的URL// 2. 等待页面上的某个元素变得可见// 3. 获取该元素的文本内容var text string // 用于存储元素的文本内容err := chromedp.Run(ctx,// 导航到 "https://example.com" 网站chromedp.Navigate(`https://example.com`),// 等待ID为 "someElementID" 的元素在页面上变得可见// 这里的 "someElementID" 需要替换成你想要获取内容的元素的实际IDchromedp.WaitVisible(`#someElementID`, chromedp.ByQuery),// 获取ID为 "someElementID" 的元素的文本内容// 并将内容存储在变量 "text" 中chromedp.Text(`#someElementID`, &text, chromedp.NodeVisible),)// 检查执行过程中是否出现错误if err != nil {log.Fatal(err)}// 输出获取到的元素文本内容log.Printf("Element text: %s\n", text)
}

chromedp+goquery

我习惯于将chromedp和goquery接合使用,chromedp可以通过延迟加载可以避免被网页判断是否为真人而拒绝返回内容。

goquery去操作dom树用的比较熟悉。我将其封装成了一个函数,可以借鉴一下:使用 chromedp 库来启动一个无头浏览器会话,导航到指定的 URL,并等待页面上的某个元素变得可见,然后获取该页面的 HTML 内容,并使用 goquery 库来解析 HTML。

// GetDocumentWithSelectWaiting 函数用于获取指定 URL 的页面内容,并等待特定元素可见
func GetDocumentWithSelectWaiting(docSelect string, url string) (*goquery.Document, []byte, error) {// 设置Chrome会话上下文和超时时间ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)defer cancel() // 确保在函数结束时释放资源// 创建Chrome会话的选项opts := append(chromedp.DefaultExecAllocatorOptions[:],// 打开无头模式chromedp.Flag("headless", true),// 设置用户代理,模拟浏览器访问chromedp.Flag("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"),// 设置浏览器窗口大小chromedp.WindowSize(1150, 1000),// 设置语言chromedp.Flag("lang", "en-US"),// 防止监测webdriverchromedp.Flag("enable-automation", false),// 禁用blink特征,减少自动化检测chromedp.Flag("disable-blink-features", "AutomationControlled"),// 忽略证书错误chromedp.Flag("ignore-certificate-errors", true),// 关闭浏览器声音chromedp.Flag("mute-audio", false),// 再次设置浏览器窗口大小,确保覆盖默认值chromedp.WindowSize(1150, 1000),)allocCtx, allocCancel := chromedp.NewExecAllocator(ctx, opts...)defer allocCancel() // 确保在函数结束时释放资源// 用于执行具体的浏览器操作taskCtx, taskCancel := chromedp.NewContext(allocCtx, chromedp.WithLogf(log.Printf))defer taskCancel() // 确保在函数结束时释放资源// 启动浏览器并导航到指定URLerr := chromedp.Run(taskCtx,// 打开该网站chromedp.Navigate(url),// 等待5秒,确保页面加载完成chromedp.Sleep(5*time.Second),)if err != nil {return nil, nil, err}var visible bool// 检查指定元素是否在页面上err = chromedp.Run(taskCtx,chromedp.Evaluate(fmt.Sprintf(`document.querySelector("%s") !== null`, docSelect), &visible),)if err != nil {return nil, nil, err}if !visible {// 如果元素不可见,返回一个自定义错误return nil, nil, errors.New("element not visible")}var adStr string// 等待元素可见并获取页面的HTML内容err = chromedp.Run(taskCtx,chromedp.WaitVisible(docSelect),chromedp.OuterHTML("html", &adStr),)if err != nil {// 判断是否是超时错误if err == context.DeadlineExceeded {log.Println("Operation timed out url:", url)} else {log.Println("Error navigating to URL:", err)}return nil, nil, err}// 将字符串转换为 []byteb := []byte(adStr)// 使用goquery解析HTMLdoc, err := goquery.NewDocumentFromReader(strings.NewReader(adStr))if err != nil {log.Fatalf("Error creating goquery document: %v", err)}defer taskCtx.Done() // 确保在函数结束时释放资源return doc, b, nil
}
  1. 无头模式:使用无头浏览器,可以在后台运行,不需要显示界面。
  2. 自定义用户代理:模拟特定浏览器的访问,有助于绕过一些简单的反爬虫机制。
  3. 窗口大小设置:可以设置浏览器窗口的大小,有时这对于页面渲染是必要的。
  4. 防检测:通过设置 enable-automationdisable-blink-features 来减少被网站检测为自动化脚本的风险。
  5. 错误处理:代码中有详细的错误处理,可以区分超时错误和其他类型的错误。
  6. 元素可见性检查:在获取元素内容之前检查元素是否可见,确保元素已经加载完成。
  7. 资源管理:使用 defer 确保上下文和资源在函数结束时被正确释放。

通过结合goquerychromedp,我们可以创建强大的爬虫,它们不仅能够处理静态内容,还能够与JavaScript生成的动态内容交互。这种组合为爬取现代Web应用提供了强大的工具,使得我们可以从复杂的网页中提取有价值的数据。


http://www.ppmy.cn/server/150368.html

相关文章

嵌入式硬件-- 元器件焊接

1.锡膏的使用 锡膏要保存在冰箱里。 焊接排线端子&#xff1b;138度的低温锡&#xff08;锡膏&#xff09;&#xff0c; 第一次使用&#xff0c;直接拿东西挑一点涂在引脚上&#xff0c;不知道多少合适&#xff0c;加热台加热到260左右&#xff0c;放在上面观察锡融化&#…

快速部署一套K8s集群-v1.28

快速部署一套K8s集群-v1.28 1.前置知识点 1.1 生产环境可部署Kubernetes集群的两种方式 目前生产部署Kubernetes集群主要有两种方式: kubeadmKubeadm是一个K8s部署工具,提供kubeadm init和kubeadm join,用于快速部署Kubernetes集群。 二进制包从github下载发行版的二进…

【工具变量】省级农产品进出口数据及农产品国际贸易数据(2001-2022年)

一、包含指标&#xff1a; yearID农产品出口额(亿元)农产品进口额(亿元)农产品出口额&#xff08;万美元&#xff09;农产品进口额&#xff08;万美元&#xff09; 二、数据来源&#xff1a;中国商务部贸易司、《中国农业年鉴》 三、资料范围&#xff1a;附带各个年度的汇率计…

Dart 3.6 发布,workspace 和 Digit separators

workspace 之前我们就聊过 Flutter 正在切换成 Monorepo 和支持 workspaces &#xff0c;Dart 3.6 开始&#xff0c;Pub 现在正式支持 monorepo 或 workspace 中 package 之间的共享解析。 pub workspaces 功能可确保 monorepo 中的 package 共享一组一致的依赖项&#xff0c…

【arm】程序跑飞,SWD端口不可用修复(N32G435CBL7)

项目场景&#xff1a; 国民N32G43X系列&#xff0c;烧录了一个测试程序&#xff0c;在DEBUG中不知什么原因挂掉&#xff0c;然后就无法连接SWD或JLINK。 问题描述 在SWD配置中不可见芯片型号&#xff0c;无法connect&#xff0c;无法烧录。但基本判断是芯片没有损坏。怀疑是程…

学习日志023---初始opencv

一、二值化 功能 二值化图&#xff1a;就是将图像中的像素改成只有两种值&#xff0c;其操作的图像必须是灰度图 1.1、阈值法(THRESH_BINARY) 通过设置一个阈值&#xff0c;将灰度图中的每一个像素值与该阈值进行比较&#xff0c;小于等于阈值的像素就被设置为0&#xff08…

我的 AI 辅助编程工具搭配心得

近年来&#xff0c;AI 在编程领域的应用越来越广泛&#xff0c;而我也逐渐摸索出一套高效的 AI 辅助编程工具搭配方法。从复杂算法到前端开发&#xff0c;再到代码反编译&#xff0c;这些 AI 工具不仅提升了我的开发效率&#xff0c;也让我在解决问题时多了一些“得力助手”。今…

【数字信号处理】期末综合实验,离散时间信号与系统的时域分析,离散信号 Z 变换,IIR 滤波器的设计与信号滤波,用窗函数法设计 FIR 数字滤波器

关注作者了解更多 我的其他CSDN专栏 过程控制系统 工程测试技术 虚拟仪器技术 可编程控制器 工业现场总线 数字图像处理 智能控制 传感器技术 嵌入式系统 复变函数与积分变换 单片机原理 线性代数 大学物理 热工与工程流体力学 数字信号处理 光电融合集成电路…