多线程提提速吧

简介: 爬虫用线程提速吧,用斗图网来做个对比。普通爬虫,没用线程的例子: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()

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

目录
相关文章
|
5月前
|
存储 NoSQL Redis
单线程模型想象不到的高并发能力、多路复用是效率杠杆
单线程模型想象不到的高并发能力、多路复用是效率杠杆
|
28天前
高性能并行编程与优化 | 第02讲回家作业
本文是关于高性能并行编程与优化课程的第二讲回家作业,包括作业要求、初始运行结果、抄的答案以及改进后的运行结果。作业内容涉及对双链表类`List`的修改,要求避免不必要的拷贝、修复智能指针问题、实现深拷贝构造函数、解释为什么可以删除拷贝赋值函数以及改进`Node`的构造函数。同时,还提供了如何提交作业、设置https代理的链接以及评分规则。
高性能并行编程与优化 | 第02讲回家作业
|
28天前
高性能并行编程与优化 | 第03讲回家作业
本文是关于高性能并行编程与优化课程的第三讲回家作业,包括题目要求、代码答案抄写以及成功运行的截图。
高性能并行编程与优化 | 第03讲回家作业
|
28天前
|
C++
高性能并行编程与优化 | 第01讲回家作业
本文是关于高性能并行编程与优化的回家作业,涉及CMake错误解决、编译问题处理、代码和编译结果分享、使用方法说明以及躺坑记录。
高性能并行编程与优化 | 第01讲回家作业
|
28天前
|
算法 编译器
高性能并行编程与优化 | 第04讲回家作业
本文是关于高性能并行编程与优化课程的第四讲回家作业,包括作业要求、原始代码运行结果、优秀的代码答案以及优化后的运行结果和解析。作业目标是利用所学知识优化多体引力求解器的代码,要求不能使用多线程并行和算法复杂度优化,但可以通过编译器和平台优化来提升性能。
高性能并行编程与优化 | 第04讲回家作业
|
2月前
|
编译器 开发者 UED
"解锁hyengine编译性能新纪元:快路径优化技术揭秘,直击热点代码效率瓶颈,让你的应用飞起来!"
【8月更文挑战第21天】hyengine是一款现代化编程引擎,其编译性能直接影响应用质量。针对直接翻译opcode至机器码效率低下的问题,hyengine采用快路径优化策略,专注于热点代码的深度优化,以减少运行时开销。例如,通过循环展开技术减少控制指令开销,智能判断循环展开时机与程度。此外,还运用函数内联等技术进一步提升性能,如内联小函数以减少调用开销。未来将持续探索更多优化方案,提升用户体验,并欢迎开发者共同参与技术进步。
31 2
|
2月前
|
存储 缓存 NoSQL
进程内缓存助你提高并发能力!
进程内缓存助你提高并发能力!
|
4月前
|
缓存 并行计算 安全
【并发编程系列一】并发编年史:线程的双刃剑——从优势到风险的全面解析
【并发编程系列一】并发编年史:线程的双刃剑——从优势到风险的全面解析
|
5月前
|
设计模式 监控 安全
Java多线程编程优化实践
本文将探讨在Java多线程编程中如何进行优化实践,通过合理的设计和技巧,提高程序性能、避免常见的问题。从线程池的使用、锁的选择到并发数据结构的应用,全面介绍优化多线程编程的方法与技巧。
|
架构师
架构系列——架构师必备基础:并发、并行与多线程关系
架构系列——架构师必备基础:并发、并行与多线程关系