百度一下
爬虫抓取豆瓣top250
,结果竟有70多页。
一、起因
为何豆瓣Top250如此受欢迎?因为它实在是太适合做爬虫入门练习了。
几乎没有任何反爬限制,要抓取的电影相关内容也全部都在源码中(没有异步加载,JS动态修改DOM等情况)。
本来计划抓取掘金热门文章来着,但是发现数据基本都是Ajax请求接口获取,所以还是以豆瓣为例吧。
二、爬虫
因为第一份工作就是Python爬虫,所以对其他语言的爬虫框架也是比较感兴趣的。
爬虫说简单也简单,会发出Http请求、了解一些Html基本知识、能够将数据保存下来,就算是爬虫入门了。但爬虫说难也难,如何高效的编写爬虫、如何保证数据的准确和实效、如何应对各种反爬机制、以及如何在合规合法的情况下去获取数据。
在GitHub上搜了一圈Go语言相关的框架,发现Colly一枝独秀,竟有11.6k✨
。
三、Colly
重要的事情只说一遍: 一定要去看官方文档,这个好像不是官方的go-colly.org,但是也要浏览一遍才可以的。
挂一下官方example
里面的basic
示例吧。注释很详细,所以官方示例一定要看!!!
func main() {
// Instantiate default collector
c := colly.NewCollector(
// Visit only domains: hackerspaces.org, wiki.hackerspaces.org
colly.AllowedDomains("hackerspaces.org", "wiki.hackerspaces.org"),
)
// On every a element which has href attribute call callback
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
link := e.Attr("href")
// Print link
fmt.Printf("Link found: %q -> %s\n", e.Text, link)
// Visit link found on page
// Only those links are visited which are in AllowedDomains
c.Visit(e.Request.AbsoluteURL(link))
})
// Before making a request print "Visiting ..."
c.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting", r.URL.String())
})
// Start scraping on https://hackerspaces.org
c.Visit("https://hackerspaces.org/")
}
四、豆瓣页面分析及代码摘要
如图,我们要做的就是:
- ①打开首页,获取列表页地址
- ②进入列表页
- ③遍历列表获取详情页URL,获取下一页(列表页)地址
- ④重复②、③,直到没有下一页为止
4.1 下一页URL的获取
如图可以看到,当我们处于第1页(非最后一页)时,span.next
元素下面是有a
元素的,里面的地址即为下一页。
当我们翻到最后一页时,a
元素不见了。因此我们可以根据是否有a
元素来判断是不是已经抓取完全部数据了。
Colly中使用goquerySelector
来选择元素,也可以使用XPath来做选择
,有兴趣的可以了解一下。这里我们使用goquerySelector
。
下一页URL获取代码如下:
collector.OnHTML("div.paginator > span.next", func(element *colly.HTMLElement) {
href, found := element.DOM.Find("a").Attr("href")
// 如果有下一页,则继续访问
if found {
element.Request.Visit(element.Request.AbsoluteURL(href))
}
})
4.2 详情页列表URL的获取
如图,我们只需要查找到div.article > ol.grid_view
就找到了li
列表的直接父元素。然后再依次遍历li
节点即可。我们所需的a
元素,在li
节点下面div.hd > a
的href
属性。
具体代码如下:
collector.OnHTML("ol.grid_view", func(element *colly.HTMLElement) {
// 依次遍历所有的li节点
element.DOM.Find("li").Each(func(i int, selection *goquery.Selection) {
href, found := selection.Find("div.hd > a").Attr("href")
// 如果找到了详情页,则继续下一步的处理
if found {
parseDetail(collector, href, writer)
log.Println(href)
}
})
})
4.3 详情页内容获取
我们要获取的内容:排名Idx,标题title,年份year,基本信息info,评分rating,地址url。
分析完页面Dom结构之后,整个抓取代码的编写就变得简单了起来。
具体信息获取代码如下:
collector.OnHTML("body", func(element *colly.HTMLElement) {
selection := element.DOM.Find("div#content")
idx := selection.Find("div.top250 > span.top250-no").Text()
title := selection.Find("h1 > span").First().Text()
year := selection.Find("h1 > span.year").Text()
info := selection.Find("div#info").Text()
info = strings.ReplaceAll(info, " ", "")
info = strings.ReplaceAll(info, "\n", "; ")
rating := selection.Find("strong.rating_num").Text()
...
4.4 后续处理
可对抓取到的数据进行清洗、整理,然后入库等操作。本例仅将数据存储至csv文件。
五、代码及结果展示
抓取数据结果如下:
六、后记
其实编写爬虫时,最耗时的是页面Dom结构分析的过程。代码编写只是整个抓取过程的实现部分,并不会耗费很多的时间。
如果耗费的很多的时间(假装在说别人),那么你需要去看文档和官方示例了。
当你的才华和能力,不足以支撑你的梦想的时候,请静下心来学习。
欢迎留言交流~