python queue模块详解

大家好,我是剑南。

本篇文章,为大家带来的是queue模块的详解!

初识queue模块

queue模块实现了多生产者、多消费者队列。这特别适用于消息必须安全地在多线程交换的线程编程。模块中的Queue类实现了所需要的锁定语义。

该模块实现了三种类型的队列,它们的区别是任务取回的顺序。在FIFO队列中,先添加任务的先取回。在LIFO队列中,最后添加的任务先取回(该操作类似于堆栈)。在优先级队列中,条目将保持排序(使用heapq模块)并且最小值的任务第一个返回。

创建“队列”对象

import queue

q = queue.Queue(maxsize=5)

maxsize是一个整数,用于设置可以放入队列中的任务数的上限,当达到这个大小的时候,插入操作将阻塞至队列中的任务被消除掉。如果maxsize小于等于0,任务数量为无限大。

队列添加数据

q.put(1)
q.put(2)
q.put(3)
q.put(4)
q.put(5)
print(q.full())

运行结果为:True。

Queue.full():表示当队列任务已满时,返回的结果为True。如果full()返回True不保证后续调用get()不被阻塞,同样的道理,如果full()返回False也不保证后续调用put()不被阻塞。

Queue.put(item, block=True, timeout=None):将Item放入队列,如果可选参数block是True并且timeout是None,则在必要时阻塞至有空闲插槽可用,如果timeout是正数,将最多阻塞timeout秒,如果这段时间没有可用的空闲插槽,则引发full异常。反之block为False,如果插槽空闲,则立即使用,把item放入队列,否则引发Full异常。

判断队列是否为空

Queue.empty():如果队列为空,则返回True,否则返回False。如果empty()返回True,不保证后续调用put()会被阻塞。类似的,如果empty()返回False,也不保证后续调用get()会被阻塞。

获取队列的大小

Queue.qsize():返回队列的大小。注意qsize>0不保证后续的get()有可能被阻塞,qsize<maxsize也不保证put()有可能被阻塞。

获取队列中数据

Queue.get(block=True, timeout=None):从对列中移除并返回一个数据。当队列为空值,将一直等待。

其他的Queue对象

Queue.task_done():表示前面的排队任务已经完成,被队列的消费者线程使用。每个get()被用于获取一个任务,后续调用task_done()告诉队列,该任务的处理已经完成。如果join()当前正在阻塞,在所有条目都被处理后,将解除阻塞(意味着每个put()进队列的条目task_done()都被收到)。

Queue.join():阻塞至对列的所有数据都被接收和处理完毕。当数据被添加到队列时,未完成的任务的计数就会增加。每当消费者线程调用task_done()表示这个条目已经被收回,未完成的计数就会减少,当完成计数降到0的时候,阻塞就会解除。

简单示例

下面的例子要展示的是,我们应该如何使用代码将等待的任务完成。

具体代码,如下所示:

import threading, queue

q = queue.Queue()

def worker():
    while True:
        item = q.get()
        print(f'Working on {item}')
        print(f'Finished {item}')
        q.task_done()

# turn-on the worker thread
threading.Thread(target=worker, daemon=True).start()

# send thirty task requests to the worker
for item in range(30):
    q.put(item)
print('All task requests sent\n', end='')

# block until all tasks are done
q.join()
print('All work completed')

实战-豆瓣短评

对于本次实战,我采用的网站是豆瓣电影。小伙伴们可以自己去找一部电影,爬取里面的短评。

这次,我爬取的电影是《我不是药神》的短评,采用的便是队列的技术。

爬取思路

image

在上图中,框出来的数据,就是我要获取的数据,并下得到的数据保存到csv文件中。

之所以选择上面的图片,其实是有原因的,不知道你发现没有,在上面的图片中第一条评论是没有给评价的,因此,当我们按照相同的规则去获取数据时,便容易出现异常。

其次,短评的数据量一共有52万条,每页20条,并且只能获取到前25页的数据。再加上,如果没有给出评价的用户,我直接过滤,因此,最后获取下来的数据应该是不足500条的。

再这里,要做的事情就是要完成翻页的操作。

在本次编码中,我的思路是采用两个线程与两个队列来完成。一个线程用于获取数据,一个线程用于保存数据;其中的一个队列用于保存25页的URL地址,另一个队列用于保存获取的数据。

获取数据

主线程

首先在主线程中,创建两个队列,并将URL添加进保存URL的队列中。

具体代码,如下所示:

def main():
    p_queue = Queue()   # 保存URL
    d_queue = Queue()   # 保存数据
    for page in range(25):
        url = f'https://movie.douban.com/subject/26752088/comments?start={page*20}&limit=20&status=P&sort=new_score'
        p_queue.put(url)

获取数据

先说一下前提,这里我才用的解析库是lxml,因此,小伙伴们需要自行熟悉xpath语法。

这里创建一个获取数据的类,这个类继承thread,方便接下来开启线程。

具体代码,如下所示:

class GetData(threading.Thread):
    def __init__(self, page_queue, data_queue):
        super(GetData, self).__init__()
        self.page_queue = page_queue
        self.data_queue = data_queue
        self.headers = {
            'User-Agent': 你的user-agent,
            'Cookie': '你的cookie'
        }

    def run(self):
        while True:
            if self.data_queue.empty() and self.page_queue.empty():
                break
            url = self.page_queue.get()
            self.parse_page(url)

    def parse_page(self, url):
        html = etree.HTML(requests.get(url, headers=self.headers).content.decode('utf-8'))
        comment_items = html.xpath('//div[@class="comment-item "]')
        for comment_item in comment_items:
            try:
                user = comment_item.xpath('.//span[2]/a/text()')[0]
                comment_time = comment_item.xpath('.//span[2]//span[3]/@title')[0]
                star = comment_item.xpath('.//span[2]//span[2]/@title')[0]
                content = comment_item.xpath('.//span[@class="short"]/text()')[0]
                self.data_queue.put((user, comment_time, star, content))
            except:
                continue

保存数据

同样的,这里创建一个保存数据的类,这个类也是继承thread,也是方便开启线程。

具体代码,如下所示:

class SaveData(threading.Thread):
    def __init__(self, page_queue, data_queue):
        super(SaveData, self).__init__()
        self.data_queue = data_queue
        self.page_queue =page_queue

    def run(self):
        with open('data.csv', 'w', newline='', encoding='utf-8-sig') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow(['user', 'comment_time', 'star', 'content'])

        while True:
            if self.data_queue.empty() and self.page_queue.empty():
                break
            user, comment_time, star, content = self.data_queue.get()
            print(self.data_queue.get())

            with open('data.csv', 'a', newline='', encoding='utf-8-sig') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerow([user, comment_time, star, content])

开启多线程

多线程实在主线程中开启,具体代码,如下所示:

def main():
    p_queue = Queue()
    d_queue = Queue()
    for page in range(25):
        url = f'https://movie.douban.com/subject/26752088/comments?start={page*20}&limit=20&status=P&sort=new_score'
        p_queue.put(url)
    for x in range(5):
        t1 = GetData(p_queue, d_queue)
        # t1.daemon = True
        t1.start()
        t2 = SaveData(p_queue, d_queue)
        # t2.daemon = True
        t2.start()

数据展示

image

不到4秒钟,便将短评数据都抓取下来了,多线程的效率是不是要比单线程要高很多呀!

最后

在本次的分享中,大家要熟悉与了解queue的使用方法,在后期分享中经常要用到,希望小伙伴们能够掌握。

我是剑南,如果文章给到了你帮助,请你点个【赞】与【再看】。

文章的每一个字都是我用心敲出来的,点个【再看】,让我知道,你也是陪着我一起努力的人。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容