QUEUE 简单介绍:
queue 模块:
queue 模块中有 Queue 类,LifoQueue、PriorityQueue 都继承了 Queue
maxsize:
maxsize 是实例化 Queue 类时的一个参数,默认为 0 小于等于0时, 默认无限个
Queue(maxsize=0) 可以控制队列中数据的容量
put :
Queue.put(block=True, timeout=None)
block:
用于设置是否阻塞, timeout 用于设置阻塞时等待时长 默认阻塞
put_nowait() = put(block=False) 这个相当于不阻塞
阻塞 : 当队列满了之后,put 就会阻塞,一直等待队列不再满时向里面添加数据
不阻塞: 当队列满了之后,如果设置 put 不阻塞,或者等待时长到了之后会报错:queue.Full
get : Queue.get(block=True, timeout=None) 默认阻塞
get_nowait() = get(block=False) 这个相当于不阻塞
阻塞
当队列空了之后,get 就会阻塞,一直等待队列中有数据后再获取数据
不阻塞
当队列空了之后,如果设置 get 不阻塞,或者等待时长到了之后会报错:_queue.Empty
full & empty:
Queue.empty()/Queue.full() 用于判断队列是否为空、满
尽量使用 qsize 代替
qsize:
Queue.qsize() 用于获取队列中大致的数据量
注意:在多线程的情况下不可靠 因为在获取 qsize 时,其他线程可能又对队列进行操作了
join
join 会在队列存在未完成任务时阻塞,等待队列无未完成任务,需要配合 task_done 使用
task_done
执行一次 put 会让未完成任务 +1 ,但是执行 get 并不会让未完成任务 -1 ,
需要使用 task_done 让未完成任务 -1 ,否则 join 就无法判断
队列为空时执行会报错:ValueError: task_done() called too many times
q = queue.Queue()
q.qsize() 返回队列的大小
q.empty() 如果队列为空,返回True,反之False
q.full() 如果队列满了,返回True,反之False
q.full 与 maxsize 大小对应
q.get([block[, timeout]]) 获取队列,timeout等待时间
q.get_nowait() 相当q.get(False)
q.put(item) 写入队列,timeout等待时间
q.put_nowait(item) 相当q.put(item, False)
q.task_done() 在完成一项工作之后,q.task_done() 函数向任务已经完成的队列发送一个信号
q.join() 实际上意味着等到队列为空,再执行别的操作
import os
import sys
import time
import pymysql
import traceback
import pandas as pd
import datetime
path = os.path.dirname(os.getcwd())
sys.path.append(path)
from db_config.sql_clause import SQL_CLAUSE
from spider.elm_real_time_data import main as elm_main
from spider.mt_real_time_data import main as mt_main
from log_config.log_setting import get_logger
from utils.proxy import get_ac_ip
from utils.proxies_mongo import get_random_ip
from config import MYSQL_CONFIG
from queue import Queue
from threading import Thread
logger = get_logger("custom_start_spider", "custom_start_spider")
connection = pymysql.connect(**MYSQL_CONFIG['myq_db'])
def producer(producer_queue):
"""
:return: 创建生产者
"""
hour = datetime.datetime.now().strftime('%H')
day = datetime.datetime.now().strftime('%Y-%m-%d')
# 11、12、13 正餐爬虫
if 11 <= int(hour) < 14:
get_data_sql = SQL_CLAUSE.get_data_sql2
# 16、17、18、21、23 正餐 + 夜宵 爬虫
else:
get_data_sql = SQL_CLAUSE.get_data_sql3
df = pd.read_sql(get_data_sql, connection)
# sql1 = f'between "{day} {int(hour)}:00:00" and "{day} {int(hour) + 1}:00:00"'
sql1 = f'> "{day} {int(hour)}:00:00"' # sql 修改
sql = SQL_CLAUSE.filter_sql.format(sql1=sql1)
df2 = pd.read_sql(sql, connection)
# 去重
df = df[~df["shop_code"].isin(df2["shop_code"].tolist())]
# 添加队列数据
for index, row in df.iterrows():
account = row.to_dict()
producer_queue.put(account)
producer_queue.join() # 队列不消费完成, 会一直阻塞, 生产者线程不会结束
def consumer(producer_queue):
"""
:param producer_queue: 队列
:return: 消费者消费
"""
# proxy = get_ac_ip() # 芝麻 ip
# proxy = None
while True:
account = producer_queue.get()
try:
plat = account.get("plat") # 2 代表饿了么 3 代表美团
if plat == "elm":
proxy = get_random_ip("elm")
result = elm_main(account, proxy)
elif plat == "mt":
proxy = get_random_ip("mt")
result = mt_main(account, proxy)
else:
logger.info("---不存在---该平台")
result = True
logger.info(f"---------result---------:{result}" )
if result is False:
producer_queue.put(account) # 未消费再次put进去
logger.info("======未消费再次put=======")
logger.info("------------spider end--------------")
except Exception as e:
logger.error(traceback.format_exc())
logger.error(e)
producer_queue.put(account) # 未消费再次put进去
logger.error("======出错---未消费再次put=======")
producer_queue.task_done() # 通知生产者,队列已消化完
def start_main_spider():
start = time.time()
queue = Queue()
producer_thread = Thread(target=producer, args=(queue,)) # 生产者单线程
producer_thread.start()
for index in range(10): # 消费者多线程
consumer_thread = Thread(target=consumer, args=(queue,))
consumer_thread.daemon = True # 设置守护线程, 执行完毕后自己结束, 否则会一直僵死在这
consumer_thread.start()
producer_thread.join() # 等待主线程结束
end = time.time()
print('总耗时:%s' % (end - start))
if __name__ == '__main__':
start_main_spider()