源自公众号:DeveloperPython
其实,“入门”最好的方式是以项目开始,这样实践起来你会被目标驱动。从而,不用一步一步慢慢的学习模块化的东西。
其实,知识体系里面的每一个知识点类似于图里的点。边就是知识体系的依赖关系,那么整个图也就是一个有向无环图。因为学习A的经验可以帮助到你学习B。因此,入门的东西根本不用学习,因为入门点根本不存在。
同时,你需要学习的是如何去做一个大的项目,来亲身体会爬虫, 并一步步学习爬虫的知识点。
那么,我总结下知乎上的相关知识点:
- 爬虫的工作原理
- 基本的Https爬虫工具:Scrapy
- 分布式爬虫系统。也就是维护一个集群机器来高效的完成分布式队列。Github上也有一个现成的例子: nvie/rq
- rq和Scrapy的结合: rolando/scrapy-redis
- 后续处理: 网页处理grangier/python-goose 存储(Mongodb)
以下,我将基于xiyouMc/WebHubBot的经验来讲:
-
爬虫的怎么工作的
爬虫的另一个意思其实就是(Spider) ,互联“网”也是它的环境。简单的来说,也就是你需要用这个蜘蛛来把相关的网站的所有角落(网页)都爬一边。
那么,你可以选择一个自己感兴趣的平台,如知乎、淘宝等等的。
我这里通过PornHub来讲解。比如,我们访问PornHub的首页,里面会出现很多链接。同时最初我们的目标是拿到所有的视频标题、视频简介、视频链接或者其他有用信息。然后我们兴高采烈的将整个首页都爬下来,这里想象下,你就是将整个页面完完整整的Copy了下来。
然后,我们随便点击一个链接进入视频详情页,进入第二个页面之后,那就会看到具体的视频标题、简介等等的。那么这只是一个视频,我们又是如何做到爬取整站的数据呢。这里可以动下脑子,一种是返回再回到首页去拿一个链接,另一种则是基于第一步爬下来的首页来找下一个链接。这里,当然是第二种方案。同时我们要做去重,爬过的链接,就不要再去爬第二遍。
所以,理论上如果后续的所有视频详情页都是从首页可达的话,那么我们就一定可以将所有网页都爬下来。
以下是Python的伪代码实现:
from Queue import Queue home_page = 'https://www.pornhub.com/' link_queue = Queue() seen = set() seen.insert(home_page) link_queue.put(home_page) while(True): if link_queue.size() > 0: current_url = url_queue.get() #拿到队列中第一个url links = save(current_url) #保存这个页面的html,并返回当前页面的所有link for link in links: if link not in seen: seen.put(link) link_queue.put(link) else: break
以上就是一个简单的伪代码来爬取pornhub中视频资源的例子。当然, 这只是一个非常简单的例子,其实爬虫是一个非常复杂的项目,类似的就是搜索引擎需要爬取整站的数据,更是需要一整个团队来开发和维护。
-
效率
如果,使用上述的代码来爬取PornHub的话,那么你是绝对无法在一天时间内完成500万的海量数据的,更别说在短时间内爬取PornHub的所有数据。
那么,问题出在哪?爬的网页太多太多了,而且上面的代码太慢太慢了。
PornHub的全网有N个页面,那么分析下判重的时间复杂度将是 N * log(N),因为每个网页都要遍历一遍,而使用Set来做判重,需要 log(N) 的复杂度。
所以,我们需要一个成熟的判重方案。Bloom Filter。 它是一个空间效率很高的随机数据结构。官方简介:
Bloom Filter是一种空间效率很高的随机数据结构,它利用位数组很简洁地表示一个集合,并能判断一个元素是否属于这个集合。Bloom Filter的这种高效是有一定代价的:在判断一个元素是否属于某个集合时,有可能会把不属于这个集合的元素误认为属于这个集合(false positive)。因此,Bloom Filter不适合那些“零错误”的应用场合。而在能容忍低错误率的应用场合下,Bloom Filter通过极少的错误换取了存储空间的极大节省。
有兴趣可以详细学习下这个算法。
简单的来说,它是一种利用Hash的方法来判重的。并且在使用固定的内存,不随url的数量增加而增长,以 O(1)的效率判断url是否已经在set中。但是荣然有极小的可能性会误判,所以对于“零错误”的系统不适用。但是对于爬虫,小概率的重复爬取也是可以接受的。
那么,以上就是判重的最快方式了。
当然,另一个瓶颈又会出现,如果你只有一台机器,那么不管你的带宽有多大你的机器下载网页的速度还是会有瓶颈。那么,只有加快这个速度,我们使用多台机器来跑。
-
集群化爬取
集群化爬取,其实不难理解。也就是将你的爬虫任务分发到n台机器来处理,当然每台机器处理的任务不同,且不重复。
那么,假设你有100台机器,怎么用Python实现一个分布式的爬取算法呢?
Server -> Client 。这种C/S 的模式,我相信大家也不陌生。那么分布式的系统,其实就是一台Server -> n个Client 来处理。这里我们将Server定义为 Master机,多个Client定义为多个 Slave。所以基于开始的伪代码,我们可以将link_queue 放到Master上,其他的Slave都可以通过网络跟Master联通,每当一个Slave下载完成一个页面之后,就会将这个页面的结果告知Master,同时获取一个新的link 来爬取。同时 BloomFliter 也是放在Master的,用来针对Links进行去重。 其中Slave和Server联通的方式,就是通过Redis,这是一个可以远程操作的缓存数据,提供了完善的队列管理。其次,Redis的队列中已经包含的去重的,当Push一个url 到Redis之后,某一个Slave Pop拿到数据之后,这个Url将只会在这个Slave进行处理。
因此,我们可以用Python来实现。Slave的机器上安装Scrapy,Master上安装Redis和rq用作分布式队列。
伪代码:
""" Slave """ url = link_from_master() # 从Master机Get到最新的链接 content = save(url) # 请求并保存这个链接下的视频信息 send_to_master(url) # 将当前处理过的url Post给主机。 """ Master """ queue = Queue() bf = BloomFilter() home_pages = "https://www.pornhub.com/" while(True): if request == 'GET': if distributed_queue.size()>0: send(queue.get()) # 将当前url push到Redis,从而让Slave获取到。 else: break elif request == 'POST': bf.put(request.url) #将处理过的Url 保存到bf队列
-
后处理
上面的路子,其实是很简单很简单的一部分。同样的,后续你还要进行其他的处理,比如:
- 有效的存储
- 有效的判重
- 有效的信息提取
- 及时更新
所以,不要在意如何“入门”,只管上路就好了。
长摁‘识别二维码’,一起进步
生活不止眼前的苟且,还有手下的代码、
和嘴上的扯淡
——
个人博客: http://xiyoumc.0x2048.com/
Github:https://www.github.com/xiyouMc
点击 Join,加入Python技术成长圈子,我在这里等着你。
网友评论