Queue (队列对象)
Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式
python 的多线程的思考
对于资源,加锁是个重要的环节。因为python原生的lit,dict等 ,都是not thread safe的。而Queue ,是线程安全的,因此满足使用条件下,建议使用队列
- 初始化: class Queue Queue(maxsize) FIFO先进先出
- 包中的常用方法:
- Queue. qsize() 返回队列的大小
- Queue. empty()如果队列为空,返回True,反之False
- Queue.ful()如果队列满了,返回True,反之False
- Queue.full 与maxsize大小对应
- Queue get([block[, timeout])获取队列, timeout等待时间
- 创建一一个“队列”对象
- import Queue
- myqueue = Queue. Queue(maxsize = 10)
- 将一个值放入队列中
- myqueue .put(10)
- 将一个值从队列中取出
- myqueue get()
生产者消费者模式的多线程爬虫 爬取斗图网
斗图网的爬取比较简单的,主要是 在消费者和生产者模式下进行数据爬取,定义生产者负责数据的获取解析提取,消费者存储数据。
import os
from queue import Queue
from urllib import request
import threading
import re
import requests
from lxml import etree
# 生产者 ,负责获取正确响应内容并进行解析
# 从queue 中取得url,将数据放入另一个queue中
class Producer(threading.Thread):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
'Accept-Encoding': 'gzip, deflate, br',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive',
'Host': 'www.doutula.com',
'sec-ch-ua': '"Google Chrome";v = "89", "Chromium";v = "89", ";Not A Brand";v = "99"',
'sec-ch-ua-mobile': '?0',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Sec-Fetch-User': '?1',
'Upgrade-Insecure-Requests': '1'
}
def __init__(self, page_queue, img_queue):
# 调用thread父类的初始化方法
super(Producer, self).__init__()
self.page_queue = page_queue
self.img_queue = img_queue
def run(self):
while True:
if self.page_queue.empty():
break
url = self.page_queue.get()
self.parse_page(url)
def parse_page(self, url):
# print(url)
response = requests.get(url, headers=self.headers)
# print(response.request.url)
# print(response.text)
text = response.text
html = etree.HTML(text)
# //*[@id="pic-detail"]/div/div[2]/div[2]/ul/li/div/div/a[1]/img
imgs = html.xpath('//*[@id="pic-detail"]/div/div[2]/div[2]/ul/li/div/div/a/img[2]')
if len(imgs) == 0:
imgs = html.xpath('//*[@id="pic-detail"]/div/div[2]/div[2]/ul/li/div/div/a/img')
for img in imgs:
img_url = img.get('data-original')
# print(img_url)
alt = img.get('alt')
alt = re.sub(r'[\??\.,。》>!:+\-\\#《<?/|!\*]', '', alt)
suffix = os.path.splitext(img_url)[1]
filename = alt + suffix
self.img_queue.put((img_url, filename))
# 消费者,负责数据的持久化
# 将数据存在本地磁盘
class Consumer(threading.Thread):
def __init__(self, page_queue, img_queue):
super(Consumer, self).__init__()
self.page_queue = page_queue
self.img_queue = img_queue
def run(self):
while True:
if self.img_queue.empty() and self.page_queue.empty():
break
img_url, filename = self.img_queue.get()
request.urlretrieve(img_url, './images/'+filename)
print(filename + ' is ok!')
def main():
page_queue = Queue(100)
img_queue = Queue(7000)
for x in range(1, 101):
url = 'https://www.doutula.com/photo/list/?page=%d' % x
page_queue.put(url)
for x in range(100):
d = Producer(page_queue, img_queue)
d.start()
for x in range(100):
d = Consumer(page_queue, img_queue)
d.start()
if __name__ == '__main__':
main()
python 下的多线程并不是真正意义上的多线程 由于有GIL 全局解释锁 的存在,在实际上在一个时间下只有一段代码被执行,并不是真正的并发执行。python的多线程适合处理 I/O 密集的程序,对于计算较多的可以通过python的多进程来解决。