如何使用Go Colly获取页面多元素并存入数据库同一行?
解决Go Colly爬取多元素并存入数据库同一行的问题
这个问题我之前也碰到过,本质上是因为你现在的写法是每匹配到一个元素就插入一次数据库,自然会生成两行。要解决这个问题,核心思路是先把需要的所有数据收集齐,再一次性插入数据库,或者直接从包含两个元素的父容器里一次性提取所有字段。下面给你两种可行的方案:
方案一:先收集数据,爬完页面后统一插入
我们可以在Collector的生命周期里先把标题和内容存到一个Post实例中,等整个页面爬取完成后,再将完整的Post插入数据库。Colly提供了OnScraped事件,它会在页面所有元素都处理完成后触发,刚好适合做这个操作:
type Post struct{ ID int64 Title string Content string } func main() { c := colly.NewCollector() // 定义一个Post实例用来暂存爬取到的数据 var post Post // 匹配标题元素(建议用更精确的选择器,比如h1,避免匹配到所有h标签) c.OnHTML("h1", func(e *colly.HTMLElement) { post.Title = e.Text }) // 匹配内容元素,如果有多个p标签,可以拼接内容 c.OnHTML("p", func(e *colly.HTMLElement) { // 这里如果要保留所有p标签内容,可以用拼接:post.Content += e.Text + "\n" post.Content = e.Text // 示例只取第一个p标签的内容 }) // 页面爬取完成后,插入完整的Post到数据库 c.OnScraped(func(r *colly.Response) { // 可以加个判断,避免插入空数据 if post.Title != "" || post.Content != "" { db.Create(&post) } }) c.Visit("http://go-colly.org/") }
这个方案的好处是不需要依赖页面的父元素结构,适合单篇内容的爬取场景。
方案二:从父容器中一次性提取所有字段
如果你的目标页面里,标题和内容是被同一个父元素包裹的(比如一个文章容器),那直接针对这个父元素写选择器,在回调里同时提取子元素的内容,这样一次就能构造出完整的Post并插入:
type Post struct{ ID int64 Title string Content string } func main() { c := colly.NewCollector() // 替换成页面中实际包含标题和内容的父元素选择器,比如div.article c.OnHTML("div.article", func(e *colly.HTMLElement) { post := Post{ // 提取父元素下的h1标签文本作为标题 Title: e.ChildText("h1"), // 提取父元素下的p标签文本作为内容 Content: e.ChildText("p"), } db.Create(&post) }) c.Visit("http://go-colly.org/") }
这种方案更适合页面有多个文章的场景,每个父容器对应一个Post,不会出现数据混乱的情况,也更符合Colly的最佳实践。
额外提示
- 原代码里的
"h"选择器会匹配所有h1到h6标签,建议改成更精确的选择器(比如h1、h2),避免重复赋值标题。 - 如果页面有多个p标签,需要根据需求选择是取第一个,还是拼接所有p标签的内容。
内容的提问来源于stack exchange,提问作者AliReza
相关产品推荐
相关产品推荐

