先看看robots协议

User-agent: *
Allow: /
Sitemap: http://kan.sogou.com/sitemap/sitemap.xml

牛皮!

分析

打开一个具体的影视:http://kan.sogou.com/player/181171191/,网址中有具体数字ID,我们假设数字ID就是递增的,即从1开始,那么我们可以拼接url:

  • http://kan.sogou.com/player/1/

  • http://kan.sogou.com/player/2/

  • http://kan.sogou.com/player/3/

  • http://kan.sogou.com/player/4/

有两个问题:

  1. ID上限是多少,我不可能永不止境的往上加吧

  2. 抓取效率问题,如果有10亿,那么我就需要发送10亿个HTTP请求,如果你开启了1000个线程,0.3秒能处理完一个ID,一天可以抓取:1000 * 0.3 * 60 * 60 * 24 = 25920000 个,需要约38天才能抓完,这肯定是不合乎要求的

针对这两个问题,可以用如下方法:

  1. 使用采样,比如我们确定间隔是1000,我们在1~1000中随机取数,在1001~2000中再随机取一个数,这样10亿数就被缩短为一百万了,这个数字就小多了

  2. 凭什么说上限是10亿呢,我们在真正爬虫之前还需要一次调研,调研的时候可以把间隔调大,比如5000,这次抓取只是为了评估ID分布范围,比如第一段是[1, 10000],第二段是[1000000, 9000000],最后一段是[10000000000, 10090000000]。确定ID分布范围后就可以在指定区间内采样抓取

代码

核心代码参考:generate_uid.py,该函数是主节点开启的一个线程,该线程会监控redis中爬虫start_urls队列,如果小于预期,调用生成器生成ID列表,加入到队列中,同时更新此时的ID到redis。

运行爬虫,命令是:scrapy crawl sougou -a master=True,日志样例如下:

2020-04-07 22:05:06 [scrapy.core.engine] INFO: Spider opened
2020-04-07 22:05:06 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2020-04-07 22:05:06 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2020-04-07 22:05:09 [sougou] INFO: spider sougou gen uid 40000001
2020-04-07 22:06:06 [scrapy.extensions.logstats] INFO: Crawled 3569 pages (at 3569 pages/min), scraped 0 items (at 0 items/min)
2020-04-07 22:07:06 [scrapy.extensions.logstats] INFO: Crawled 7034 pages (at 3465 pages/min), scraped 0 items (at 0 items/min)
2020-04-07 22:08:06 [scrapy.extensions.logstats] INFO: Crawled 10521 pages (at 3487 pages/min), scraped 0 items (at 0 items/min)
2020-04-07 22:09:06 [scrapy.extensions.logstats] INFO: Crawled 13849 pages (at 3328 pages/min), scraped 0 items (at 0 items/min)
2020-04-07 22:09:39 [sougou] INFO: success get useful id http://kan.sogou.com/updown.php?gid=181159677&op=get
2020-04-07 22:10:06 [scrapy.extensions.logstats] INFO: Crawled 17292 pages (at 3443 pages/min), scraped 0 items (at 0 items/min)

分布式

这是MS设计,master负责去向队列里添加任务,slave负责消费。(这里的master同样有消费)

开启master只需要向启动命令添加额外参数,slave启动方式正常即可。

注意:master只能开启一个,否则会有重复任务,slave开启多少个取决于机器、网络、代理条件。

思考

这种全量抓取方式只适合ID是数字的,这种网站还挺多的,淘宝、京东、知乎、美团等等。这些ID并不是递增,而是分布在一块块区域,先宏观上调查出大体范围,再缩小ID间隔。

但是有些网站,比如优酷的:https://v.youku.com/v_show/id_XNDU4OTM3NzM0NA==.html,id明显就是混淆过的,想要全量抓取只能通过分类接口去抓。这个有时间再聊聊。

线程安全

我们想想,这样设计会不会有线程安全?

先看看slave端,对redis的操作只有一个就是获取redis里的id,我们看看代码,scrapy_redis.spiders.RedisMixin.next_requests

    def next_requests(self):"""Returns a request to be scheduled or none."""use_set = self.settings.getbool('REDIS_START_URLS_AS_SET', defaults.START_URLS_AS_SET)fetch_one = self.server.spop if use_set else self.server.lpop# XXX: Do we need to use a timeout here?found = 0# TODO: Use redis pipeline execution.while found < self.redis_batch_size:data = fetch_one(self.redis_key)if not data:# Queue empty.breakreq = self.make_request_from_data(data)if req:yield reqfound += 1else:self.logger.debug("Request not made from data: %r", data)

scrapy-redis使用使用list结构,所以这里我们用到的是lpop命令,多次去redis中获取request,直到found = self.redis_batch_size,每次从redis中获取request的数量如果没有设置,默认就是settings中的CONCURRENT_REQUESTS,一般就是16。

        if self.redis_batch_size is None:# TODO: Deprecate this setting (REDIS_START_URLS_BATCH_SIZE).self.redis_batch_size = settings.getint('REDIS_START_URLS_BATCH_SIZE',settings.getint('CONCURRENT_REQUESTS'),)

每次从redis中获取到request后,会直接调用self.make_request_from_data(data)方法。作者在注释里也说到了这里的两个改进方法:

  1. Use timeout。redis的lpop是阻塞操作,所以理论上需要加上超时

  2. Use redis pipeline。如果不了解的可以看看文档,Redis 管道(Pipelining),

有心可以去提个pr,以后就可以吹牛说自己是scrapy-redis的contributors了,有人已经行动了,read start_urls use redis pipeline

因为lpop是原子操作,任何时候只会有单一线程从redis中拿到request,所以在获取request这一步是线程安全的。对这块不熟悉的可以阅读Redis 和 I/O 多路复用

再看看master端,有两个redis操作,

  1. 查询spider种子数量,使用llen

  2. 如果数量小于预期,生成任务ID,使用lpush插入数据

线程安全一般出现多线程之间的共享变量,这个场景下共享变量是什么,redis中的request列表吗,我仔细想了下,因为我们对redis的操作都保证原子性,并且插入的id保证不重复,所以不会出现问题。可以改进的地方,就是对master端使用redis pipeline操作。

欢迎交流想法。

实际应用

这两天看到一个新闻:

 “卖空”研究公司Wolfpack Research今日在其网站上发布报告称,爱奇艺早在2018年IPO(首次公开招股)之前就存在欺诈行为,而且此后也一直在这样做。

  该机构表示,我们估计爱奇艺将其2019年的营收夸大了约80―130亿元人民币(27%―44%),将其用户数量夸大了约42%―60%。

别的不说,就用户数,现在这个分布式爬虫就可以抓取用户数据。用户主页是:https://www.iqiyi.com/u/1416657079/feeds,也是数字的,同样可以使用采样的办法去抓取用户数据。

代码细节点击阅读原文

分布式全站爬虫——以搜狗电视剧为例相关推荐

  1. Python爬虫||Xpath-以豆瓣电视剧为例

    XPath--全称XML Path Language,即XML路径语言,它是一门在XML文档中查找信息的语言.它最初是用来搜寻XML文档的,但是它同样适用于HTML文档的搜索.所以在爬虫里就可以直接使 ...

  2. Python分布式通用爬虫(4)

    Python分布式通用爬虫(4):处理并统一时间格式 划分.py文件部分 处理并统一时间格式 普通时间转换 代码展示 佛历时间转换 代码展示 缅甸语时间转换 代码展示 其他需要替换月份的时间转换 代码 ...

  3. java 爬虫框架 j_一个分布式java爬虫框架JLiteSpider

    A lite distributed Java spider framework. 这是一个轻量级的分布式java爬虫框架 特点 这是一个强大,但又轻量级的分布式爬虫框架.jlitespider天生具 ...

  4. Python网络爬虫与信息提取笔记08-实例2:淘宝商品比价定向爬虫

    Python网络爬虫与信息提取笔记01-Requests库入门 Python网络爬虫与信息提取笔记02-网络爬虫之"盗亦有道" Python网络爬虫与信息提取笔记03-Reques ...

  5. python爬取伯乐在线_Crawl Jobble - 伯乐在线全站爬虫

    Crawl Jobble - 伯乐在线全站爬虫 抓取流程 以 全部文章 的首页为爬取起始点,获取每页所有文章详情页URL. 进入文章详情页,获取文章的标题.发布时间.转发收藏评论数.文章正文等,并获取 ...

  6. Scrapy框架爬虫—以京东众筹为例

    Scrapy框架爬虫--以京东众筹为例 第一步, 打开命令提示符,创建一个Scrapy框架: 第二步,定位到创建的文件夹: 第三步,在spider文件夹中创建一个.py文件(注:不要关闭命令提示符): ...

  7. Python爬虫之scrapy高级(全站爬取,分布式,增量爬虫)

    文章目录 1 scrapy全站爬取 1.1 全站爬取简介 1.2 CrawlSpider 1.2.1 基本讲解 1.2.2 使用CrawlSpider 1.2.2.1 爬虫文件 1.2.2.2 ite ...

  8. NO.44-----QQ音乐全站爬虫

    一.目的 qq音乐提供免费在线试听,但是下载需要付费,通过开发爬虫,绕过付费环节,直接下载我们需要的歌曲. 二.方法 爬取对象是web端qq音乐,爬取范围是全站的歌曲信息,爬取方式是在歌手列表下获取每 ...

  9. 笔趣阁分布式flask爬虫

    通过flask+mongo+requests实现一个主从分布式爬虫 一:分布式爬虫介绍 1.对等分布式:每台机器上的的爬虫都一致 无论那台机器掉线,都不会影响其他机器的爬虫 可以根据需求动态的增/删计 ...

最新文章

  1. Kafka配置消息保存时间的方法
  2. (*长期更新)软考网络工程师学习笔记——Section 7 网络层下篇
  3. php返回一个变量,PHP从另一个文件获取变量
  4. Spring 注入内部 Beans
  5. 每天一个linux命令(1):ln 命令
  6. Android官方开发文档Training系列课程中文版:调用相机之简单摄像
  7. 网页导出pdf不完整_网站中的页面如何转成PDF文档?
  8. JavaScript的call,apply和bind方法之间的区别
  9. 谷歌浏览器登录不了账号_谷歌浏览器使用分享(可谷歌账号登录)之谷歌账号登录...
  10. 三星Galaxy Tab S8系列国行版曝光:对标iPad Pro的旗舰平板
  11. Oracle P6培训系列:15定义资源库
  12. 【Linux系统】第2节 虚拟机中安装Kali系统
  13. java 文档比较功能_java 实现在线比较两个版本的Word文档的功能
  14. PID控制器的离散化推导及其C语言实现
  15. OpenGL 纹理基础与索引
  16. ceph kernel rbd (二): rbd map , rbd unmap
  17. android百度网盘不限速,什么?百度网盘不限速?而且安卓和iOS都支持,开玩笑吧?...
  18. RegExp——正则表达式
  19. 在ABP中通过EF直接执行原生Sql的解决方案
  20. 《人性的弱点》卡耐基

热门文章

  1. hpet 定时器中断 8259 linux,[OSDEV]编程高精度定时器(HPET)
  2. Zookeeper简单搭建
  3. 基于Python的头脑王者脚本(纯娱乐)
  4. python连接wifi的模块--pywifi介绍
  5. 网络唤醒 php,php 远程唤醒电脑 php源码远程唤醒电脑 远程开机
  6. python 豆瓣源_使用douban源下载python包
  7. 离线语音远程遥控车控门制作教程(二)
  8. uva1593代码对齐
  9. 「奋斗者协议」又来了:自愿加班、接受淘汰、不与公司发生法律纠纷
  10. 蓝桥杯 算法练习 数字游戏