一个事件驱动的图片爬虫

起因
  1. 无聊的时候会翻出去看看国外的漫画,然而一页一页加载总是会很慢,偶尔还需要多刷新几次才能显示出来,非常影响体验。于是就写了个脚本去抓某一个漫画下所有的图片,这样跑一遍脚本,就能在本地看图片了。

  2. 为了偷懒,第一个版本用的单线程模型,几百张图片串行请求,真的慢。

  3. 实际工作中一直没什么机会用到异步IO,正好拿来练练手。

分析

并发的下载图片,有多线程和事件驱动两套方案。

多线程的实现方式,例如一部漫画有300张图,我不可能开300个Thread,系统受不了。比较实际的做法是使用一个容量为N的ThreadPool,那么,同时就只能发出N个请求,然后所有线程Block等待,其实效率也不高

然而事件驱动的方式就不一样了,我可以一口气把所有请求发出去,当有请求完成时,就调用事先定义的回调Handle,实现了300张图片的并行下载。

先上图看看效果

从图中就可以看出,所有的请求都发出去之后,才陆续有响应结果乱序到达。这就是典型的异步IO的情景。

基于EventMachine的异步图片爬虫

EventMachine是ruby社区知名的事件驱动库,类似于Netty、NodeJS

通过 EM.run{}就可以开始一个事件循环

以下是关键代码

  #img_info = [{file_name: '1.jpg', url:'xxx'}...]def getImg(img_info)EM.run{ #开启事件循环multi = EventMachine::MultiRequest.new #request容器@img_info_copy = img_info.dupimg_info.each do |info|file_name = File.join(@dir, info[:file_name])if FileTest::exist?(file_name)@img_info_copy.delete(info)puts "#{file_name} skip".bluenextendputs "#{file_name} start".greenreq = EventMachine::HttpRequest.new(info[:url]).get #创建requestmulti.add "#{file_name}",reqreq.callback { #成功回调File.open(file_name, 'w') { |file| file.write(req.response) }@img_info_copy.delete(info)puts "#{file_name} done".green}req.errback { #失败回调puts "#{file_name} fail".red}endmulti.callback do #所有request都完成后的回调if @img_info_copy.size == 0 #如果没有图片下载失败EM.stopelse #递归调用,重新下载的图片puts "Total fails: #{@img_info_copy.size}, solving...".redgetImg @img_info_copy.dupendend}end
复制代码
遇到的小坑
EM.run {}之后,主线程就block了,所有写在它后面的代码都不执行
复制代码
效果

通过这次的优化,下载一部两三百页漫画的时间从之前单线程版本的二十多分钟,变成了现在的两分钟左右!


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部