作者:Masamune
在日常生活中,我们时常会遇到一些采集数据相关的需求,比如获取一些官方数据整理到excel表中进行统计,聚合一些网页新闻提高自己的阅读效率等等。
虽然许多爬虫教程都是用python写的,但是我认为Go语言是比python更合适的选择,Go语言有着优秀的并发特性,很容易就可以写出高并发量的爬虫,并且没有python烦人的编码转换混乱的问题。
爬虫预期的爬取规模决定的爬虫程序本身的复杂度,本文将从语言到结构部分简单阐述如何设计一个爬虫程序。
首先我们选用的Go库是github.com/PuerkitoBio/goquery,先上示例:
爬取并列出ve2x首页的文章标题示例
package mainimport ("fmt""github.com/PuerkitoBio/goquery"
)func main() {root, err := goquery.NewDocument("https://www.v2ex.com/")if err != nil {panic(err)}root = goquery.NewDocumentFromNode(root.Find("div#Main").Nodes[0])nodes := root.Find("span.item_title").Nodesfor _, n := range nodes {fmt.Println(goquery.NewDocumentFromNode(goquery.NewDocumentFromNode(n).Find("a").Nodes[0]).Text())}
}
示例结果:
怎么记忆不同发行版的一些差别?
[讨论]当代下,对拿来主义和分享主义取舍的迷惑
macOS Mojave 和 Catalina 哪个更好用
谁能做一个监控社会物资捐赠和 redten 字会收入和发放的页面
macOS 如何更新 Python ?
暂时选择了 magic keyboard,有没其他推荐?
git clone 有没有什么有效的加速方法
谁能提供微软官网上面的 Win10 镜像 sha1 值
阿里云轻量级服务器提速原理是啥呢?
为什么我这段返乡戴笠忽然失效了~~
跨网络队列服务项目更新了~ [wukongqueue]
迫于穷, 300 块钱以内的无线键盘、鼠标,求推荐~
有没有感兴趣一起学习英语的?
微信小程序咨询: 请求私人 API,是否收费问题
- 该库可以对html文档结构进行解析,并提供了类似DOM的API对html页面内的元素的内容进行提取,十分方便,相较于正则表达式来说更加优雅,具体细节可以参照文档和自己测试。
- 注意调试页面过程中建议将页面保存成本地文件,每次运行的时候通过文件读取页面,否者抓取频率过高可能会触发网站的反爬规则。
- 对于分析页面的引用库建议隔离一层接口,方便以后进行替换。
结构设计
对于绝大多数人来说,一个单机单进程的爬虫已经满足需求了,但是一个有点规模的爬虫如何做呢?这里简答阐述一个分布式的爬虫的设计思路。
任务分发
这部分建议使用队列中间件或者队列云服务,将爬取请求拆分成一个一个任务,投递到消息队列,而真实执行爬取任务的程序则从消息队列里获取任务进行爬取和分析,再进行数据落地。
任务处理
真实执行爬取任务的程序应该是无状态的,从队列获取到爬取任务后按业务规则进行处理,这部分程序可以通过多节点部署十分方便的进行扩展,不用担心数据丢失的问题,也方便做并发量的控制。
数据落地
建议通过数据库进行数据落地,并且针对每个URL的每次请求结果都进行存档,方便数据回溯,对于存储空间的消耗可能会相当大,这个需要根据爬虫规模自行评估如何做数据的落地和轮转。
数据处理
可以将已经落地的数据接入全文搜索引擎进行处理,具体则根据业务的复杂度和成本选取合适的方案。
URL去重
在爬取任务执行之前,需要判断URL在之前是否已经爬取过,对于已经爬取过的URL不需要在进行处理。这部分建议使用redis进行处理。对于已经爬取过的URL,作为key存到redis中,每次爬取之前都从redis中尝试获取一次,如果存在则跳过,这部分往深了做还可以使用布隆过滤器进行优化,这里不再阐述。
任务再分发
爬虫任务可能是通过其他程序或者人工生成的,也可能是在爬取任务处理过程中生成的,比如递归的爬取某个界面上的超链接,在这种情况下需要仔细处理再生成的任务,一些爬虫陷阱就埋在里面。比如某个连接正常用户永远无法点击到,但是爬虫在分析html页面的时候没有注意而直接进行请求的话,则会触发反爬规则被屏蔽。
关于反爬虫
爬取与反爬取是一个永恒的对抗,一般来说,只要爬取频率不要太高,就不容易触发网站的反爬规则,思路就是尽量伪装成正常的客户端请求。这里有需要注意的几点:
动态内容处理
有些需要的网页内容并不是html页面直接提供的,而是通过浏览器渲染后动态生成的,对于这部分页面,可以交由PhantomJS进行处理,获得渲染后的页面再进行分析处理。
UserAgent
一般的HTTP客户端有一个默认的UserAgent,比如Go语言官方的httpclient的类似:Go-http-client/1.1
,而一般一个普通浏览器的UserAgent类似:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36
,有些网站可能会通过UserAgent来识别异常的请求来源从而进行限制,当然这种方式比较低级,但确实有效,我们自己在请求的时候可能需要写入一个比较正常的UserAgent,甚至可以搜集多个常见浏览器的UserAgent值,发起请求的时候随机选取填入。
IP
如果同一个IP存在大量请求,肯定是会进行反爬处理,规避的方案就是需要大量的代理组成代理池,请求的时候随机选取,通过代理请求则可以避免对单一IP的屏蔽。随之而来的就是对代理本身请求成功率的评估,如果某一个代理的请求成功率太低,则需要考虑踢出代理池,以免对爬取效率造成影响。
控制爬取频率
控制爬取频率是非常重要的,如果请求频率太高引起网站管理员的注意就得不偿失了。这里建议将爬取的请求拆分成一个一个的任务,结合消息队列进行爬取频率的控制,可以通过Go本身的channel进行限流,也可以结合外部队列中间件进行限制。
验证码
简单的方案就是选择一个识别简单验证码的库,付费的话可以调用别人的识别验证码的API。
爬虫陷阱
有些超链接埋在html页面里并没有显示,正常用户不可能请求,但是如果爬虫程序识别为普通超链接并且发起请求的话,就会触发反爬规则被屏蔽。
内容
更恶心的反爬手段是,如果触发了网站的反爬规则,并不会直接拒绝请求,而是返回看似正常,但其实是虚假的数据。这种问题不易被发现,而且难以追查反爬策略,只能尽可能的关注爬取内容,做好质量控制。
最后
总的来说,爬虫的规模根据当前需求进行设计,简单的单机单进程就满足需求。复杂的就需要考虑设计成分布式程序跨机运行并且具有良好的扩展性。每个人都可以拥有自己的爬虫,方便自己的生活。