摘要:第三节协程继续基础框架搭好了,下面来正式的来一个项目吧全球设计师的作品展示平台就从这拉几张图吧,具体的网页解析方式网上有很多,在此略过,我已经取出了一些图片地址,保存在了里,这次就用这些吧。
第三节 协程!?
继续...基础框架搭好了,下面来正式的来一个项目吧
behance
全球设计师的作品展示平台
就从这拉几张图吧,具体的网页解析方式网上有很多,在此略过,我已经取出了一些图片地址,保存在了list.txt里,这次就用这些吧。
综合多种因素,最后选用了协程来下载图片
即asyncio
框架则用了aiohttp
实现思路:
目的
将网络上的图片(最好是缩略图)先下载到本地,记录图片信息,比如ID以便获得更高质量的图片,将图片显示到界面
问题
为了更快速的展示页面,我需要同时下载一定数量的图片...
我需要动态的将下载任务发送给后台服务...
这里可以在程序启动的时候设置一个配置列表cfg
from os import path as osPath, getcwd, mkdir ... def __init__(self): ... self.cfg = self.initCfg() def initCfg(self): cfg = {} # 代理,没有可不用设置 # cfg["proxies"] = "127.0.0.1:61274" # 加载图片列表 filename = "list.txt" if osPath.exists(filename): with open(filename, "r") as f: cfg["picList"] = f.read().strip().split(" ") # 设置图片的保存位置 current_folder = getcwd() cfg["pic_temp"] = osPath.join( current_folder, "pic_temp") if not osPath.isdir( cfg["pic_temp"] ): mkdir(cfg["pic_temp"]) return cfg
然后传递给服务进程就可以了
p = Process(target = startServiceP, args = ( self.GuiQueue, self.ServiceQueue, self.cfg ))
先来修改一下html的内容,添加一个自定义控件,用来存放图片:
在服务进程ServiceEvent里添加一个方法getPicByList()
def getPicByList(self, msg): # 为图片创建占位图 imgidList = self.__creatPlaceholderImg() for imgid in imgidList: picHttp = self.cfg["picList"].pop(0) file_name = picHttp.split("/")[-1] file_path = osPath.join( self.cfg["pic_temp"], file_name ) # 图片下载完成后需要执行的任务 _GuiRecvMsgDict = { "fun" : "setImgBg", "msg" : {"id":imgid,"fpath":file_path} } if not osPath.isfile(file_path): # 将下载任务动态添加到协程循环中 self.__run_coroutine_threadsafe( {"id": imgid,"http": picHttp,"fpath": file_path}, _GuiRecvMsgDict ) else: self.__putGui( "setImgBg", {"id":imgid,"fpath":file_path} )
当用户点击下载图片的按钮后会执行到这个方法,为了更好的体验,在图片下载之前先为其占据了空间,可以在其上显示loading动画,更重要的一点是,通过它可以控制图片的显示顺序,因为用协程下载图片,你无法预知完成的顺序...
def __creatPlaceholderImg(self): # 先创建5个占位图 html = "" imgidList = [] time_now = "" for i in range(0, 5): time_now = "-".join( ( str(i), str(time()) ) ) # 储存图片的id imgidList.append( time_now ) html += self.html % ( time_now ) self.__putGui("creatPlaceholderImg", html) return imgidList
之后就到了动态创建协程的部分了
def __run_coroutine_threadsafe(self, data, _GuiRecvMsgDict): asyncio.run_coroutine_threadsafe(self.dld.stream_download( data, _GuiRecvMsgDict ), self.new_loop)
但在正式介绍run_coroutine_threadsafe()之前,我们需要先开启一个协程循环
但我们已经开启了一个用于处理队列的循环了,没办法再开一个(也不排除是咱太菜),于是另开了一个线程专来处理协程
class ServiceEvent(object): """服务进程""" def __init__(self, _GuiQueue, cfg): ... # 主线程中创建一个事件循环 self.new_loop = asyncio.new_event_loop() self.dld = AsyncioDownload( self.new_loop, self.GuiQueue, self.proxies )
class AsyncioDownload(object): """使用协程下载图片""" def __init__(self, loop, _GuiRecvMsg, proxies=None ): self.GuiRecvMsg = _GuiRecvMsg self._session = None self.loop = loop self.prox = "".join(("http://", proxies)) if proxies else proxies self.timeout = 10 # 启动一个线程,传递主线程中创建的事件循环 t = Thread(target=self.start_loop, args=(self.loop,)) t.setDaemon(True) # 设置子线程为守护线程 t.start() def start_loop(self, loop): # 启动事件循环 asyncio.set_event_loop(loop) loop.run_forever() def __session(self): if self._session is None: self._session = aiohttp.ClientSession(loop=self.loop) return self._session async def stream_download(self, d, _GuiRecvMsgDict): try: client = self.__session() async with client.get( d["http"], proxy=self.prox, timeout=self.timeout) as response: if response.status != 200: print("error") return # 保存图片到本地 if not osPath.isfile(d["fpath"]): with open(d["fpath"], "ab") as file: while True: chunk = await response.content.read(1024) if not chunk: break file.write(chunk) # 图片下载完成后告知主线程 self.GuiRecvMsg.put(_GuiRecvMsgDict) except asyncio.TimeoutError: pass
最后,主进程获得图片的id及路径,显示到窗口中
function setImgBg( d ){ var div = $(div[imgid="{d.id}"]); if(div){ div.post( ::this.style#background-image = "url(" + d.fpath + ")" ); } }
源码
总结:
完成这个项目使用了
多进程 ---- 后台服务
多线程 ---- 事件循环
协程 ---- IO操作
相对一个单纯的爬虫脚本来说,还是有点复杂的,尤其是交互,难怪这么多人不愿意写界面...
虽然还有不足,但本次项目的内容就到这了。
谢谢。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/40761.html
摘要:第二节将任务添加到队列上一个栗子只是简单实现了下网页与后台的通信你可以在这里处理任何你想要的操作你已经点到我了但由于是同一个进程,如果你做了很耗时的操作,比如下载一张图片之类的操作你会发现,窗口卡住了,一般表现为窗口泛白,出现未响应的提示但 第二节 将任务添加到队列! 上一个栗子只是简单实现了下网页与后台的通信 def clickMe(self): #你可以在这里处理任何你想要...
摘要:下载地址简介结合与编写软件使用方法安装个人使用建立的环境下载并解压下载并解压打开控制台转到解压目录比如此时就会将安装到第三方安装包的目录下我的是注册或者修改源码注册方式找到位位使用控制台注册路径使用绝对路径可以复制到然后就可以了修改 下载地址: Pysciter-GitHub Sciter 简介: 结合HTML与Python编写软件 使用方法: 安装Python3 (个人使用A...
摘要:如何在中使用动画前端掘金本文讲一下中动画应用的部分。与的快速入门指南推荐前端掘金是非常棒的框架,能够创建功能强大,动态功能的。自发布以来,已经广泛应用于开发中。 如何在 Angular 中使用动画 - 前端 - 掘金本文讲一下Angular中动画应用的部分。 首先,Angular本生不提供动画机制,需要在项目中加入Angular插件模块ngAnimate才能完成Angular的动画机制...
摘要:第四章安全管理制度发布第十条安全管理制度必须以正式文件的形式发布施行。第十一条安全管理制度由信息安全管理小组制订,信息安全领导小组审批发布。第二十条安全管理制度的修改与废止须经信息安全领导组织审批确认,信息安全管理部门备案。 字数 3610阅读 760评论 0赞 3《xxxx安全管理制度汇编》****制度管理办法****文...
阅读 1668·2021-11-17 09:33
阅读 3530·2021-11-16 11:40
阅读 3054·2019-08-30 11:23
阅读 1047·2019-08-29 16:36
阅读 2464·2019-08-29 13:23
阅读 1738·2019-08-29 12:59
阅读 1541·2019-08-29 12:42
阅读 1978·2019-08-28 18:22