当你能够针对一个url进行请求,获取数据,继续请求的时候,说明你的爬虫已经可以自给自足的爬起来。但是这样的爬虫其效率将会严重限制在单进程效率极限之下,时间的主要消耗还是在请求返回的等待时间,如果想进一步提高效率那么多进程以及分布式就会你提高效率的最好手段。而且分布式并不意味着你一定要很多台电脑,只要你在本机测试通过一样可以方便迁移。构建分布式爬虫主要是构建分布式环境,至于写爬虫并不复杂。咱们一步步来。
redis是一个key-value存储系统,速度很快因为将数据存放在内存,在必要时可以转到硬盘。支持数据类型有string,lists,sets,zsets。这些数据类型都支持push/pop,add/remove以及取交集并集差集等操作,对这些操作都是原子性的,redis还支持各种不同的排序能力。
redis本身属于一个数据库类型的系统,不过在分布式中反而是他的队列性特别好用,就被开发成分布式的基石。所以今天我们测试的内容就是在多台机器上安装redis,然后让一台作为服务器别的机器开启客户端共享队列。
我就不搬砖了。下面的链接非常详细(重点博文):
http://blog.fens.me/linux-redis-install/
还有redis有大量的命令,详细可以查询此处
http://www.3fwork.com/b902/001557MYM005172/
还有修改配置打开远程访问之类的
http://www.ttlsa.com/redis/install-redis-on-ubuntu/
使用你的服务器(有远程服务器最好,没有的话也可以在自己本机安装),按照上面安装好环境后,先去除保护的开启服务器端(未来长久使用要改配置)
如果报错,请直接改配值,将127.0.0.1注释掉(系统的防火墙要关)
redis-server --protected-mode no
然后先再开一个终端链接到服务器,在服务器本机的环境也开一个客户端,(只是因为机器不多)
redis-cli -h ip地址 -p 6379
回车之后会看到进入一个交互命令行界面。
首先增加一个key对应的value
set key1 lala
其次查看这个值
get key1
然后返回你的个人电脑,也通过客户端链接过去然后查看这个值
get key1
如果出现同样的lala。那么说明分布式的环境已经搭建好了。
python的爬虫框架scrapy也可以使用redis.请看这里:
https://www.zhihu.com/question/32302268/answer/55724369
本次的测试项目就是运用了一个共享队列而已,还没有增加别的多种功能。
因为scrapy——redis使用起来需要环境配置的比较麻烦,还得改写其中中间件,想进一步学习在scrapy中用redis可以再找文章。
一般而言分布式至少需要两个程序:完整代码我放在github上:https://github.com/luyishisi/WebCrawlers/tree/master/request
一个用于捕捉待爬队列,并且加入到队列。如果待爬队列可以通过一定的规律算出来则可以轻松直接push到队列中
另外一个则是读取待爬队列进行新一轮的爬取工作
要实现捕捉待爬队列并且加入见下代码:
requets用于发请求,re正则匹配,time控制速度,redis分布式。headers用于简单的反反爬
import requests
import re
import time
from redis import Redis
headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36' }
计算并发送请求获取根页面的html从中匹配到img的地址发送到本地redis中。
def push_redis_list():
r = Redis()
print r.keys('*')
for i in range(100):
num = 5100+i;#抓取的取件仅在5100--5200之间
url ='http://www.meizitu.com/a/'+ str(num) +'.html'
img_url = requests.get(url,timeout=30)
#print img_url.text
#time.sleep(10)
img_url_list = re.findall('http://pic.meizitu.com/wp-content/uploads/201.*.jpg',img_url.text)
print img_url_list
for temp_img_url in img_url_list:
l = len(re.findall('limg',temp_img_url))
#print l
if(l == 0):
print "url: ",temp_img_url
r.lpush('meizitu',temp_img_url)
print r.llen('meizitu')
return 0
def get_big_img_url():
r = Redis()
print r.keys('*')
while(1):
try:
url = r.lpop('meizitu')
download(url)
time.sleep(1)
print url
except:
print "请求求发送失败重试"
time.sleep(10)
continue
return 0
def download(url):
try:
r = requests.get(url,headers=headers,timeout = 50)
name = int(time.time())
f = open('./pic/'+str(name)+'.jpg','wb')
f.write(r.content)
f.close()
except Exception,e:
print Exception,":",e
开一个客户端运行push_redis_list 函数,别的可以开启任意多个客户端运行get_big_img_url 函数,则简单的分布式就构建完成
if __name__ == '__main__':
url = 'http://www.meizitu.com/a/list_1_'
print "begin"
#push_redis_list(5100)#开启则加任务队列.其中的值请限制在5400以内。不过是用于计算页码的
#get_big_img_url()#开启则运行爬取任务
运行的过程中你可以看一下该文件夹下是否有一个pic的文件夹,所有图片存储其中。
运行截图:图一加入任务队列
图二:单个客户端进行读取任务队列进行爬取
图三:截个图,比较模糊应该不会查水表吧。
要开启多机合作的redis分布式,也并不困难,最好将redis服务器放置在某服务器上,有固定ip。同时设定好安全密码或者固定的ip白名单,redis直接开放很容易被黑了。
然后修改代码建立redis连接的部分,。如下:
r=Redis(host=REDIS_HOST, port=REDIS_PORT,password=PASSWORD)
然后便是依旧一样的使用。
redis命令大全
redis常用操作命令 操作相关的命令连接 quit:关闭连接(connection) auth:简单密码认证 持久化 save:将数据同步保存到磁盘 bgsave:将数据异步保存到磁盘 lastsave:返回上次成功将数据保存到磁盘的Unix时戳 shundown:将数据同步保存到磁盘,然后关闭服务 远程服务控制 info:提供服务器的信息和统计 monitor:实时转储收到的请求 slaveof:改变复制策略设置 config:在运行时配置Redis服务器 对value操作的命令 exists(key):确认一个key是否存在 del(key):删除一个key type(key):返回值的类型 keys(pattern):返回满足给定pattern的所有key randomkey:随机返回key空间的一个 keyrename(oldname, newname):重命名key dbsize:返回当前数据库中key的数目 expire:设定一个key的活动时间(s) ttl:获得一个key的活动时间 select(index):按索引查询 move(key, dbindex):移动当前数据库中的key到dbindex数据库 flushdb:删除当前选择数据库中的所有key flushall:删除所有数据库中的所有ke 对String操作的命令 set(key, value):给数据库中名称为key的string赋予值value get(key):返回数据库中名称为key的string的value getset(key, value):给名称为key的string赋予上一次的value mget(key1, key2,…, key N):返回库中多个string的value setnx(key, value):添加string,名称为key,值为value setex(key, time, value):向库中添加string,设定过期时间time mset(key N, value N):批量设置多个string的值 msetnx(key N, value N):如果所有名称为key i的string都不存在 incr(key):名称为key的string增1操作 incrby(key, integer):名称为key的string增加integer decr(key):名称为key的string减1操作 decrby(key, integer):名称为key的string减少integer append(key, value):名称为key的string的值附加value substr(key, start, end):返回名称为key的string的value的子串 对List操作的命令 rpush(key, value):在名称为key的list尾添加一个值为value的元素 lpush(key, value):在名称为key的list头添加一个值为value的 元素 llen(key):返回名称为key的list的长度 lrange(key, start, end):返回名称为key的list中start至end之间的元素 ltrim(key, start, end):截取名称为key的list lindex(key, index):返回名称为key的list中index位置的元素 lset(key, index, value):给名称为key的list中index位置的元素赋值 lrem(key, count, value):删除count个key的list中值为value的元素 lpop(key):返回并删除名称为key的list中的首元素 rpop(key):返回并删除名称为key的list中的尾元素 blpop(key1, key2,… key N, timeout):lpop命令的block版本。 brpop(key1, key2,… key N, timeout):rpop的block版本。 rpoplpush(srckey, dstkey):返回并删除名称为srckey的list的尾元素,并将该元素添加到名称为dstkey的list的头部 对Set操作的命令 sadd(key, member):向名称为key的set中添加元素member srem(key, member) :删除名称为key的set中的元素member spop(key) :随机返回并删除名称为key的set中一个元素 smove(srckey, dstkey, member) :移到集合元素 scard(key) :返回名称为key的set的基数 sismember(key, member) :member是否是名称为key的set的元素 sinter(key1, key2,…key N) :求交集 sinterstore(dstkey, (keys)) :求交集并将交集保存到dstkey的集合 sunion(key1, (keys)) :求并集 sunionstore(dstkey, (keys)) :求并集并将并集保存到dstkey的集合 sdiff(key1, (keys)) :求差集 sdiffstore(dstkey, (keys)) :求差集并将差集保存到dstkey的集合 smembers(key) :返回名称为key的set的所有元素 srandmember(key) :随机返回名称为key的set的一个元素 对Hash操作的命令 hset(key, field, value):向名称为key的hash中添加元素field hget(key, field):返回名称为key的hash中field对应的value hmget(key, (fields)):返回名称为key的hash中field i对应的value hmset(key, (fields)):向名称为key的hash中添加元素field hincrby(key, field, integer):将名称为key的hash中field的value增加integer hexists(key, field):名称为key的hash中是否存在键为field的域 hdel(key, field):删除名称为key的hash中键为field的域 hlen(key):返回名称为key的hash中元素个数 hkeys(key):返回名称为key的hash中所有键 hvals(key):返回名称为key的hash中所有键对应的value hgetall(key):返回名称为key的hash中所有的键(field)及其对应的value
原创文章,转载请注明: 转载自URl-team
本文链接地址: 运用基于内存的数据库redis构建分布式爬虫–抓妹子图网