多线程提提速吧

简介: 爬虫用线程提速吧,用斗图网来做个对比。普通爬虫,没用线程的例子:import re,os,requests,timefrom urllib import requestfrom lxml import etreefrom fake_usera...

爬虫用线程提速吧,用斗图网来做个对比。

普通爬虫,没用线程的例子:

import re,os,requests,time
from urllib import request
from lxml import etree
from fake_useragent import UserAgent

def get_url(url):
    ua = UserAgent().random
    headers = {'User-Agent':ua}
    r = requests.get(url,headers=headers)
    if r.status_code == 200:
        print('请求成功')
        return r.text

def parse(html):
    html_data = etree.HTML(html)
    # 这里获取的图片不包括 GIF 动图的形式,注意这种写法
    imgs = html_data.xpath('//div[@class="page-content text-center"]//img[@class!="gif"]')
    for img in imgs:
        image_url = img.get('data-backup')
        # 获取图片链接的后缀 单词是后缀的意思
        suffixs = os.path.splitext(image_url)[1]
        suffix = suffixs.replace(suffixs,'.jpg')
        # 获取图片标题及剔除标题特殊字符
        img_title = img.get('alt')
        img_title = re.sub('[?\?。,\.!!]','',img_title)

        filenamre = img_title + suffix
        request.urlretrieve(image_url,'images/'+filenamre) # 保存图片到文件夹 images 下
        print('保存图片成功')

def main():
    for i in range(1,2):
        print('第 %d 页' % i)
        url = 'https://www.doutula.com/photo/list/?page={}'.format(i)
        html = get_url(url)
        parse(html)

if __name__ == '__main__':
    start = time.time()
    main()
    end = time.time()
    print('共运行了%s秒' % (end - start))

这里获取一页68图片,看一下运行速度:


img_4faf54179b849033324c8d398f7d038a.png
运行速度

生产者消费者模型

在这个例子中,定义两个队列,一个是获取页面的队列,一个是获取图片链接的队列。那么生产者就是这两个队列,消费者完成的任务就是下载图片到本地。
上述代码改造一下:
在main() 函数中,定义这两个队列:


def main():
    page_queue = Queue(100) # 创建下载页面的队列
    image_queue = Queue(1000) # 创建获取具体表情 url 的队列
    for i in range(1,10):
        print('第 %d 页' % i)
        url = 'https://www.doutula.com/photo/list/?page={}'.format(i)
        page_queue.put(url)

下载页面的队列的容量是100,put 方法将获取页面的url传入获取页面的队列中。

创建生产者模型:


# 创建生产者队列
class Procuder(threading.Thread):
    # 重写父类构造函数,继承父类所有方法,同时添加两个参数
    def __init__(self,page_queue,image_queue,*args,**kwargs):
        super(Procuder,self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        self.image_queue = image_queue

    def run(self):
        while True:
            if self.page_queue.empty(): # 如果下载页面的队列为空,就结束循环
                break
            url = self.page_queue.get() # 获取页面url,交由解析图片url的方法解析图片url
            self.parse(url)

    def parse(self,url):
        ua = UserAgent().random
        headers = {'User-Agent':ua}
        r = requests.get(url,headers=headers)
        html_data = etree.HTML(r.text)
        # 这里获取的图片不包括 GIF 动图的形式,注意这种写法
        imgs = html_data.xpath('//div[@class="page-content text-center"]//img[@class!="gif"]')
        for img in imgs:
            image_url = img.get('data-backup')
            # 获取图片链接的后缀 单词是后缀的意思
            suffixs = os.path.splitext(image_url)[1]
            suffix = suffixs.replace(suffixs,'.jpg')
            # 获取图片标题及剔除标题特殊字符
            img_title = img.get('alt')
            img_title = re.sub('[?\?。,\.!!\*]','',img_title)
            filenamre = img_title + suffix
            self.image_queue.put((image_url,filenamre)) # 把图片的url传递给获取图片的队列

创建消费者模型

class Consumer(threading.Thread):
    # 重写父类构造函数,继承父类所有方法,同时添加两个参数
    def __init__(self,page_queue,image_queue,*args,**kwargs):
        super(Consumer,self).__init__(*args,**kwargs)
        self.page_queue = page_queue
        self.image_queue = image_queue

    def run(self):
        while True:
            # 判断两个队列是否都为空,是就结束循环,不要让队列一直在等待操作
            if self.page_queue.empty() and self.image_queue.empty():
                break
            image_url,filename = self.image_queue.get() # 获取具体表情的url队列中的图片url和文件名
            request.urlretrieve(image_url,'images/'+ filename) # 保存图片到文件夹 images 下
            print(filename + '保存图片成功')

主程序

def main():
    page_queue = Queue(100) # 创建下载页面的队列
    image_queue = Queue(1000) # 创建获取具体表情 url 的队列
    for i in range(1,10):
        print('第 %d 页' % i)
        url = 'https://www.doutula.com/photo/list/?page={}'.format(i)
        page_queue.put(url)

    # 分别创建五个生产者,五个消费者,开启线程
    for i in range(5):
        t = Procuder(page_queue,image_queue)
        t.start()

    for i in range(5):
        t = Consumer(page_queue,image_queue)
        t.start()


if __name__ == '__main__':
    main()

几百张图片几乎在两三秒就下载完成了。

目录
相关文章
|
2月前
|
Python
解锁Python并发新世界:线程与进程的并行艺术,让你的应用性能翻倍!
【7月更文挑战第9天】并发编程**是同时执行多个任务的技术,提升程序效率。Python的**threading**模块支持多线程,适合IO密集型任务,但受GIL限制。**multiprocessing**模块允许多进程并行,绕过GIL,适用于CPU密集型任务。例如,计算平方和,多线程版本使用`threading`分割工作并同步结果;多进程版本利用`multiprocessing.Pool`分块计算再合并。正确选择能优化应用性能。
28 1
|
19天前
|
存储 监控 算法
深入探究Java线程池:提升并发性能的利器
在当今高度并发的应用开发中,Java线程池作为一种广泛应用的并发编程技术,提供了一种优雅且高效的线程管理方案。本文深入探究Java线程池的相关技术,涵盖其核心概念、优势、常见类型(如FixedThreadPool、CachedThreadPool、SingleThreadExecutor、ScheduledThreadPool、ForkJoinPool及WorkStealingPool)、核心参数配置、异常处理与监控方法,以及性能调优的最佳实践,帮助读者更好地理解和应用线程池,从而提升并发性能。
|
21天前
|
Rust 并行计算 安全
揭秘Rust并发奇技!线程与消息传递背后的秘密,让程序性能飙升的终极奥义!
【8月更文挑战第31天】Rust 以其安全性和高性能著称,其并发模型在现代软件开发中至关重要。通过 `std::thread` 模块,Rust 支持高效的线程管理和数据共享,同时确保内存和线程安全。本文探讨 Rust 的线程与消息传递机制,并通过示例代码展示其应用。例如,使用 `Mutex` 实现线程同步,通过通道(channel)实现线程间安全通信。Rust 的并发模型结合了线程和消息传递的优势,确保了高效且安全的并行执行,适用于高性能和高并发场景。
31 0
|
1月前
|
编译器 开发者 UED
"解锁hyengine编译性能新纪元:快路径优化技术揭秘,直击热点代码效率瓶颈,让你的应用飞起来!"
【8月更文挑战第21天】hyengine是一款现代化编程引擎,其编译性能直接影响应用质量。针对直接翻译opcode至机器码效率低下的问题,hyengine采用快路径优化策略,专注于热点代码的深度优化,以减少运行时开销。例如,通过循环展开技术减少控制指令开销,智能判断循环展开时机与程度。此外,还运用函数内联等技术进一步提升性能,如内联小函数以减少调用开销。未来将持续探索更多优化方案,提升用户体验,并欢迎开发者共同参与技术进步。
23 2
|
26天前
|
存储 缓存 NoSQL
进程内缓存助你提高并发能力!
进程内缓存助你提高并发能力!
|
2月前
|
缓存 自然语言处理 Java
浅析JAVA日志中的性能实践与原理解释问题之减少看得见的业务开销问题如何解决
浅析JAVA日志中的性能实践与原理解释问题之减少看得见的业务开销问题如何解决
|
2月前
|
Java 测试技术 容器
多线程编程基础与并发问题解决方案
多线程编程基础与并发问题解决方案
|
3月前
|
缓存 并行计算 安全
【并发编程系列一】并发编年史:线程的双刃剑——从优势到风险的全面解析
【并发编程系列一】并发编年史:线程的双刃剑——从优势到风险的全面解析
|
4月前
|
消息中间件 缓存 安全
清华架构大牛剖析高并发与多线程的关系、区别,带你击穿面试难题
当提起这两个词的时候,是不是很多人都认为高并发=多线程? 当面试官问到高并发系统可以采用哪些手段来解决,是不是一脸懵逼?
|
Java Maven
批量任务体现多线程的威力!
批量任务体现多线程的威力!
80 0