使用 Chromedp 监听网页请求和响应

发布于:2025-03-27 ⋅ 阅读:(44) ⋅ 点赞:(0)

使用 Chromedp 监听网页请求和响应

在进行网络爬虫的时候,有很多网站都有反爬机制,比如你想抓点数据,结果发现每次请求都带一堆奇奇怪怪的参数 —— 什么 timestamp 签名、AES 加密的字段,还有各种 Token 令牌,跟密码本似的。你直接看网页源代码吧,那些 JS 代码被混淆得亲妈都不认识,像一团乱麻似的根本理不清头绪。

这时候我们可以使用chromedp来监听网页请求,它的核心价值在于:第一不用费劲巴拉地去解密那些被混淆的 JS 代码,直接拿现成的参数用;第二能盯着不同操作时参数怎么变,比如刷新页面时哪个值会变,登录时多了哪个字段;

什么是 Chromedp

ChromeDP 是一个 Go 语言实现的库,它提供了无头浏览器的操作和控制能力,可以用来进行网页自动化、爬虫和测试等工作。通过 ChromeDP,我们可以编程方式监听网页上的各种事件,包括网络请求和响应。

环境准备

首先,我们需要安装 Go 和 ChromeDP 库:

// 安装 ChromeDP
go get -u github.com/chromedp/chromedp

演示代码

下面是一个基本的示例,演示如何使用 ChromeDP 监听网页请求:

package main

import (
	"context"
	"encoding/base64"
	"fmt"
	"log"
	"time"

	"github.com/chromedp/cdproto/network"
	"github.com/chromedp/chromedp"
)

func EnBase64(base64String string) string {
	decodedBytes, err := base64.StdEncoding.DecodeString(base64String)
	if err != nil {
		log.Println("解码错误:", err)
		return ""
	}
	return string(decodedBytes)
}

func main() {
	// 创建一个带取消功能的上下文
	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Second)
	defer cancel()

	// 创建一个ChromeDP选项,启用网络事件
	opts := append(chromedp.DefaultExecAllocatorOptions[:],
		chromedp.Flag("headless", false),
		chromedp.Flag("disable-gpu", true),
		chromedp.Flag("enable-automation", true),
		chromedp.Flag("disable-extensions", true),
	)

	allocCtx, cancel := chromedp.NewExecAllocator(ctx, opts...)
	defer cancel()

	// 创建一个新的浏览器实例
	taskCtx, taskCancel := chromedp.NewContext(allocCtx)
	defer taskCancel() // 确保在退出时取消 taskCtx

	// 监听请求开始事件
	chromedp.ListenTarget(taskCtx, func(ev interface{}) {
		switch e := ev.(type) {
		case *network.EventRequestWillBeSent:
			// 打印请求信息
			fmt.Printf("请求URL: %s\n", e.Request.URL)
			fmt.Printf("请求方法: %s\n", e.Request.Method)
			fmt.Printf("请求头: %v\n", e.Request.Headers)
			// 如果有请求体,打印请求体
			if e.Request.HasPostData {
				entries := e.Request.PostDataEntries
				for _, entry := range entries {
					fmt.Printf("请求体: %s\n", EnBase64(entry.Bytes))
				}
			}
			fmt.Println("------------------------")

		case *network.EventResponseReceived:
			// 打印响应信息
			fmt.Printf("响应URL: %s\n", e.Response.URL)
			fmt.Printf("响应状态: %d\n", e.Response.Status)
			fmt.Printf("响应头: %v\n", e.Response.Headers)

			// 获取响应体
			/*
			不知道怎么回事直接在ListenTarget中使用GetResponseBody,会一直报错invalid context
			关于GetResponseBody 处理可以看 https://github.com/chromedp/chromedp/issues/427
			*/
			go func() {
				
				chromedp.Run(taskCtx, chromedp.ActionFunc(func(ctx context.Context) error {
					body, err := network.GetResponseBody(e.RequestID).Do(ctx)
					if err != nil {
						fmt.Println(err)
					}
					fmt.Printf("响应体: %s\n", string(body))
					return nil
				}))
			}()

			fmt.Println("------------------------")
		}
	})

	// 启用网络监听
	if err := chromedp.Run(taskCtx,
		network.Enable(),
		// 访问指定网页
		chromedp.Navigate("https://blog.csdn.net/yang731227"),
		// 等待页面加载完成
		chromedp.Sleep(15*time.Second),
	); err != nil {
		log.Fatal(err)
	}
}


过滤特定类型的请求

如果您只想监听特定类型的请求(例如,只关注 XHR 请求或特定 URL 模式),可以这样做:

// 在 EventRequestWillBeSent 事件处理中添加过滤逻辑
case *network.EventRequestWillBeSent:
    // 只关注 XHR 请求
    if e.Type == network.ResourceTypeXHR {
        fmt.Printf("XHR请求: %s %s\n", e.Request.Method, e.Request.URL)
    }
    
    // 或者只关注特定URL模式
    if strings.Contains(e.Request.URL, "api") {
        fmt.Printf("API请求: %s %s\n", e.Request.Method, e.Request.URL)
    }