如何在Colly中结合chromedp实现无头模式爬取Vue.js站点?
结合chromedp与Colly处理Vue.js动态页面爬取
完全可以通过chromedp配合Colly解决你遇到的SPA(单页应用)爬取问题。Colly本身是HTTP客户端,无法执行JavaScript,因此拿不到Vue动态渲染后的内容;而chromedp可以控制Chrome/Chromium浏览器完成页面渲染,获取完整的动态HTML后,再交给Colly做解析,或者两者分工处理不同环节。
核心思路
- 用chromedp访问目标页面,等待JS渲染完成后获取完整HTML
- 将渲染后的HTML传给Colly解析,提取食谱列表的URL和标题
- 对每个食谱详情页,同样用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
相关产品推荐
相关产品推荐

