python多线程下载图片(setu)

前言

python的学习其实早在两三个星期前就开始了,奈何我是个急性子,所以学的很浮躁,甚至连md笔记都没写。但是想了想,我学python的目的反正也仅仅是为了ctf的比赛。所以我直接去学习了对ctf有用的部分。例如,requests库、re库、threading库。所以这章的内容也就是我这几周研究的成果了。

那么进入正题。这章是对学习多线程的一个交代。

说来也好笑,激发我学习python的动力之一居然是setu。

由于ctf男♂童交流群有个bot,其中有个功能是——“来份色图”

这就很nb了。虽然我做不到监听QQ消息,制作出QQbot的程度,但是我可以找学长py一个setu接口呀~

所以拿到了api的我,开始了爬取setu的艰苦奋斗之路!

如果是想要最终版本的多线程爬取setu脚本的话,直接下拉到最后即可!

另附setu API
请节制使用!!!

Version 01

这是我的第一个爬取图片脚本。

当时是第一次写python的下载脚本。

刚刚学会用with打开文件的我,迫不及待的开始了第一版本的脚本撰写~

import requests
import time
for i in range(1,300):print('start')url = 'https://api.lolicon.app/setu/?r18=2'res = requests.get(url)data = res.json()data = data['data'][0]setu_url = data['url']setu_id = data['uid']with open ('C:/Users/木鲸/OneDrive/setu_url.txt',"a") as file :file.write(setu_url+'\n')try:res = requests.get(setu_url).contentexcept:res = requests.get(setu_url).contentelse:passwith open('C:/Users/木鲸/OneDrive/setup/'+str(setu_id)+".jpg","wb") as f:f.write(res)print('over')time.sleep(2)

当时写的想法就是,先访问api接口,爬取json的数据中的url和uid。
url用来下载图片,为了避免重复下载同样的,用uid来当作图片的名字。
我是将图片下载到本地的OneDrive中。因为当时用heroku搭建了一个每个月500小时免费的onemanager下载站。这样在下载站上看图片也比较方便。

但是这个版本的脚本可以说是一坨屎,根本没有什么技术可言,就仅仅是爬取了url进行下载。也没有进行错误处理。在当时这个api接口只有每日300次的时候,这个脚本可以说是非常浪费资源了。

Version 02

接下来来到了第二个版本

这个版本相较于01版本,多了一个错误处理。

原理就是,每次连接api接口的时候,将其中的url写入一个txt中。

然后调用处理txt的脚本来下载文件。

而下载失败的url会存入一个err_txt中,在下载完成之后,我只需要打开errtxt进行下载就行。

那么放下面这两个脚本

下载setu_url.txt中的图片内容

import requests
import timedata = []
err = []
err_txt = []
# 打开存着url的txt文件,将url全部读入data中
with open ('C:/Users/木鲸/OneDrive/setu_url.txt','r') as f:data = f.read().splitlines()
# 开始下载url中的图片,保存在本地的OneDrive中
for i in range(0,len(data)):print('Start! Ready to count!')startTime = time.time()url = data[i]try:print('Trying to connect!')res = requests.get(url).contentexcept:err.append(url)print("error!")continueoutpath = time.strftime("%Y%m%d%H%M%S", time.localtime())with open('C:/Users/木鲸/OneDrive/setup/'+(outpath)+".jpg","wb") as file:file.write(res)endTime = time.time()runTime = endTime - startTimeprint('over! It downloads ' + str(runTime))time.sleep(1)
# 将请求失败的数据再次下载
for i in range(0,len(err)):print('Start! Ready to count!')startTime = time.time()url = err[i]try:print('Trying to connect!')res = requests.get(url).contentexcept:err_txt.append(url)print('error! To be a txt!')continueoutpath = time.strftime("%Y%m%d%H%M%S", time.localtime())with open('C:/Users/木鲸/OneDrive/setup/'+(outpath)+".jpg","wb") as file:file.write(res)endTime = time.time()runTime = endTime - startTimeprint('over! It downloads ' + str(runTime))time.sleep(1)
# 将最终错误的url写入一个存放错误的txt中
with open ('C:/Users/木鲸/OneDrive/err_txt.txt','a') as f:for i in range(0,len(err_txt)):f.write(err_txt[i]+'\n')
# 结束!
print('All over!')

下载err_txt.txt中的图片内容

import requests
import timedata = []
err = []with open ('C:/Users/木鲸/OneDrive/err_txt.txt','r') as f:data = f.read().splitlines()for i in range(0,len(data)):print('Start! Ready to count!')startTime = time.time()url = data[i]try:print('Trying to connect!')res = requests.get(url).contentexcept:err.append(url)print('error!')continueoutpath = time.strftime("%Y%m%d%H%M%S", time.localtime())with open('C:/Users/木鲸/OneDrive/setup/'+outpath+".jpg",'wb') as file:file.write(res)endTime = time.time()runTime = endTime - startTimeprint('Over! It downloads ' + str(runTime))time.sleep(1)for i in range(0,len(err)):print('Start! Ready to count!')startTime = time.time()url = err[i]try:print("Trying to connect error_txt!")res = requests.get(url).contentexcept:print('Error! Try again!')res = requests.get(url).contentelse:passoutpath = time.strftime("%Y%m%d%H%M%S", time.localtime())with open('C:/Users/木鲸/OneDrive/setup/'+outpath+".jpg",'wb') as file:file.write(res)endTime = time.time()runTime = endTime - startTimeprint('Over! It downloads ' + str(runTime))time.sleep(1)print('All over!')

这个版本虽然解决了下载失败的部分问题,但他的缺点也是显而易见的,速度太慢了。一张图快的时候3秒下载完成,慢的时候需要200秒。这种单线程下载实在是太慢了。

所以我决定这个时候去学习多线程下载!

Version 03

这是第一个多线程版本,也是废了半天时间写出来的。

由于那个时候api接口是有连接限制,例如不能过快访问,不然ip会被ban,而且每天访问的次数只有300次。

所以我写了一个多线程脚本,大致思想就是,进行20次循环,每次循环中访问API 15次,并且开15个线程进行图片下载。等这这一轮的线程结束,进行下一次循环。

这个版本的脚本不仅有记录err的txt,也有多线程的快速,可以说是在API没有无限开放时候的巅峰了。

脚本如下:

import requests
import time
import threading
import reurl = 'https://api.lolicon.app/setu/?r18=2'
headers = {'user-agent': '这里写你自己的user-agent!'
}def download(url,pid,way):try:res = requests.get(url,headers=headers).contentexcept:print('Download error!')with open ('C:/Users/木鲸/OneDrive/setup/err.txt',"a") as file :file.write(setu_url+'\n')returnwith open('C:/Users/木鲸/OneDrive/setup/'+ str(pid)+ way, "wb") as file:file.write(res)for count in range(1,21):print(f'开始第{count}次下载!')setu_urls = []setu_pids = []ways = []threads = []for i in range(1,16):try:print('Try to connect api!')res = requests.get(url,headers=headers)print('Successful connection!')try:data = res.json()data = data['data'][0]setu_url = data['url']setu_pid = data['pid']except:print('JOSN error!')continueway = re.findall('(.png|.jpg)', setu_url,re.S)[0]ways.append(way)setu_urls.append(setu_url)setu_pids.append(setu_pid)time.sleep(0.5)except:print('Connect api error!')time.sleep(1)continuefor i in range(len(setu_urls)):t = threading.Thread(target=download, args=(setu_urls[i],setu_pids[i],ways[i],))t.start()threads.append(t)for t in threads:t.join()print(f'{t.name}-执行下载!')

Version 04

这个版本是最终版本。

今天早上看到室友写的一份脚本,发现同样的api接口多了一个num参数,一次访问可以得到最多100份数据。

再上API网站,发现开放限制了。

于是直接写了一份不需要等待,一次爬100张图片的最终版本。

脚本如下:

import requests
import re
import threading
import time# 获取json数据
def getApiJSON(url,headers):try:print('Try to connect api!')res = requests.get(url,headers=headers)print('Successful connecting!')except:print('Connect error!')return getApiJSON(url,headers)JSON_data = res.json()return JSON_data# 进行图片下载
def download(url, pid, way):global errCounttry:res = requests.get(url).contentexcept:print('Download error!')lock.acquire()errCount += 1lock.release()with open ('C:/Users/木鲸/OneDrive/setup/err.txt',"a") as file :file.write(setu_url+'\n')returnwith open('C:/Users/木鲸/OneDrive/setup/'+ pid + way, "wb") as file:file.write(res)print('Success!')allTime = 0
allSuccess = 0# 每次循环下载100张图
for i in range(2):print(f'The {i+1} time!')url = 'https://api.lolicon.app/setu/?num=100&r18=1'headers = {'user-agent': '这里写你自己的user-agent!'}lock = threading.Lock()threads = []errCount = 0startTime = time.time()jsons = getApiJSON(url, headers)jsons = jsons['data']for json in jsons:setu_url = json['url']setu_pid = str(json['pid'])way = re.findall('(.png|.jpg)', setu_url, re.S)[0]t = threading.Thread(target=download, args=(setu_url,setu_pid,way,))t.start()threads.append(t)time.sleep(1)for t in threads:t.join()print(f'{t.name} over!')endTime = time.time()print(f'The {i+1} time over! It takes {endTime-startTime}! It has {100-errCount} successes!')allTime += endTime - startTimeallSuccess += 100 - errCounttime.sleep(1)print(f"All over! It takes {allTime}! It has {allSuccess} successes!")

最终测试发现。如果每次time.sleep(0.5),会有25%的错误率,速度也是在300s左右。但是晚上运行脚本的时候,无意间改成了time.sleep(1),发现错误率为0,并且速度也是在210s左右。几乎是1s一张图片。可以说在速度和错误率上都是最优的了。

后话

当然,本人也是刚刚学习的python多线程,这个最终版本的脚本也是花了一天时间不断的改出来的,所以可能并不是最完美的。但是在我这里,这个多线程setu爬取脚本已经毕业了。从最初的单线程下载,到后面的记录错误下载,再到后面的多线程爬取,最后的不断改进。

在这个过程中,我发现,枯燥的听网课是很难运用一个知识的。只有在学习的过程中去真正自己动手,这样知识才能学好。

所以在学习的时候,实践、实践、实践!这点很重要!

当然,看看这个文件夹的大小,你就知道这几天我有多努力了(bushi)

从开始有多线程的想法(飞机师傅的指导)

到学习多线程遇到瓶颈

再到最后发现更改睡眠时间的欣喜若狂

其实这样学习才是快乐的~

【python学习】多线程下载图片实战相关推荐

  1. python多线程学习-多线程下载图片

    目录 开发工具 知识点 代码 总结 开发工具 python版本: python-3.8.1-amd64 python开发工具: JetBrains PyCharm 2018.3.6 x64 知识点 多 ...

  2. Python 多线程下载图片

    多线程下载图片 参考链接: Python标准库-urllib和urllib3 urllib实战2–urllib基础urlretrieve().urlcleanup().info().getcode() ...

  3. python根据url list多线程下载图片、删除图片目录

    看到很多个版本多线程下载图片,自己改了改,主要是异常以及urlretrieve的使用 import time from multiprocessing.pool import ThreadPool i ...

  4. Python多线程下载图片

    文章目录 导包 模拟浏览器登录参数 一:单线程爬取 1.生成网页列表 2.爬取图片的网址 3.下载图片到本地 二:多线程下载图片 0.加锁 1.获取图片网址 2.下载图片 3.函数调用 4.问题 完整 ...

  5. python批量旋转裁剪图片实战

    python批量旋转裁剪图片实战 引子 有一本PDF电子书,由扫描的图片生成的,每页的截图看起来如下图: 它是展开扫描的,两页并列在一张图片上.并且每页又是分两栏,这样的PDF在手机.平板上,需要放大 ...

  6. java 多线程下载图片_java多线程实现下载图片并压缩

    最近在做一个需求:从其他系统的ftp目录下载存储图片url的文件,然后读取文件中的url地址,根据地址下载图片后按天压缩成一个包,平均一个地址文件中包含4000个地址左右,也就是说一个文件扫描后需要下 ...

  7. python代码示例下载-Python实现多线程下载脚本的示例代码

    0x01 分析 一个简单的多线程下载资源的Python脚本,主要实现部分包含两个类: Download类:包含download()和get_complete_rate()两种方法.download() ...

  8. Python - Requests库下载图片

    Python - Requests库下载图片 import requests# 获取网络图片资源 r = requests.get('https://www.baidu.com/img/bd_logo ...

  9. 多线程下载图片并压缩

    最近在做一个需求:从其他系统的ftp目录下载存储图片url的文件,然后读取文件中的url地址,根据地址下载图片后按天压缩成一个包,平均一个地址文件中包含4000个地址左右,也就是说一个文件扫描后需要下 ...

最新文章

  1. 接口是否可继承接口? 抽像类是否可实现(implements)接口? 抽像类是否可继承实体类(concrete class)?...
  2. rabbitmq中消息的存储
  3. ASP.NET Core MVC压缩样式、脚本及总是复制文件到输出目录
  4. 微软企业库5.0学习笔记(三十三)数据访问模块
  5. iOS开发-CocoaPods使用详细说明
  6. 文字在div中的垂直居中问题 div在页面中的水平垂直居中问题 vertical-align ??????
  7. 排序算法有哪些_面经 | 超强整理,科班算法岗的秋招之路
  8. #PYTHON#数据模型
  9. 10月24日杨力祥老师谈话有感[转]
  10. wps ppt word 双屏幕同时浏览技巧
  11. pmp中项目经理如何进行目标设定
  12. HDU3404POJ3533(Nim积摸版)
  13. Angular 入门教程系列:33:移动端统计图表F2
  14. win7计算机自动关机设置在哪里设置方法,win7自动关机设置在哪?自动关机怎么设置具体方法...
  15. 橡皮筋框QRubberBand Class
  16. 情感驿站004:我有一壶酒,可以慰风尘
  17. Vue3的生命周期的使用
  18. excel表格 筛选 通过mysql语句
  19. 暑期机器学习小组读书报告----机器学习概述
  20. Unity之Animation

热门文章

  1. NVIDIA AGX xavier系列,三分钟教你刷盒子(保姆级教程)
  2. 学习记录651@python之收益率与对数收益率案例实战
  3. c语言产生系统年月日,c语言中如何输入年月日
  4. oracle cpio,cpio - 手册页部分 1: 用户命令
  5. 未转变者服务器保存红字警告,未转变者3.1版开服图文教程,未转变者3.1版怎么开服 - 攻略心得 - 找游戏手游网...
  6. 标志设计思维--概念的提练方法
  7. html li圆点单独颜色,html中li标签可以设置颜色吗
  8. 传奇人物《周兴和》书连载9 小荷初露尖尖角
  9. Mac 下 Chrome 快捷键大全
  10. python实现卷积运算