是用redis做任务队列时,要思考:

  • 用什么数据类型来做任务队列
  • 怎样才能防止重复爬取

上一篇文章已经决定使用list来做任务队列,但是去重问题没有得到解决。这里可以用set来解决思考二的问题,就是防止重复爬取的问题。

使用list当作未完成任务队列,存储还没有爬的url(或者是用户id,文章id等唯一标识)
使用set当作已完成任务队列,存储已经爬取的url
每次爬虫程序从list未完成任务队列获取任务的时候,都去set已完成任务队列里面验证一下,如果已完成队列里已经有了,就舍弃掉,如果没有,就开始爬取,并将这个url加入到已爬取的任务队列
这样做的方便之处在于:每当我往list未完成任务队列里加任务的时候,我不用考虑这个任务有没有爬过,这个任务是不是已经在未爬取任务队列了,我只需要往里加就行了,当爬虫去取的时候,让爬虫程序去做这个操作。

以下是具体代码 
算是一个生产消费把,master往队列里塞任务,parser使用get_html的返回值进行解析,然后入库。

协程爬取贴吧里发帖内容(redis做任务队列,mongo存储)

 import requests
from lxml import etree
import redis
import asyncio,aiohttp import pymongo
conn = pymongo.MongoClient('localhost',27017) db = conn.nicedb # 指定数据库名称,连接nicedb数据库,没有则自动创建
my_set = db.test_set # 使用test_set集合,没有则自动创建
# 以上两步都是延时操作,当往数据库插入第一条数据的时候,才会真正的创建数据库和集合 # decode_responses=True,记得加这个参数,不加的话取出来的数据都是bytes类型的
r = redis.StrictRedis(host = '127.0.0.1', port = 6379, db = 2,decode_responses=True)
# pool = redis.ConnectionPool(host = '127.0.0.1', port = 6379, db = 2)
# r = redis.StrictRedis(connection_pool=pool,decode_responses=True) def master(page):
url = 'https://tieba.baidu.com/f?kw=美女&ie=utf-8&pn={}'.format(page*50)
base = 'https://tieba.baidu.com'
res = requests.get(url).text
html = etree.HTML(res)
half_urls = html.xpath("//div[@class='threadlist_title pull_left j_th_tit ']/a/@href")
full_urls = [base + i for i in half_urls]
for url in full_urls:
# 从url_list列表头部塞任务,也就是url
r.lpush('url_list',url)
#print(r.llen('url_list')) async def get_html(url):
async with asyncio.Semaphore(5): # 限制并发数为5个
async with aiohttp.ClientSession() as session:
async with session.get(url) as html:
# errors='ignore',不加这个参数的话,会报错,具体错误内容见下面图片
response = await html.text(encoding='utf-8',errors='ignore')
return response
async def parse():
while True:
# 从redis的url_list列表取任务,从右边开始取
url = r.rpop('url_list')
if url == None:
break
# 判断这个任务是否已经做过了,也就是判断这个url在没在redis的history集合里
if r.sismember('history',url) == 1:
continue
response = await get_html(url)
html = etree.HTML(response)
content = html.xpath("//div[@class='left_section']/div[2]/div[1]//cc/div[1]/text()")[0].strip()
if content != '':
# 当内容不为空时,将内容存到mongo里
my_set.save({'content':content})
#print(content)
# 将爬取过的任务放到redis的history集合里,也就是已完成任务队列
r.sadd('history', url)
t1 = time.time()
# 爬取前10页
for i in range(10):
master() # async的一些步骤
loop = asyncio.get_event_loop()
tasks = [parse() for _ in range(15)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close() t2 = time.time()
print(t2-t1)
# 最后用时:32.930299043655396
# 把mongo数据库换成mysql后,用时:43.06192493438721 原文:https://blog.csdn.net/fiery_heart/article/details/82121237

用redis做简单的任务队列(二)的更多相关文章

  1. 用redis做简单的任务队列(一)

    队列本身其实是个有序的列表,而Redis是支持list的,我们可以查看Redis的官方文档 http://redis.io/commands#list,其中我们可以对这个队列的两端分别进行操作,所以其 ...

  2. 面试连环炮系列(二):你们的项目Redis做了集群部署吗

    你们的项目Redis做了集群部署吗? 我们有大量数据需要缓存,而单实例的容量毕竟是有限的,于是做了Redis集群部署. 采取的方案是什么,Codis还是Redis Cluster,为什么要选择这个方案 ...

  3. 程序员修神之路--redis做分布式锁可能不那么简单

    菜菜哥,复联四上映了,要不要一起去看看? 又想骗我电影票,对不对? 呵呵,想去看了叫我呀 看来你工作不饱和呀 哪有,这两天我刚基于redis写了一个分布式锁,很简单 不管你基于什么做分布式锁,你觉得很 ...

  4. Tomcat7基于Redis的Session共享实战二

    目前,为了使web能适应大规模的访问,需要实现应用的集群部署.集群最有效的方案就是负载均衡,而实现负载均衡用户每一个请求都有可能被分配到不固定的服务器上,这样我们首先要解决session的统一来保证无 ...

  5. 使用Redis做预定库存缓存功能

    最近在自己的工作中,把其中一个PHP项目的缓存从以前的APC缓存逐渐切换到Redis中,并且根据Redis所支持的数据结构做了库存维护功能.缓存是在业务层做的,准确讲应该是在MVC模型中Model的O ...

  6. 使用Redis做MyBatis的二级缓存

    使用Redis做MyBatis的二级缓存 通常为了减轻数据库的压力,我们会引入缓存.在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数据库了. 如果没有才去 ...

  7. redis 的简单命令

    以下实例讲解了如何启动 redis 客户端: 启动 redis 客户端,打开终端并输入命令 redis-cli.该命令会连接本地的 redis 服务. $redis-cli redis > re ...

  8. 使用Redis做分布式

    一 为什么使用 Redis 在项目中使用 Redis,主要考虑两个角度:性能和并发.如果只是为了分布式锁这些其他功能,还有其他中间件 Zookpeer 等代替,并非一定要使用 Redis. 性能: 如 ...

  9. Redis源码阅读(二)高可用设计——复制

    Redis源码阅读(二)高可用设计-复制 复制的概念:Redis的复制简单理解就是一个Redis服务器从另一台Redis服务器复制所有的Redis数据库数据,能保持两台Redis服务器的数据库数据一致 ...

随机推荐

  1. SpringBoot添加自定义消息转换器

    首先我们需要明白一个概念:springboot中很多配置都是使用了条件注解进行判断一个配置或者引入的类是否在容器中存在,如果存在会如何,如果不存在会如何. 也就是说,有些配置会在springboot中 ...

  2. 【第三十一章】 elk(2)- 第二种架构(最常用架构)

    参考:http://linuxg.blog.51cto.com/4410110/1761757 最常用架构: 一.安装redis 1.下载:http://redis.io/download 2.解压后 ...

  3. BFS-迷宫问题-用宽度(广度)优先搜索解决最优路径问题

    题目: 给定一个大小为 N×M 的迷宫.迷宫由通道和墙壁组成,每一步可以向邻接的上下左右四格 的通道移动.请求出从起点到终点所需的最小步数.请注意,本题假定从起点一定可以移动 到终点. 限制条件;N, ...

  4. Pro Git读书笔记 - 分支

    Git 分支介绍. 几乎所有的版本控制系统都以某种形式支持分支. 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线. 在很多版本控制系统中,这是一个略微低效的过程--常常需要完全创 ...

  5. Java中的this

    首先this作为关键字其实是随着对象的创建而产生的,当我们调用对象的一个方法的时候: 例如: A a = new A(); a.f(1)  其实我们可以理解为a.f(a,1) 编译器默默的把所操作的对 ...

  6. ubuntu 14.04(desktop amd 64) nginx 安装启动停止

    sudo apt-get install nginx 关闭: sudo service nginx stop 启动: sudo nginx

  7. c语言 快速排序

    #include<stdio.h> #include<stdlib.h> #define BUF_SIZE 10 void display(int array[], int m ...

  8. c++ primer plus 第二章 课后题答案

    #include<iostream> using namespace std; int main() { cout << "My name is Jiantong C ...

  9. 3-19(晚) require_relative 和 require. === operator的解释。

    kernel#require_relative Ruby tries to load the library named string relative to the requiring file's ...

  10. 安装 tensorflow 时遇到 OSError: [Errno 1] Operation not permitted 的解决办法

    Installing collected packages: numpy, scipy, six, pyyaml, Keras, opencv-python, h5py, html5lib, blea ...