工作中的接口需要发送2个参数,url和md5,但是一般情况下md5都是将文件下载后再计算出来的。少量的话可以手动下载文件后,通过md5sum命令计算出来,如果有批量的url,手动计算就不太现实,所以考虑通过程序并发实现md5计算。

1、环境

win10 + python3.6.5

如果手头有linux或者mac环境的话,可以尝试下多线程效果更明显

2、通过multiprocessing模块的回调功能实现

2.1 思路

  1. 先遍历指定文件(url.txt),拿到每个待下载的url;
  2. 每遍历一个url,就调用apply_asyn函数将新的请求提交到Pool中,将url传递给下载函数,下载完成后计算md5;
  3. 将上一步获得的url和md5拼接完毕,交给回调函数输出到文件

2.2 代码实现

进程池的apply_asyn函数是异步非阻塞的,而且支持回调功能,当指定的任务执行完毕后会自动调用回调函数进行处理,无需再不同的进程间同步数据。
所以在上面的需求中,我们只要拿到文件内容的md5之后,直接传递给回调函数写入另外的文件中即可。

# encoding=utf-8
"""
多进程下载文件并计算md5,写入到指定文件
"""import os
import time
import requests
import hashlib
from multiprocessing import Pool
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)base_path = os.path.dirname(os.path.realpath(__file__))def get_md5(content):"""计算md5"""hash = hashlib.md5(content)return hash.hexdigest()def mycallback(content):"""将内容追加到指定文件"""# print(content)with open(os.path.join(base_path, 'url_md5.txt'), "a+") as fp:fp.write(content)def download(url):"""根据url下载文件,并计算出md5"""# https不校验证书res = requests.get(url, verify=False)# print(res.status_code)md5 = get_md5(res.content)content = "%s  %s\n" % (url.strip(), md5)return contentif __name__ == "__main__":start = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))print("【%s】开始写入数据" % start)# 创建一个进程池pool = Pool()poolObjects = []url_file = os.path.join(base_path, 'url.txt')with open(url_file) as fp:for url in fp:url = url.strip()if not url:continueobj = pool.apply_async(download, args=(url,), callback=mycallback)poolObjects.append(obj)pool.close()pool.join()# 调试用,有问题的话可以看到每个进程的出错信息for f in poolObjects:f.get()end = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))print("【%s】所有数据都写入完成" % end)

url.txt文件内容如下:

https://www.wandoujia.com/apps/596157/download/dot?ch=detail_normal_dl
https://www.wandoujia.com/apps/566489/download/dot?ch=detail_normal_dl
https://www.wandoujia.com/apps/5919749/download/dot?ch=detail_normal_dl
https://www.wandoujia.com/apps/280155/download/dot?ch=detail_normal_dl
https://www.wandoujia.com/apps/8301911/download/dot?ch=detail_normal_dl

结果文件:

3、通过协程实现

3.1 思路

受制于操作系统的性能,能够开启的进程和线程数都是有限的,而且多进程资源的开销相对来说是最大的,所以各种编程语言对协程这种充分利用单线程资源,支持的也越来越好。因此也尝试下用协程实现下上面的需求。

3.2 代码实现

# encoding=utf-8"""
协程的方式下载文件
"""
import os
import time
import hashlib
import asyncio
import requests
import queue
import threading
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)base_path = os.path.dirname(os.path.realpath(__file__))def get_md5(content):"""计算md5"""hash = hashlib.md5(content)return hash.hexdigest()def write_to_file(q):"""将队列中的内容写入到文件"""with open(os.path.join(base_path, "result.txt"), "a+") as fp:while 1:# 阻塞等待content = q.get()if content == "end":breakfp.write(content)async def download(q, url):"""创建协程函数,根据指定url下载文件,并将url和md5写入到队列"""print("开始下载")# 获取事件循环loop = asyncio.get_event_loop()# requests模块不支持异步,所以用线程池配合实现future = loop.run_in_executor(None, requests.get, url)res = await future#计算md5url = url.strip()md5 = get_md5(res.content)q.put("%s %s\n" % (url, md5))if __name__ == "__main__":url_list = ["https://www.wandoujia.com/apps/8301911/download/dot?ch=detail_normal_dl","https://www.wandoujia.com/apps/8301911/download/dot?ch=detail_normal_dl"]start = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))print("【%s】开始写入数据" % start)q = queue.Queue()# 开启写入结果的线程t = threading.Thread(target=write_to_file, args=(q,))t.start()tasks = [download(q, url) for url in url_list]loop = asyncio.get_event_loop()loop.run_until_complete(asyncio.wait(tasks))# 向队列中输出特殊的结束标志q.put("end")t.join()end = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))print("【%s】所有数据都写入完成" % end)

说明:

  • 函数定义前加上async表示这是一个协程函数,执行协程函数(函数名加括号),创建协程对象,但是和普通函数不一样的地方就是,协程函数内部代码是不会执行的。必须将协程对象交给事件循环执行;
  • 方式二再进一步简化就是使用协程+aiohttp模块,这个是支持异步的http包

结果和方式一类似。

【python】并发实现文件下载并计算md5相关推荐

  1. Python实现计算MD5

    本文章和我的博客同时发布:厉害博客 MD5 是 Message-Digest Algorithm5 的缩写,MD5 是一种信息摘要算法. 在平时的开发中可能要计算 MD5,可是自己写一个算法很麻烦,这 ...

  2. 深入浅出讲解Python并发编程

    微信公众号:运维开发故事,作者:素心 Python并发编程 本文比较长,绕的也比较快,需要慢慢跟着敲代码并亲自运行一遍,并发编程本身来说就是编程里面最为抽象的概念,单纯的理论确实很枯燥,但这是基础,基 ...

  3. Python 并发编程之使用多线程和多处理器

    在Python编码中我们经常讨论的一个方面就是如何优化模拟执行的性能.尽管在考虑量化代码时NumPy.SciPy和pandas在这方面已然非常有用,但在构建事件驱动系统时我们无法有效地使用这些工具.有 ...

  4. python并发编程之协程

    python并发编程之协程 1.协程: 单线程实现并发 在应用程序里控制多个任务的切换+保存状态 优点: 应用程序级别速度要远远高于操作系统的切换 缺点: 多个任务一旦有一个阻塞没有切,整个线程都阻塞 ...

  5. Python并发编程理论篇

    Python并发编程理论篇 前言 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例的人,却不知道如何去学习更加高深的知识 ...

  6. python里如何计算大文件的md5

    在python3中,有了一个hashlib,可以用来计算md5,这里先给出一个简单的例子: import hashlibsstr="i love hanyu" print(hash ...

  7. 【Python成长之路】python并发学习:多进程与多线程的用法及场景介绍

    刚开始学习Python 并发查询或者并发读写时,看到大神们说,多线程是python的鸡肋,要学就学多进程.好吧,我连多线程怎么写都不知道呢. 因此,就写了以下的示例代码.代码目的是将test.txt文 ...

  8. python并发编程之semaphore(信号量)_python 之 并发编程(守护进程、互斥锁、IPC通信机制)...

    9.5 守护进程 主进程创建守护进程 其一:守护进程会在主进程代码执行结束后就立即终止 其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic process ...

  9. Python并发编程之进程间通信

    Python并发编程之进程间通信 一.Python进程间通信 二.使用队列Queue进行进程间通信 2.1 Queue实例中的方法和属性 2.2 使用Queue进行进程间通信的代码示例 三.使用管道P ...

最新文章

  1. ValueError: cannot convert to ‘int64‘-dtype NumPy array with missing values. Specify an appropriate
  2. 8个容易被忽略但不能忽略的SD-WAN功能-Vecloud
  3. Python中的构造方法
  4. Debug shell: Syntax error: “(“ unexpected
  5. My cnblogs's first day
  6. zookeeper的名词复盘-Stat状态信息
  7. word流程图两条线的端点连接_GitMind免费的思维导图+流程图制作工具
  8. 各种IE(IE6-IE10)兼容问题一行代码搞定
  9. 奥飞娱乐:贝肯熊和镇魂街盲盒产品计划于2021年下半年上市
  10. mysql binlog DDL_MySQL binlog原理及应用
  11. 自定义异常 java_Java自定义异常–用户定义的异常
  12. Java之品优购课程讲义_day05(4)
  13. yum mysql 无法启动失败_Linux下MySQL数据库yum升级后无法启动解决办法
  14. 设计算法之分治法(补充)
  15. hdu 4599 Dice
  16. ModelAndView简介
  17. 支撑江苏移动百亿级话单实时监控是这样实现的
  18. Esri官网购买个人版ArcGIS Pro激活方法
  19. jmeter 入门到精通
  20. c语言以e为底和以10为底对数,以e为底的运算法则

热门文章

  1. 从苏宁电器到卡巴斯基第28篇:难忘的三年硕士时光 IV
  2. windows server2012 安全配置方案—NTFS分区、防毒软件、备份盘的安全
  3. jQuary学习(一)
  4. 卢京潮老师视频-自控原理PPT
  5. 【操作系统】30天自制操作系统--(18)应用程序
  6. 大年三十晚上腾讯服务器不稳定,“大年三十一定要换的新年个签”
  7. RFID,RC522教程
  8. vue iview 输入银行卡号匹配开户行
  9. 窗口置顶工具v1.1.0
  10. 电梯惊魂”不再发生,物联卡解决电梯安全问题!