Scrapy_Study03

2023-11-30 19:02:57 浏览数 (2)

Queue (队列对象)

Queue是python中的标准库,可以直接import Queue引用;队列是线程间最常用的交换数据的形式 python 的多线程的思考 对于资源,加锁是个重要的环节。因为python原生的lit,dict等 ,都是not thread safe的。而Queue ,是线程安全的,因此满足使用条件下,建议使用队列

  1. 初始化: class Queue Queue(maxsize) FIFO先进先出
  2. 包中的常用方法:
  • Queue. qsize() 返回队列的大小
  • Queue. empty()如果队列为空,返回True,反之False
  • Queue.ful()如果队列满了,返回True,反之False
  • Queue.full 与maxsize大小对应
  • Queue get([block[, timeout])获取队列, timeout等待时间
  1. 创建一一个“队列”对象
  • import Queue
  • myqueue = Queue. Queue(maxsize = 10)
  1. 将一个值放入队列中
  • myqueue .put(10)
  1. 将一个值从队列中取出
  • myqueue get()
生产者消费者模式的多线程爬虫 爬取斗图网

斗图网的爬取比较简单的,主要是 在消费者和生产者模式下进行数据爬取,定义生产者负责数据的获取解析提取,消费者存储数据。

代码语言:javascript复制
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的多进程来解决。

0 人点赞