目录

前言

一、什么是协程?

二、协程的优势

三、代码分析

1.引入库

2.获取所有时间线的链接

3.获取一个时间线中所有相册的链接

4.获取一个相册中所有的图片链接以及相册的名字

5.下载并保存图片

6.main函数

7.主方法

四、完整代码


前言

在爬虫的过程中,效率是一个很关键的问题,最常用的是多线程、多进程、线程池、进程池等等。这篇文章主要介绍使用协程来完成抓取妹子图片。


一、什么是协程?

协程,英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程 。最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是用户执行)。

二、协程的优势

优势就是性能得到了很大的提升,不会像线程切换那样消耗资源。协程的开销远远小于线程的开销。协程本质是单线程,在不占用更多系统资源的情况下,将IO操作进行了挂起,然后继续执行其他任务,等待IO操作完成之后,再返回原来的任务继续执行。

三、代码分析

1.引入库

import requests #同步的网络请求模块
import re       #正则模块,用于提取数据
import asyncio  #创建并管理事件循环的模块
import aiofiles #可异步的文件操作模块
import aiohttp  #可异步的网络请求模块
import os       #可调用操作系统的模块

2.获取所有时间线的链接

#定义一个同步函数,使用requests库进行请求
def get_date_list():#目标链接url='https://zhaocibaidicaiyunjian.ml/'#伪装请求头header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#发送请求r=requests.get(url=url,headers=header)#获取网页页面源代码htm=r.text#使用正则模块re解析获得时间线的链接results=re.findall('''<aside id="archives-2" class="widget widget_archive">(.*?)</aside>''',htm,re.S)date_list_str=str(re.findall('''<a href=(.*?)>.*?</a>''',str(results)))date_list=re.findall('''\\'(.*?)\\\\\\\\\'''',date_list_str)#返回一个所有时间线链接的列表return date_list

3.获取一个时间线中所有相册的链接

#判断一个时间线页面是否含有第二页(在下面的协程函数get_album_urls中调用)
def hasnext(Responsetext):if re.findall('''<a class="next page-numbers" href="(.*?)">下一页</a>''', Responsetext):nextpage = re.findall('''<a class="next page-numbers" href="(.*?)">下一页</a>''',Responsetext)[0]return nextpageelse:return None#async 定义一个协程函数,参数为一个时间线的链接
async def get_album_urls(date_url):header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#创建一个上下文管理器,并创建一个可异步的session对象async with aiohttp.ClientSession() as session:#session的get()的方法与requests库中的用法一样(注:requests库中get()方法的代理参数为字典形式proxies=dict,支持http和https。而session中get()方法的代理参数为字符串,proxy=str。且仅支持http,不支持https)async with session.get(url=date_url,headers=header) as Response:#获取网页页面源代码,使用await将网络IO请求挂起,程序继续执行其他任务,等待内容返回后再跳转到此处继续执行htm=await Response.text()#使用正则提取所有相册的链接album_urls=re.findall('''<a href="(.*?)" class="more-link">继续阅读<span class="screen-reader-text">.*?</span></a>''',htm)#判断此时间线页面是否含有第二页nextpage=hasnext(htm)#如果有第二页则提取第二页所有的相册链接if nextpage:async with session.get(url=nextpage,headers=header) as Response1:htm1=await Response1.text()#列表生成器,将第二页中的所有相册链接加入到列表album_urls中[album_urls.append(album_url) for album_url in re.findall('''<a href="(.*?)" class="more-link">继续阅读<span class="screen-reader-text">.*?</span></a>''',htm1)]#返回一个时间线中所有的相册链接return album_urls

4.获取一个相册中所有的图片链接以及相册的名字

#async 定义一个协程函数,参数为一个相册链接
async def get_pic_urls_and_title(album_url):header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#创建一个上下文管理器,并创建一个可异步的session对象async with aiohttp.ClientSession() as session:#session的get()的方法与requests库中的用法一样(注:requests库中get()方法的代理参数为字典形式proxies=dict,支持http和https。而session中get()方法的代理参数为字符串,proxy=str。且仅支持http,不支持https)async with session.get(url=album_url,headers=header) as Response:#获取网页页面源代码,使用await将网络IO请求挂起,程序继续执行其他任务,等待内容返回后再跳转到此处继续执行htm=await Response.text()#使用正则提取出所有的图片地址以及相册的名字pic_urls=re.findall('''<img src="(.*?)" alt=".*?" border="0" />.*?''',htm,re.S)title=re.findall('''<h1 class="entry-title">(.*?)</h1>''',htm,re.S)[0]#返回所有的图片地址以及相册的名字return pic_urls,title

5.下载并保存图片

#定义一个协程函数,参数为一个相册的所有图片地址以及相册的名字
async def download(pic_urls,title):header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#为一个相册创建一个文件夹dir_name = title#判断是否有这个文件夹,有则结束,否则创建一个文件夹并将图片放入if os.path.exists(dir_name):print(dir_name + '这个图片夹已存在')return Falseelif not os.path.exists(dir_name):os.mkdir(dir_name)print(f'--------正在下载:  {dir_name}--------')#对每张照片进行异步请求for pic_url in pic_urls:#图片的名字取图片的链接地址pic_name = pic_url.split('/')[-1]async with aiohttp.ClientSession() as session:async with session.get(url=pic_url, headers=header) as Response:#创建一个上下文管理器,并创建一个可异步的文件对象async with aiofiles.open(file=dir_name + '/' + pic_name, mode='wb') as f:#因为Response对象的read()和text()方法会将响应一次性全部读入内存,这会导致内存爆满,导致卡顿,影响效率。#因此采取字节流的形式,每次读取4096个字节并写入文件while True:#遇到IO阻塞的情况则挂起,等待内容返回再跳转到此处继续执行pic_stream = await Response.content.read(4096)#如果读取完毕之后,则跳出此次循环if not pic_stream:break#文件写入为IO操作,挂起后执行其他任务,写入完成后跳转到此处继续执行await f.write(pic_stream)

6.main函数

#定义一个协程函数,参数为一个时间线链接
async def main(date_url):#获取一个时间线中所有的相册链接,使用await进行挂起操作(因为此处get_album_urls为一个协程对象,并且内部有IO等待)album_urls=await get_album_urls(date_url)#获取每个相册中的相册名字以及所有图片地址for album_url in album_urls:#获取一个相册中的所有图片地址以及相册名字,使用await进行挂起操作(因为此处get_pic_urls_and_title为一个协程对象,并且内部有IO等待)pic_urls,title=await get_pic_urls_and_title(album_url)#下载一个相册,使用await进行挂起操作(因为此处download为一个协程对象,并且内部有IO等待)await download(pic_urls,title)

7.主方法

if __name__=="__main__":#创建一个任务列表tasks=[]#使用同步获取所有时间线的地址(因为后面的所有的操作都基于获取到的时间线链接,所以使用同步操作将所有链接获取完毕后再进行接下来的操作)date_list=get_date_list()#为每个时间线链接都创建为一个任务对象for date_url in date_list:#此处不是立即执行main函数,而是创建了一个协程对象task=main(date_url)#将任务添加到任务列表中tasks.append(task)#创建一个事件循环,用户接收信息(接收某个任务的状态,未执行?,执行中?,执行完毕?)loop=asyncio.get_event_loop()#此处是真正执行任务,等待所有任务执行结束(可以认为是一种固定的写法)loop.run_until_complete(asyncio.wait(tasks))#所有任务执行完毕后关闭事件循环,释放资源loop.close()

四、完整代码

import requests #同步的网络请求模块
import re       #正则模块,用于提取数据
import asyncio  #创建并管理事件循环的模块
import aiofiles #可异步的文件操作模块
import aiohttp  #可异步的网络请求模块
import os       #可调用操作系统的模块#定义一个同步函数,使用requests库进行请求
def get_date_list():#目标链接url='https://zhaocibaidicaiyunjian.ml/'#伪装请求头header={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#发送请求r=requests.get(url=url,headers=header)#获取网页页面源代码htm=r.text#使用正则模块re解析获得时间线的链接results=re.findall('''<aside id="archives-2" class="widget widget_archive">(.*?)</aside>''',htm,re.S)date_list_str=str(re.findall('''<a href=(.*?)>.*?</a>''',str(results)))date_list=re.findall('''\\'(.*?)\\\\\\\\\'''',date_list_str)#返回一个所有时间线链接的列表return date_list#判断一个时间线页面是否含有第二页(在下面的协程函数get_album_urls中调用)
def hasnext(Responsetext):if re.findall('''<a class="next page-numbers" href="(.*?)">下一页</a>''', Responsetext):nextpage = re.findall('''<a class="next page-numbers" href="(.*?)">下一页</a>''',Responsetext)[0]return nextpageelse:return None#async 定义一个协程函数,参数为一个时间线的链接
async def get_album_urls(date_url):header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#创建一个上下文管理器,并创建一个可异步的session对象async with aiohttp.ClientSession() as session:#session的get()的方法与requests库中的用法一样(注:requests库中get()方法的代理参数为字典形式proxies=dict,支持http和https。而session中get()方法的代理参数为字符串,proxy=str。且仅支持http,不支持https)async with session.get(url=date_url,headers=header) as Response:#获取网页页面源代码,使用await将网络IO请求挂起,程序继续执行其他任务,等待内容返回后再跳转到此处继续执行htm=await Response.text()#使用正则提取所有相册的链接album_urls=re.findall('''<a href="(.*?)" class="more-link">继续阅读<span class="screen-reader-text">.*?</span></a>''',htm)#判断此时间线页面是否含有第二页nextpage=hasnext(htm)#如果有第二页则提取第二页所有的相册链接if nextpage:async with session.get(url=nextpage,headers=header) as Response1:htm1=await Response1.text()#列表生成器,将第二页中的所有相册链接加入到列表album_urls中[album_urls.append(album_url) for album_url in re.findall('''<a href="(.*?)" class="more-link">继续阅读<span class="screen-reader-text">.*?</span></a>''',htm1)]#返回一个时间线中所有的相册链接return album_urls#async 定义一个协程函数,参数为一个相册链接
async def get_pic_urls_and_title(album_url):header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#创建一个上下文管理器,并创建一个可异步的session对象async with aiohttp.ClientSession() as session:#session的get()的方法与requests库中的用法一样(注:requests库中get()方法的代理参数为字典形式proxies=dict,支持http和https。而session中get()方法的代理参数为字符串,proxy=str。且仅支持http,不支持https)async with session.get(url=album_url,headers=header) as Response:#获取网页页面源代码,使用await将网络IO请求挂起,程序继续执行其他任务,等待内容返回后再跳转到此处继续执行htm=await Response.text()#使用正则提取出所有的图片地址以及相册的名字pic_urls=re.findall('''<img src="(.*?)" alt=".*?" border="0" />.*?''',htm,re.S)title=re.findall('''<h1 class="entry-title">(.*?)</h1>''',htm,re.S)[0]#返回所有的图片地址以及相册的名字return pic_urls,title#定义一个协程函数,参数为一个相册的所有图片地址以及相册的名字
async def download(pic_urls,title):header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36'}#为一个相册创建一个文件夹dir_name = title#判断是否有这个文件夹,有则结束,否则创建一个文件夹并将图片放入if os.path.exists(dir_name):print(dir_name + '这个图片夹已存在')return Falseelif not os.path.exists(dir_name):os.mkdir(dir_name)print(f'--------正在下载:  {dir_name}--------')#对每张照片进行异步请求for pic_url in pic_urls:#图片的名字取图片的链接地址pic_name = pic_url.split('/')[-1]async with aiohttp.ClientSession() as session:async with session.get(url=pic_url, headers=header) as Response:#创建一个上下文管理器,并创建一个可异步的文件对象async with aiofiles.open(file=dir_name + '/' + pic_name, mode='wb') as f:#因为Response对象的read()和text()方法会将响应一次性全部读入内存,这会导致内存爆满,导致卡顿,影响效率。#因此采取字节流的形式,每次读取4096个字节并写入文件while True:#遇到IO阻塞的情况则挂起,等待内容返回再跳转到此处继续执行pic_stream = await Response.content.read(4096)#如果读取完毕之后,则跳出此次循环if not pic_stream:break#文件写入为IO操作,挂起后执行其他任务,写入完成后跳转到此处继续执行await f.write(pic_stream)#定义一个协程函数,参数为一个时间线链接
async def main(date_url):#获取一个时间线中所有的相册链接,使用await进行挂起操作(因为此处get_album_urls为一个协程对象,并且内部有IO等待)album_urls=await get_album_urls(date_url)#获取每个相册中的相册名字以及所有图片地址for album_url in album_urls:#获取一个相册中的所有图片地址以及相册名字,使用await进行挂起操作(因为此处get_pic_urls_and_title为一个协程对象,并且内部有IO等待)pic_urls,title=await get_pic_urls_and_title(album_url)#下载一个相册,使用await进行挂起操作(因为此处download为一个协程对象,并且内部有IO等待)await download(pic_urls,title)if __name__=="__main__":#创建一个任务列表tasks=[]#使用同步获取所有时间线的地址(因为后面的所有的操作都基于获取到的时间线链接,所以使用同步操作将所有链接获取完毕后再进行接下来的操作)date_list=get_date_list()#为每个时间线链接都创建为一个任务对象for date_url in date_list:#此处不是立即执行main函数,而是创建了一个协程对象task=main(date_url)#将任务添加到任务列表中tasks.append(task)#创建一个事件循环,用户接收信息(接收某个任务的状态,未执行?,执行中?,执行完毕?)loop=asyncio.get_event_loop()#此处是真正执行任务,等待所有任务执行结束(可以认为是一种固定的写法)loop.run_until_complete(asyncio.wait(tasks))#所有任务执行完毕后关闭事件循环,释放资源loop.close()

Python异步爬虫之协程抓取妹子图片(aiohttp、aiofiles)相关推荐

  1. python从网址爬图片协程_Python爬虫多任务协程爬取虎牙MM图片

    查看: 4420|回复: 241 [作品展示] Python爬虫多任务协程爬取虎牙MM图片 电梯直达 发表于 2019-4-17 21:35:47 | 只看该作者 |倒序浏览 |阅读模式 马上注册,结 ...

  2. python从网址爬图片协程_python 用 gevent 协程抓取海量网页

    python作为爬虫利器,抓网页的方式简洁明了.爬成百上千的网页,都可以很快爬完,但是如果网页数量上万呢?速度就不能忍受了. 这是一段爬取页面的函数,用了requests库:1 2 3 4 5impo ...

  3. Python 异步 IO 、协程、asyncio、async/await、aiohttp

    From :廖雪峰 异步IO :https://www.liaoxuefeng.com/wiki/1016959663602400/1017959540289152 Python Async/Awai ...

  4. Python小爬虫之协程爬虫快速上手

    文章目录 前言 协程 协程快速上手 协程异步运行 工作流程 任务管理 aiohttp 异步保存 异步回调 前言 爬虫是个好东西,最近要用用这玩意,所以顺便把以前的小东西给发出来,水几篇博客~ 协程 首 ...

  5. python 异步编程:协程与 asyncio

    文章目录 一.协程(coroutine) 1.1 协程的概念 1.2 实现协程的方式 二.asyncio 异步编程 2.1 事件循环 2.2 快速上手 2.3 运行协程 2.4 await 关键字 2 ...

  6. Python开源爬虫项目代码:抓取淘宝、京东、QQ、知网数据--转

    数据来源:数据挖掘入门与实战  公众号: datadw scrapy_jingdong[9]- 京东爬虫.基于scrapy的京东网站爬虫,保存格式为csv.[9]: https://github.co ...

  7. Python开发爬虫之动态网页抓取篇:爬取博客评论数据——通过浏览器审查元素解析真实网页地址...

    由于主流网站都使用JavaScript展示网页内容,和前面简单抓取静态网页不同的是,在使用JavaScript时,很多内容并不会出现在HTML源代码中,而是在HTML源码位置放上一段JavaScrip ...

  8. Python 异步编程之——协程

    1.总则 多进程可以实现真正的并行,但进程间无法进行直接通信且占用资源较多.多线程的使用代价相对多进程较小,但为了解决数据安全问题引入了锁的机制.这又使得多线程并发度降低,同时,使用锁还可能造成死锁. ...

  9. Python新手爬虫,简单制作抓取廖雪峰教程的小爬虫

    先看几张对比图,分别是官网截图和抓取下来的 txt文档的截图,不算那难看的排版的话,内容是一致的,图片用 url替换了! 在整个抓取过程中,除了普通的文本以外,还需要处理 3个地方,分别是:代码.图片 ...

最新文章

  1. MySQL 8.0 Invisible Indexes 和 RDS 5.6 Invisible Indexes介绍
  2. unity3d发布linux版本_微软发布 Linux 版本 Microsoft Defender ATP,并计划将其引入 Android 等移动端...
  3. php 表单处理,用PHP提交from表单的处理方法
  4. 设计模式复习-备忘录模式
  5. 计算机应用基础操作题教学考试,电大教学全国计算机应用基础考试网考内容全部操作题.doc...
  6. cocos2dx build_native.sh clean 命令报错的解决
  7. python abc
  8. 阿里面试官居然问我如何设计一个本地缓存
  9. python处理xps文件_自学WPF--第二十四课XPS文件处理
  10. PS_BaseUse_红眼擦拭
  11. 曹晋睿 受邀担任第七届少儿模特明星盛典亲善大使
  12. Rsync-同步备份服务器脚本
  13. 苹果首批ARKit应用展示 或随iOS 11一起亮相
  14. pear php库,PEARX-不依赖 PEAR 的 PEAR 的 PHP 库
  15. chrome和Chromium有什么区别
  16. 无心剑英汉双语诗005.《浮生若云》
  17. 在纯粹虚拟空间萌发的种子,刚刚萌芽 就要面临夭折
  18. 2022年华东师范大学计科考研复试机试题-详细题解
  19. SAP SMARTFORMS 打印机配置 SPAD
  20. 2018-03-29-阿里菜鸟面试(电面一)

热门文章

  1. error: cannot pull with rebase: Your index contains uncommitted changes.
  2. Python-Excel 零基础学习xlwings,看这篇文章就够了
  3. Sahi 使用技巧4-iframe、下载文件、模态框、访问不了网页的处理
  4. 荣耀VNXP 5G天团全员正式出道,Play4系列空降“走花路”
  5. atop用法_atop学习
  6. error: failed to push some refs to ... 就这篇,一定帮你解决
  7. Boring counting
  8. svg地球昼夜交替动画js特效
  9. BUG监测平台,Sentry 集成全过程。
  10. VIjos 晴天小猪历险记之Number (搜索+链表hash)