使用goquery和chromedp写爬虫

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

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

使用 goquery 爬取静态网页内容

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

html 复制代码
<!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. 提取数据:从选定的元素中提取文本或属性。
go 复制代码
package main

import (
    "fmt"
    "log"
    "net/http"
    "strings"

    "github.com/PuerkitoBio/goquery"
)

func main() {
    // 发起HTTP GET请求到指定的URL
    resp, 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) 输出获取到的元素文本内容。
go 复制代码
package main

import (
    "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" 需要替换成你想要获取内容的元素的实际ID
        chromedp.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。

go 复制代码
// 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"),
		// 防止监测webdriver
		chromedp.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() // 确保在函数结束时释放资源

	// 启动浏览器并导航到指定URL
	err := 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
	}

	// 将字符串转换为 []byte
	b := []byte(adStr)

	// 使用goquery解析HTML
	doc, 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应用提供了强大的工具,使得我们可以从复杂的网页中提取有价值的数据。

相关推荐
数据龙傲天15 小时前
API接口安全:电商数据保护的坚固防线
爬虫·python·数据分析·api
计算机软件程序设计17 小时前
Python爬虫之使用BeautifulSoup进行HTML Document文档的解析
爬虫·python·beautifulsoup
跟着小郑学前端21 小时前
Python实现银杏树绘制与效果展示
开发语言·爬虫·python
RacheV+TNY2642781 天前
API接口安全:保障电商数据不被泄露的关键
大数据·爬虫·python·api
心死翼未伤1 天前
python数据分析之爬虫基础:requests详解
开发语言·爬虫·python·http·数据挖掘·数据分析·pip
代码敲得好外卖送到老1 天前
补环境过a_bogus(版本v 1.0.1.19)
爬虫·逆向
m0_748231311 天前
简单的爬虫脚本编写
爬虫
zi__you2 天前
【Python网络爬虫 常见问题汇总】
开发语言·爬虫·python
m0_548049702 天前
网络爬虫的原理
爬虫