作者: 孔扎根

简介: 工作13余载,现任高级爬虫工程师,在工作中积累了丰富的数据库、ETL及python开发方面的经验。

座佑铭:美的东西都喜欢, 好的东西都想要, 美好的东西是我追求的目标

Python是一种跨平台的计算机编程语言。是一种面向对象的动态类型语言,Python的设计哲学是“优雅”、“明确”、“简单”,随着版本的不断更新和语言新功能的添加,越多被用于独立的、大型项目的开发。

本篇文章产生的背景是源于公司的一个项目"数字大屏",从公司的服务器下载图片后, 对图像进行处理, 本篇记录了其中的一个小知识点, 在极端网络波动下的图片智能下载处理,我们知道网络稳定顺畅时一切下载都很顺利, 程序结束的很完美,你开心我也开心^_^哈哈。但是图片往往都比较大,当偶尔网络极其慢的时候下载一张图片可能需要很久的时间,而此时业务还要求你不能等太长时间, 怎么办呢? 此时我们可能就希望你不要再下载了, 中断吧……, 我再想想其它法…^_^.

别瞎叨叨了, 说正事...

在使用python3下载图片时, 常用的方法有urlretrieve和requests两种, 但是不管哪种方法在网速极慢的情况下, 都会出现图片下载卡住现象。那如何解决呢? 小编总结了几种常用的图片下载方法及中断下载的方法, 记录了一段寻找最佳方法的心路历程。

方法一、socket: 设置默认超时

正常网速时下载情况是什么样子呢?

#!/usr/bin/env python3

#-*- coding:utf-8 -*-# __author__:kzg

import datetime

import osfromurllib.request import urlretrieve# 函数运行计时器

def count_time(func):

def int_time(*args, **kwargs):

start_time=datetime.datetime.now() # 程序开始时间

func(*args, **kwargs)

over_time=datetime.datetime.now() # 程序结束时间

total_time= (over_time-start_time).total_seconds()

print("download picture used time %s seconds" %total_time)returnint_time

# 下载回调函数, 计算下载进度

def callbackinfo(down, block, size):'''回调函数:

down:已经下载的数据块

block:数据块的大小

size:远程文件的大小''' per = 100.0 * (down * block) /sizeif per > 100:

per= 100print('%.2f%%' %per)

# 图片下载

@count_time

def downpic(url, filename):try:

print("Start Down %s" %url)ifos.path.exists(filename):

os.remove(filename)

urlretrieve(url, filename, callbackinfo)

except socket.timeoutasex:

print(ex)

url= "http://p1.meituan.net/wedding/d4c375416ab70b8b0bc4247875c929b0434138.jpg%40800w_600h_0e_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D2%26y%3D2%26relative%3D1%26o%3D20"filename= r'C:\blueprint-face-detection\scripts\mv.jpg'downpic(url, filename)

结果:

Start Down http://p1.meituan.net/wedding/d4c375416ab70b8b0bc4247875c929b0434138.jpg%40800w_600h_0e_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D2%26y%3D2%26relative%3D1%26o%3D20

0.00%

7.01%

14.02%

21.03%

28.04%

35.05%

42.06%

49.07%

56.08%

63.09%

70.10%

77.11%

84.12%

91.13%

98.14%

100.00%download picture used time0.165558 seconds

结论: 网速较快时下载完全没问题, 仅用零点几秒。

如果把图片下载速度降低, 那下载完图片的整体时间会拉长, 如果网速特别低,那下载完图片的时候会无限延长。

那试试在限制网速的情况下, 下载图片会出现什么现象呢, 如结论所说的那样吗?

2、使用charles限制网速, 设置为下载1k/s, 上传10k/s

1、设置一下charles吧

2、设置socket的默认超时时间为10秒

#!/usr/bin/env python3

# -*- coding:utf-8 -*-

# __author__:kzg

import datetime

import os

import socket

from urllib.request import urlretrieve

# 函数运行计时器

def count_time(func):

def int_time(*args, **kwargs):

start_time = datetime.datetime.now() # 程序开始时间

func(*args, **kwargs)

over_time = datetime.datetime.now() # 程序结束时间

total_time = (over_time-start_time).total_seconds()

print("download picture used time %s seconds" % total_time)

return int_time

# 下载回调函数, 计算下载进度

def callbackinfo(down, block, size):

'''

回调函数:

down:已经下载的数据块

block:数据块的大小

size:远程文件的大小

'''

per = 100.0 * (down * block) / size

if per > 100:

per = 100

print('%.2f%%' % per)

# 图片下载

@count_time

def downpic(url, filename):

try:

print("Start Down %s" % url)

if os.path.exists(filename):

os.remove(filename)

urlretrieve(url, filename, callbackinfo)

except socket.timeout as ex:

print(ex)

url = "http://p1.meituan.net/wedding/d4c375416ab70b8b0bc4247875c929b0434138.jpg%40800w_600h_0e_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D2%26y%3D2%26relative%3D1%26o%3D20"

filename = r'C:\blueprint-face-detection\scripts\mv.jpg'

# 设置10秒超时

socket.setdefaulttimeout(10)

downpic(url, filename)

结果:

Start Down http://p1.meituan.net/wedding/d4c375416ab70b8b0bc4247875c929b0434138.jpg%40800w_600h_0e_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D2%26y%3D2%26relative%3D1%26o%3D20

timed outdownload picture used time10.013625 seconds

结论: 正如期望的那样, 10秒种后socket超时报出错误,urlretrieve下载过程被迫中断,进而捕获错误信息打印出来。

又重试了3次, 每次都好使,  好高兴 ^_^

那再猜想一下, 如果把网速提高一些呢, 应该更没问题了!!!, 下载速度设置为10k/s, 上传10k/s 。

看图:

运行一下:

完啦, 擦擦擦, 这是什么情况, 网速提高了, 10秒钟早过了, 按说下载应该结束才对, 这都过半分钟了咋还在运行呢?

经过多次尝试发现下载速度超过4k/s时, socket设置defaulttime好像就不起作用了, 图片下载不能被中断, 由此可见想用此方法在任何情况下完全做到强制中断图片下载是不行的。

那咱办呀,要不要在一颗树上吊死呀,那就个换个方法试试呗。

方法二、线程

一说到线程, 想到最多的可能就是父子线程了,主线程结束子线程也跟着结束,听着感觉很完美, 这个咱也会呀,啥不说了, 上代码看看效果吧。

#!/usr/bin/env python3

# -*- coding:utf-8 -*-

# __author__:kzg

import threading

import os, datetime, time

from urllib.request import urlretrieve

# 回调函数

def callbackinfo(down, block, size):

'''

回调函数:

down:已经下载的数据块

block:数据块的大小

size:远程文件的大小

'''

per = 100.0 * (down * block) / size

if per > 100:

per = 100

print('%.2f%%' % per)

# 计时器

def count_time(func):

def int_time(*args, **kwargs):

start_time = datetime.datetime.now() # 程序开始时间

func(*args, **kwargs)

over_time = datetime.datetime.now() # 程序结束时间

total_time = (over_time-start_time).total_seconds()

print("download picture used time %s seconds" % total_time)

return int_time

# 图片下载函数

@count_time

def downpic(url):

print("Start Down %s" % url)

if os.path.exists(filename):

os.remove(filename)

urlretrieve(url, filename, callbackinfo)

# 子线程

def mainFunc(funcname, args):

t = threading.Thread(target=funcname, args=(args,))

t.start()

t.join(10) # 子线程阻塞10秒

url = "http://p1.meituan.net/wedding/d4c375416ab70b8b0bc4247875c929b0434138.jpg%40800w_600h_0e_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D2%26y%3D2%26relative%3D1%26o%3D20"

filename = r'C:\blueprint-face-detection\scripts\mv.jpg'

# 主线程

m = threading.Thread(target=mainFunc, args=(downpic, url))

m.setDaemon(True)

m.start()

m.join() # 10秒后取消子线程阻塞, 主线程不阻塞立即运行后面程序

print("m is alive? ", m.is_alive())

time.sleep(10)

os.remove(filename)

将下载速度分别限制在1k/st和10k/s,结果如下

结论: 主线程已经结束, 但子线程并未结束, 图片下载在持续中, 直到整个程序结束后, 下载被中断。所以通过父线程结束子线程也结束的方法来强制图片中断下载是不可行的。

这下可惨啦, 没办法实现了吗? 于是乎在网上找呀找, 终于看到有人也遇到了这类问题, 怎么解决呢?  。。。。 强制中断线程, 听起来很霸气,那咱也跟着试试吧, 上代码。

#!/usr/bin/env python3

#-*- coding:utf-8 -*-# __author__:kzg

import threading

import os, datetime, time

import ctypes

import inspectfromurllib.request import urlretrieve

# 回调函数

def callbackinfo(down, block, size):'''回调函数:

down:已经下载的数据块

block:数据块的大小

size:远程文件的大小''' per = 100.0 * (down * block) /sizeif per > 100:

per= 100print('%.2f%%' %per)

# 计时器

def count_time(func):

def int_time(*args, **kwargs):

start_time=datetime.datetime.now() # 程序开始时间

func(*args, **kwargs)

over_time=datetime.datetime.now() # 程序结束时间

total_time= (over_time-start_time).total_seconds()

print("download picture used time %s seconds" %total_time)returnint_time

# 图片下载函数

@count_time

def downpic(url):

print("Start Down %s" %url)ifos.path.exists(filename):

os.remove(filename)

urlretrieve(url, filename, callbackinfo)

def _async_raise(tid, exctype):"""raises the exception, performs cleanup if needed"""tid=ctypes.c_long(tid)ifnot inspect.isclass(exctype):

exctype=type(exctype)

res=ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, ctypes.py_object(exctype))if res == 0:

raise ValueError("invalid thread id")

elif res!= 1:

#"""if it returns a number greater than one, you're in trouble,

# and you should call it again with exc=NULL to revert the effect"""ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, None)

raise SystemError("PyThreadState_SetAsyncExc failed")

def stop_thread(thread):

_async_raise(thread.ident, SystemExit)

url= "http://p1.meituan.net/wedding/d4c375416ab70b8b0bc4247875c929b0434138.jpg%40800w_600h_0e_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D2%26y%3D2%26relative%3D1%26o%3D20"filename= r'C:\blueprint-face-detection\scripts\mv.jpg'# 开启线程下载图片

t= threading.Thread(target=downpic, args=(url,))

t.start()

t.join(10)

# 强制结束线程ift.is_alive():

stop_thread(t)

time.sleep(3)

print("thread is alive?", t.is_alive())

os.remove(filename)

结论:

下载速度设置为1k/s, 8k/s, 10k/s, 15k/s时, 结果如下,跟预期结果一致(重试几次结果一致)

下载速度设置为3k/s,12k/s时结果如下(重试几次结果一致)

似乎看出点什么了吧, 为什么同一个程序, 不同网速下执行结果不一致呢?因实现网速我们是无法控制的,所以此方法大部分情况下都是可以的, 但也会出现例外。

python下载图片时, 每次下载8k, 下载完就会写入一次文件。

方法三、 request

timeout 仅对连接过程有效,与响应体的下载无关。 timeout 并不是整个下载响应的时间限制,而是如果服务器在 timeout 秒内没有应答,将会引发一个异常(更精确地说,是在 timeout 秒内没有从基础套接字上接收到任何字节的数据)。

换句话说: 如果在timeout时间内没有返回任何字节数据, 则request会报错退出, 如果在timeout时间内收到了任何字节的数据, 则request会一直下载, 直到响应体被下载完。

1、正常网速下的现象

# -*- coding: utf-8 -*-

import requests

import datetime

url = "http://p1.meituan.net/wedding/d4c375416ab70b8b0bc4247875c929b0434138.jpg%40800w_600h_0e_1l%7Cwatermark%3D1%26%26r%3D1%26p%3D9%26x%3D2%26y%3D2%26relative%3D1%26o%3D20"

filename = r'C:\blueprint-face-detection\scripts\meinv.jpg'

# 计时器

def count_time(func):

def int_time(*args, **kwargs):

start_time = datetime.datetime.now() # 程序开始时间

func(*args, **kwargs)

over_time = datetime.datetime.now() # 程序结束时间

total_time = (over_time-start_time).total_seconds()

print("download picture used time %s seconds" % total_time)

return int_time

@count_time

def downpic(url):

ret = requests.get(url)

print("req status is: ", ret.status_code)

with open(filename, 'wb') as fp:

print("start write picture..")

fp.write(ret.content)

downpic(url)

结果:

req status is: 200

start write picture..

download picture used time 0.26226 seconds

结论: 正常网速下, request请求成功了, 美女0.3秒被成功请出。

1、将网速限制在100k/s,结果如何呢 ?

结论: 下载速度为100k/s时, request也成功了, 美女经过15.6秒被成功请出来。

2. 将网速限制在50k/s,结果如何呢?

结论: 下载速度为50k/s时, request也成功了, 美女经过40.9秒被成功请出来。 设置timeout=3, 是否会生效呢?

感觉要绝望了,咋整呀? 没有一种方法可以及时中断下载吗?经过不断的尝试终于找到了一个可行的方法。

函数中断 !!! 如果超过设定时间则正在下载的函数被中断, 试一下吧。

# #!/usr/bin/python3

# #-*- coding: utf-8 -*-import requests

import datetimefromfunc_timeout import func_set_timeout, FunctionTimedOut

url= "http://pic1.win4000.com/wallpaper/5/58b3b56d0a0a7.jpg"@func_set_timeout(10)

def downpic(url):

ret= requests.get(url)

print("Start Down %s" %url)if ret.status_code == 200:

with open('meinv.jpg', 'wb') asfp:

fp.write(ret.content)

print("downLoad pic ok %s" %url)

print(datetime.datetime.now())

downpic(url)

print(datetime.datetime.now())

运行一下程序, 正常网速时下载一张图片的时间是多少 !

可以看出大概8秒时间可以下载完一张图片, 那我将时间设置为5秒, 那图片下载会被中断,肯定下不完。

正如预料的那样, 5秒钟后图片下载被中断了, 经过多次检查, 图片并没有继续下载, 证明图片下载被中断成功。

哈……

python下载图片超时_python3在极端网络波动下的智能判断下载相关推荐

  1. 多线程 python tcp 图片_Python第四周之网络编程TCP (服务器/客户端; 线程下载图片;线程版服务器和客户端(单人、多人)) UDP...

    # 网络编程 # 计算机网络, 把多台独立自主的计算机,连接到网络,实现资源的共享 # Internet网,(互联网)eniac 1946美国大学第一台电子计算机 # # 一个TCP报文除了包含要传输 ...

  2. python按照图片命名复制到不同文件夹下

    一.批量创建文件夹 首先需要创建在一个文件夹下,批量创建多个文件夹,代码如下: import os import shutilcount = len(os.listdir(LABLE_DIR)) pr ...

  3. python做图片美化_Python实现简单的照片磨皮(照片智能磨皮) 最新免费版

    Python实现简单的照片磨皮(照片智能磨皮)是一款用python写的最好的照片磨皮软件.需要配置opencv和numpy,使用的时候地址需要加英文的引号,斜杠要用双斜杠,例如"D:\\a. ...

  4. 教你怎么使用python批量下载图片

    教你怎么使用python批量下载图片 文章目录 教你怎么使用python批量下载图片 前言 一.运行环境 1. win10 2. python==3.7.2 二.需要用到的参数 1. download ...

  5. Python实现Excel表格图片下载-腾讯文档收集表下载Excel表格后下载图片

    代码目的 QQ腾讯文档收集表下载本地后变成Excel表格,可是腾讯文档内的图片变成了链接,为了不手动保存图片,所以写出以下代码来实现自动保存图片. 代码原理 准备工作: python编译器 pytho ...

  6. scrapy python下载图片_使用Scrapy自带的ImagesPipeline下载图片,并对其进行分类。

    imagespipeline是scrapy自带的类,用来处理图片(爬取时将图片下载到本地)用的. 优势: 将下载图片转换成通用的jpg和rgb格式 避免重复下载 缩略图生成 图片大小过滤 异步下载 . ...

  7. 手机下载神器批量下载图片教程

    手机版IDM下载神器批量下载图片漫画实际操作教程,现在介绍两个批量下载图片的方法 视频教程: 手机IDM下载器批量下载图片漫画实际操作 首先,打开你要下载图片的网页,同时打开手机版idm下载器和手机记 ...

  8. java异步下载图片_CMS项目实现异步图片下载

    create table CMS_IMAGES ( id LONG PRIMARY KEY, --id wsrc varchar2(256),--远程图片地扯 lsrc varchar2(256),- ...

  9. 使用 clumsy 模拟极端网络状况

    clumsy 能在 Windows 平台下人工造成不稳定的网络状况,方便你调试应用程序在极端网络状况下的表现. 简介 利用封装 Winodws Filtering Platform 的WinDiver ...

最新文章

  1. Xamarin XAML语言教程构建ControlTemplate控件模板 (三)
  2. 字节跳动再扩招1000人,招聘要求让人窒息
  3. dedecms插件开发教程
  4. 可视化神器背后的奥秘
  5. 头回见!95后女大学生买iPhone11出租:租借者想尝鲜或显摆
  6. MySQL-数据类型 整理总结
  7. keil4出现目标未被创建_STM32入门系列-创建寄存器模板
  8. java中的StringUtil.isEmpty和StringUtil.isBlank
  9. 「镁客早报」微软总市值超过苹果亚马逊;Linux内核出现漏洞
  10. 超级计算机在鄂尔多斯那个区,内蒙古超级计算机运算能力 进入中国最强行列...
  11. 两点天上来,爱情一线牵——Global Timing Debugger
  12. Linux 命令(159)—— hostname 命令
  13. debian ELK6.2.2安装教程
  14. 常用HTML技术 淘宝店铺装修
  15. Android按键音
  16. CANopen总线的高级协议详解
  17. WMS系统学习之ModernWMS
  18. 小米 红米 MIUI 重新打开(开启)蓝牙耳机电量弹窗
  19. 刚体的相对运动与机器人连杆的运动
  20. Git:恢复未合并的已删除分支

热门文章

  1. docker-搭建本地镜像仓库
  2. Resource shrinker cannot be used for libraries.的解决办法
  3. python中notebook左侧in中_Python Notebook (Jupyter Notebook) 介绍
  4. 线上幽灵:世界头号黑客_开放世界设计的经验教训:幽灵行动的故事
  5. 屋顶绿化,给建筑披上绿装!
  6. 基于HTML环境保护网站项目的设计与实现【 html+css+javascript+jquery+bootstarp响应式的绿化种植类公司网站模板】企业网站制作...
  7. 华泰证券分布式数据库的探索与应用
  8. 人脸识别/车牌识别系统安防视频云服务EasyCVR支持大华SDK语音对讲功能流程介绍
  9. Appium连接真机
  10. 科技赋能公益,看掌门1对1“灯塔计划”下的教育新火花