You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Colly中结合chromedp实现无头模式爬取Vue.js站点?

结合chromedp与Colly处理Vue.js动态页面爬取

完全可以通过chromedp配合Colly解决你遇到的SPA(单页应用)爬取问题。Colly本身是HTTP客户端,无法执行JavaScript,因此拿不到Vue动态渲染后的内容;而chromedp可以控制Chrome/Chromium浏览器完成页面渲染,获取完整的动态HTML后,再交给Colly做解析,或者两者分工处理不同环节。

核心思路

  1. 用chromedp访问目标页面,等待JS渲染完成后获取完整HTML
  2. 将渲染后的HTML传给Colly解析,提取食谱列表的URL和标题
  3. 对每个食谱详情页,同样用chromedp处理动态渲染,提取描述、食材、步骤等信息

实现代码

首先安装依赖:

go get github.com/gocolly/colly/v2
go get github.com/chromedp/chromedp

修改后的完整代码:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"github.com/chromedp/chromedp"
	"github.com/gocolly/colly/v2"
)

type Recipe struct {
	Name        string   `json:"name"`
	URL         string   `json:"url"`
	Description string   `json:"description"`
	Ingredients []string `json:"ingredients"`
	Steps       []string `json:"steps"`
}

var allRecipes []Recipe

func main() {
	// 初始化带优化配置的chromedp上下文
	ctx, cancel := chromedp.NewContext(
		context.Background(),
		chromedp.WithHeadless(),
		chromedp.WithDisableImages(),
		chromedp.SetUserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"),
	)
	defer cancel()

	var listPageHTML string
	// 用chromedp获取渲染后的列表页HTML
	err := chromedp.Run(ctx,
		chromedp.Navigate("https://www.allrecipes.com/recipes/17562/dinner/"),
		chromedp.Sleep(1*time.Second),
		chromedp.WaitVisible(`.mntl-card`, chromedp.ByQuery),
		chromedp.OuterHTML(`html`, &listPageHTML),
	)
	if err != nil {
		fmt.Printf("chromedp获取列表页失败: %v\n", err)
		return
	}

	// 初始化Colly收集器
	c := colly.NewCollector(colly.AllowedDomains("www.allrecipes.com"))

	// 解析列表页的食谱卡片
	c.OnHTML(`a.mntl-card`, func(h *colly.HTMLElement) {
		recipeURL := h.Request.AbsoluteURL(h.ChildAttr(`a.mntl-card-list-items`, `href`))
		recipeName := h.ChildText(`.card__title-text`)

		// 爬取单个食谱的详情信息
		scrapeRecipeDetail(recipeURL, recipeName)
	})

	// 将chromedp获取的HTML传给Colly解析
	err = c.VisitHTML(listPageHTML)
	if err != nil {
		fmt.Printf("Colly解析列表HTML失败: %v\n", err)
		return
	}

	// 保存数据到JSON文件
	content, err := json.MarshalIndent(allRecipes, "", "  ")
	if err != nil {
		fmt.Printf("JSON序列化失败: %v\n", err)
		return
	}
	os.WriteFile("recipes.json", content, 0644)
	fmt.Printf("共爬取到 %d 个食谱\n", len(allRecipes))
}

// scrapeRecipeDetail 爬取单个食谱的详情数据
func scrapeRecipeDetail(url, name string) {
	ctx, cancel := chromedp.NewContext(
		context.Background(),
		chromedp.WithHeadless(),
		chromedp.WithDisableImages(),
	)
	defer cancel()

	var description string
	var ingredients []string
	var steps []string

	// 用chromedp渲染详情页并提取数据
	err := chromedp.Run(ctx,
		chromedp.Navigate(url),
		chromedp.Sleep(1*time.Second),
		chromedp.WaitVisible(`.article-subheading`, chromedp.ByQuery),
		// 获取食谱描述
		chromedp.Text(`.article-subheading`, &description),
		// 通过JS提取食材列表
		chromedp.Evaluate(`Array.from(document.querySelectorAll('.mntl-structured-ingredients__list-item')).map(el => el.textContent.trim())`, &ingredients),
		// 通过JS提取步骤列表
		chromedp.Evaluate(`Array.from(document.querySelectorAll('.mntl-sc-block-html')).map(el => el.textContent.trim())`, &steps),
	)
	if err != nil {
		fmt.Printf("爬取详情页 %s 失败: %v\n", url, err)
		return
	}

	// 将数据加入结果集
	allRecipes = append(allRecipes, Recipe{
		Name:        name,
		URL:         url,
		Description: description,
		Ingredients: ingredients,
		Steps:       steps,
	})
	fmt.Printf("已完成爬取: %s\n", name)
}

关键说明

  • 无头模式:默认启用无界面运行,避免弹出浏览器窗口
  • 性能优化:禁用图片加载、设置合理延迟,减少目标站点压力的同时提升爬取稳定性
  • 动态数据提取:通过chromedp.Evaluate执行JavaScript,直接提取动态生成的食材和步骤列表,比纯选择器更可靠

内容的提问来源于stack exchange,提问作者maka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 01:27:17