网络爬虫程序的优劣,很大程度上反映了一个搜索引擎的好差。不信,你能够随便拿一个网站去查询一下各家搜索对它的网页收录状况,爬虫强大程度跟搜索引擎好坏基本成正比。
1.世界上最简单的爬虫——三行情诗
咱们先来看一个最简单的最简单的爬虫,用python写成,只须要三行。
import requests url="http://www.cricode.com" r=requests.get(url)
上面这三行爬虫程序,就以下面这三行情诗通常,很干脆利落。
是好男人,
就应该在和女朋友吵架时,
抱着必输的心态。
2.一个正常的爬虫程序
上面那个最简单的爬虫,是一个不完整的残疾的爬虫。由于爬虫程序一般须要作的事情以下:
- 1)给定的种子URLs,爬虫程序将全部种子URL页面爬取下来
- 2)爬虫程序解析爬取到的URL页面中的连接,将这些连接放入待爬取URL集合中
- 3)重复一、2步,直到达到指定条件才结束爬取
所以,一个完整的爬虫大概是这样子的:
import requests #用来爬取网页
from bs4 import BeautifulSoup #用来解析网页
seds = ["http://www.hao123.com", #咱们的种子
"http://www.csdn.net",
http://www.cricode.com]
sum = 0 #咱们设定终止条件为:爬取到100000个页面时,就不玩了
while sum < 10000 :
if sum < len(seds):
r = requests.get(seds[sum])
sum = sum + 1
do_save_action(r)
soup = BeautifulSoup(r.content)
urls = soup.find_all("href",.....) //解析网页
for url in urls:
seds.append(url) else:
break
3.如今来找茬
上面那个完整的爬虫,不足20行代码,相信你能找出20个茬来。由于它的缺点实在是太多。下面一一列举它的N宗罪:
- 1)咱们的任务是爬取1万个网页,按上面这个程序,一我的在默默的爬取,假设爬起一个网页3秒钟,那么,爬一万个网页须要3万秒钟。MGD,咱们应当考虑开启多个线程(池)去一块儿爬取,或者用分布式架构去并发的爬取网页。
- 2)种子URL和后续解析到的URL都放在一个列表里,咱们应该设计一个更合理的数据结构来存放这些待爬取的URL才是,好比队列或者优先队列。
- 3)对各个网站的url,咱们一视同仁,事实上,咱们应当区别对待。大站好站优先原则应当予以考虑。
- 4)每次发起请求,咱们都是根据url发起请求,而这个过程当中会牵涉到DNS解析,将url转换成ip地址。一个网站一般由成千上万的URL,所以,咱们能够考虑将这些网站域名的IP地址进行缓存,避免每次都发起DNS请求,费时费力。
- 5)解析到网页中的urls后,咱们没有作任何去重处理,所有放入待爬取的列表中。事实上,可能有不少连接是重复的,咱们作了不少重复劳动。
- 6)…..
4.找了这么多茬后,颇有成就感,真正的问题来了,学挖掘机到底哪家强?
如今咱们就来一一讨论上面找茬找出的若干问题的解决方案。
1)并行爬起问题
咱们能够有多重方法去实现并行。
多线程或者线程池方式,一个爬虫程序内部开启多个线程。同一台机器开启多个爬虫程序,如此,咱们就有N多爬取线程在同时工做。能大大减小时间。
此外,当咱们要爬取的任务特别多时,一台机器、一个网点确定是不够的,咱们必须考虑分布式爬虫。常见的分布式架构有:主从(Master——Slave)架构、点对点(Peer to Peer)架构,混合架构等。
说道分布式架构,那咱们须要考虑的问题就有不少,咱们须要分派任务,各个爬虫之间须要通讯合做,共同完成任务,不要重复爬取相同的网页。分派任务咱们要作到公平公正,就须要考虑如何进行负载均衡。负载均衡,咱们第一个想到的就是Hash,好比根据网站域名进行hash。
负载均衡分派完任务以后,千万不要觉得万事大吉了,万一哪台机器挂了呢?原先指派给挂掉的哪台机器的任务指派给谁?又或者哪天要增长几台机器,任务有该如何进行从新分配呢?
一个比较好的解决方案是用一致性Hash算法。
2)待爬取网页队列
如何对待待抓取队列,跟操做系统如何调度进程是相似的场景。
不一样网站,重要程度不一样,所以,能够设计一个优先级队列来存放待爬起的网页连接。如此一来,每次抓取时,咱们都优先爬取重要的网页。
固然,你也能够效仿操做系统的进程调度策略之多级反馈队列调度算法。
3)DNS缓存
为了不每次都发起DNS查询,咱们能够将DNS进行缓存。DNS缓存固然是设计一个hash表来存储已有的域名及其IP。
4)网页去重
说到网页去重,第一个想到的是垃圾邮件过滤。垃圾邮件过滤一个经典的解决方案是Bloom Filter(布隆过滤器)。布隆过滤器原理简单来讲就是:创建一个大的位数组,而后用多个Hash函数对同一个url进行hash获得多个数字,而后将位数组中这些数字对应的位置为1。下次再来一个url时,一样是用多个Hash函数进行hash,获得多个数字,咱们只须要判断位数组中这些数字对应的为是全为1,若是全为1,那么说明这个url已经出现过。如此,便完成了url去重的问题。固然,这种方法会有偏差,只要偏差在咱们的容忍范围之类,好比1万个网页,我只爬取到了9999个,剩下那一个网页,who cares!
5)数据存储的问题
数据存储一样是个颇有技术含量的问题。用关系数据库存取仍是用NoSQL,抑或是本身设计特定的文件格式进行存储,都大有文章可作。
6)进程间通讯
分布式爬虫,就必然离不开进程间的通讯。咱们能够以规定的数据格式进行数据交互,完成进程间通讯。
7)……
废话说了那么多,真正的问题来了,问题不是学挖掘机到底哪家强?而是如何实现上面这些东西!:)
实现的过程当中,你会发现,咱们要考虑的问题远远不止上面这些。纸上得来终觉浅,觉知此事要躬行!