使用gopm获取无法下载的包
由于在国内谷歌的网站被墙了 所以我们有时候无法使用go get 下载go 的第三方包,这时候可以使用gopm 来解决这个问题
1.从github 上下载 gopm
go get -v github.com/gpmgo/gopm
2. 下载自动导入包插件 goimports
gopm get -g -v golang.org/x/tools/cmd/goimports
3.下载转码插件
gopm get -g -v golang.org/x/text
由于下载此插件 会出现权限被拒绝或者超时等错误,因此我们可以用这个命令
go get github.com/golang/text
从git.hub上下载 然后移动到 src\golang.org\x 目录下
4. 下载自动检测网页编码 插件
gopm get -g -v golang.org/x/net/html
单任务版爬虫架构
解析:engine收到的request全部加到任务队列里,然后不断从任务队列中拿到下一个request。具体过程是首先把request中含有的url 送给fetcher,fetcher返回根据url获取的文本,然后把文本送给parser,parser从文本中解析出有价值的信息,包括两部分,requests 和 items , requests 就是新的request, engine拿到新的requeset后再加到任务队列,就这样反复做
改成并发版爬虫
Go爬虫 并发版 : https://blog.csdn.net/weixin_42654444/article/details/82491155?utm_source=blogxgwz2
将耗时最初最长的模块改成并发版,这个模块就是fetcher,因为要拿到url 从互联网上获取文本,这部分耗时很长,因为有一个远程网络传输
我们可以把Fetcher 和parser 和起来,再包含engine 的一部分,成为一个较大的模块,这个模块叫做Worker,worker 这一块,可以采用并发实现
解析:并发版爬虫就是要将Worker 并发,一口气开很多 Worker,此时加入一个新的模块叫Scheduler, 用于多个并发Worker任务的分配。Worker的输入是Request,输出是Requests加Items,Engine收到Requests 和 Items ,Item 是直接打印出来,将 Request 送给 Scheduler ,Scheduler 中会收到很多 request, 然后将request 分发给响应的Worker处理此处输入输出不再是函数的参数和返回值,此处输入输出的箭头代表 channel,每个方框是一个goroutine,Engine 和 Scheduler只要一个goroutine,Worker 是要很多个goroutine,这些goroutine之间通过channel 互相连接。
简单版
并发版爬虫的关键在于实现scheduler,怎么把request进行分发
简单版:所有worker公用一个输入,也就是所有worker一起抢下一个request,谁抢到了谁做
并发之后变快了,原因在于Fetch操作是在worker里进行的,而且是10个worker一起进行。Fetch操作结束后,Parse会交给Engine新的Request,Engine将Request放在Scheduler中,Scheduler将等到有空闲的worker可以接受in通道的Request。虽然10个worker公用一个in通道,但是却可以同时处理很多request。不需要等一个request处理完了再处理下一个
但是这种做法有一个问题,那就是Schedule必须等到有空闲的worker来接收Scheduler写入in通道的东西。但是有可能的问题是当从out中取出一个结果时,对应要往in通道写入多个。这就会导致in通道可能会一直等空闲的worker来读而卡在那里。
解决的办法就是:
并发版调度器
为每个request 创造一个goroutine,每个goroutine 只做一件事情,就是往worker这个统一 队列里面分发request,每个request 建立一个goroutine,往统一的worker去分发。这样的速度已经很快了,快过了对方网站能承受的速度。但是这种方式有一个缺点,就是控制力很小,每个request创建一个goroutine,分发出去的goroutine 就收不回来了。所有的worker都在抢同一个同一个request channel 过来的东西,没办法控制给哪个 worker,如果要做一些负载均衡之类的事情也做不了。如果要解决这个问题,可以用第三种方式:
队列并发调度器
在这种方式中,把request 放到request队列里面去,里面有一个活动的request 可以分发,request可以分发给一个worker,这时我们可以从worker里选择我们想要的worker,因此
我们还要准备一个worker队列,把这些worker存起来,在这些worker里选择我们想要的worker, 我们就可以把我们想要的request发给我们想要的worker。这里的Scheduler 就是一个单独的goroutine, 不再是一个很简单的函数调用,它和engine及worker 都是通过channel 互相连接的。有request来就加到request队列,有worker来就加到worker队列,收到的数据缓存起来,如果request 和 worker 队列都有值,就尝试将request 发给worker。
分布式爬虫
1.并发版爬虫的问题:
1)限流问题:某一台机器的流量有限
2)去重问题:数据量很大之后,所有的程序都跑在一台机器上,单节点去重压力比较大,URL比较需要一定的计算量;之前去重的结果无法保存,因为去重的结果保存在内存的map里面,Engine接到requeset之后,看map中是否有这个request,这样做非常慢,Engine就会卡在这里。
3)数据存储问题:把存储做成一个单独的服务
2.解决办法
1)限流问题解决办法,将worker放到不同的节点,worker 主要做两件事情,拉取网页并解析
2)去重问题解决办法:基于key-value store(如redis)进行分布式去重
让worker去调用去重, worker卡住没问题,因为woker可以开很多个,很多个worker把数据放到redis里面做去重,当然redis也可以是一个集群
最终办法:并发版爬虫goroutine 之间通过Channel 通信,分布式爬虫节点之间通过RPC调用返回结果,本课程使用的是 jsonrpc
最终结构: