多线程提提速吧

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

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

目录
相关文章
|
4月前
|
存储 NoSQL Redis
单线程模型想象不到的高并发能力、多路复用是效率杠杆
单线程模型想象不到的高并发能力、多路复用是效率杠杆
|
2月前
|
设计模式 监控 安全
Java多线程编程优化实践
本文将探讨在Java多线程编程中如何进行优化实践,通过合理的设计和技巧,提高程序性能、避免常见的问题。从线程池的使用、锁的选择到并发数据结构的应用,全面介绍优化多线程编程的方法与技巧。
|
9月前
|
Java 程序员 开发者
疫情过后,Java开发者如何应对多线程与高并发面试题目?
发某粉丝年前参加某个NB企业的面试题列表: 聊聊synchronized的CPU原语级别实现 有一千万个数,写一个程序进行高效求和 已知2开平方为1.414,如何不用数学库,求开平方的值,精确到小数点儿后面10位 编码实现两个线程,线程A不断打印1-10的数字,要求在打印到第五个数字的时候通知线程B 自定义线程池需要指定哪7个参数,为什么不建议使用JUC内置线程池? 高并发、任务执行时间短的业务怎样使用线程池? 并发不高、任务执行时间长的业务怎样使用线程池? 并发高、业务执行时间长的业务怎样使用线程池? 设计一个12306网站,能够撑住最高百万级别TPS(淘宝最高54万TPS),你该如何实现
|
Java Maven
批量任务体现多线程的威力!
批量任务体现多线程的威力!
66 0
|
存储 Web App开发 缓存
对你的 SPA 提提速
1. 监控 SPA 性能 2. 提升 SPA 性能(6种) a. 延迟渲染首屏下的内容 b. 非必要数据的懒加载 c. 缓存静态内容 d. 对实时性较强的应用使用WebSocket e. 使用JSONP/CORS绕过同源策略 f. CDN处理
|
JSON 缓存 JavaScript
提高系统吞吐量的一把利器:DeferredResult 到底有多强?
提高系统吞吐量的一把利器:DeferredResult 到底有多强?
使用线程池多线程优化大数据量项目 ✨ 每日积累
使用线程池多线程优化大数据量项目 ✨ 每日积累
使用线程池多线程优化大数据量项目 ✨ 每日积累
|
Java 程序员 API
十分钟带你深入了解多线程—— Java虚拟机对锁优化所做的努力
十分钟带你深入了解多线程—— Java虚拟机对锁优化所做的努力
106 0
|
缓存 Java 编译器
多线程基础——构建高并发应用必会
java多线程编程技术快速学习,经验总结
1020 0
|
应用服务中间件 数据库 缓存
系统性能提升优先法宝|缓存应用实践
缓存是系统性能提升优先法宝,在互联网应用系统中,屡试不爽。网上有很多资料介绍缓存理论及使用策略,本文就不再涉及了,今天简单将缓存做个归类,重点分享以前在实际业务中碰到场景以及如何使用。
1777 0