多线程提提速吧

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

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

目录
相关文章
|
3月前
|
存储 NoSQL Redis
单线程模型想象不到的高并发能力、多路复用是效率杠杆
单线程模型想象不到的高并发能力、多路复用是效率杠杆
|
26天前
|
存储 缓存 监控
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(场景问题分析+性能影响因素)
【分布式技术专题】「缓存解决方案」一文带领你好好认识一下企业级别的缓存技术解决方案的运作原理和开发实战(场景问题分析+性能影响因素)
34 0
|
8月前
|
UED
直播平台源码优质平台技术:并行处理与线程优化的探索与实践
直播平台源码并行处理与线程优化的部分参考代码import concurrent.futures import numba @numba.jit(nopython=True) def process_data(data): result = data * 2 return result
直播平台源码优质平台技术:并行处理与线程优化的探索与实践
|
9月前
|
存储 SQL 缓存
提升性能的利器:理解线程池的使用、工作原理和优势
在Java中,创建和销毁线程开销较大,为了避免线程过多而带来使用上的开销。 所以我们需要对线程进行统一管理及复用,这就是我们要说的线程池。
|
11月前
|
Java Maven
批量任务体现多线程的威力!
批量任务体现多线程的威力!
65 0
|
JSON 缓存 JavaScript
提高系统吞吐量的一把利器:DeferredResult 到底有多强?
提高系统吞吐量的一把利器:DeferredResult 到底有多强?
|
存储 Web App开发 缓存
对你的 SPA 提提速
1. 监控 SPA 性能 2. 提升 SPA 性能(6种) a. 延迟渲染首屏下的内容 b. 非必要数据的懒加载 c. 缓存静态内容 d. 对实时性较强的应用使用WebSocket e. 使用JSONP/CORS绕过同源策略 f. CDN处理
使用线程池多线程优化大数据量项目 ✨ 每日积累
使用线程池多线程优化大数据量项目 ✨ 每日积累
使用线程池多线程优化大数据量项目 ✨ 每日积累
|
Kubernetes 负载均衡 算法
异步任务处理系统,如何解决业务长耗时、高并发难题?
阿里云函数计算 FC 为用户提供了开箱即用的,接近于Level ß3能力的异步任务处理服务。用户只需要创建任务处理函数,通过控制台,命令行工具,API/SDK,事件触发等多种方式提交任务,就可以弹性、可靠、可观测完备的方式处理任务。
异步任务处理系统,如何解决业务长耗时、高并发难题?
|
存储 缓存 负载均衡
C++高并发场景下读多写少的优化方案
C++高并发场景下读多写少的优化方案 述 一谈到高并发的优化方案,往往能想到模块水平拆分、数据库读写分离、分库分表,加缓存、加mq等,这些都是从系统架构上解决。单模块作为系统的组成单元,其性能好坏也能很大的影响整体性能,本文从单模块下读多写少的场景出发,探讨其解决方案,以其更好的实现高并发。 不同的业务场景,读和写的频率各有侧重,有两种常见的业务场景: 读多写少:典型场景如广告检索端、白名单更新维护、loadbalancer 读少写多:典型场景如qps统计 本文针对读多写少(也称一写多读)场景下遇到的问题进行分析,并探讨一种合适的解决方案。
559 0
C++高并发场景下读多写少的优化方案