一个事件驱动的图片爬虫

起因
  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.dup
      img_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".blue
          next
        end
        puts "#{file_name} start".green
        req = EventMachine::HttpRequest.new(info[:url]).get #创建request
        multi.add "#{file_name}",req
        req.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
        }
      end
      multi.callback do #所有request都完成后的回调
        if @img_info_copy.size == 0 #如果没有图片下载失败
          EM.stop
        else #递归调用,重新下载的图片
          puts "Total fails: #{@img_info_copy.size}, solving...".red
          getImg @img_info_copy.dup
        end
      end
    }
  end
复制代码
遇到的小坑
EM.run {}之后,主线程就block了,所有写在它后面的代码都不执行
复制代码
效果

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python爬虫动态网页图片的逻辑与静态网页图片稍有不同。对于动态网页,常见的处理方式是使用Selenium库,它可以模拟浏览器操作,加载并执行网页中的JavaScript代码。 下面是爬取动态网页图片的基本逻辑: 1. 导入所需的库:需要导入Selenium库和相关的驱动程序(如Chrome驱动或Firefox驱动),以及requests库用于下载图片。 2. 配置和启动浏览器:使用Selenium库创建一个浏览器实例,并配置相关选项,例如指定驱动程序的路径和浏览器类型。 3. 访问目标网页:使用浏览器实例打开目标网页,可以使用`driver.get(url)`方法。 4. 模拟滚动或点击操作:如果网页中的图片需要通过滚动或点击操作加载出来,可以使用Selenium库提供的方法模拟这些操作。例如,使用`driver.execute_script()`方法执行JavaScript代码来模拟滚动。 5. 解析网页内容并提取图片链接:使用Selenium库获取网页的HTML内容,并使用BeautifulSoup库解析网页内容。然后,提取出所有的<img>标签或其他包含图片链接的元素。 6. 提取图片链接并下载图片:针对每个<img>标签或其他包含图片链接的元素,从中提取出图片链接,并使用requests库发送HTTP请求下载图片并保存到本地。 7. 关闭浏览器:完成图片下载后,关闭浏览器实例。 需要注意的是,使用Selenium库进行动态网页爬取可能会增加爬取的复杂性和时间消耗。同时,也要遵守网站的使用限制和隐私政策,并尊重网站的服务器负载。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值