小爬爬4.协程基本用法&&多任务异步协程爬虫示例(大数据量)

1.测试学习

(2)单线程:

from time import sleep
import time
def request(url):
    print('正在请求:',url)
    sleep(2)
    print('下载成功:', url)
urls = ['www.baidu.com','www.sogou.com','www.goubanjia.com']
start = time.time()
for url in urls:
    request(url)
print(time.time()-start)

测试结果:需要6秒多

正在请求: www.baidu.com
下载成功: www.baidu.com
正在请求: www.sogou.com
下载成功: www.sogou.com
正在请求: www.goubanjia.com
下载成功: www.goubanjia.com
6.001747369766235

(2)开启线程池:测试结果是2秒多

from time import sleep
import time
from multiprocessing.dummy import Pool
def request(url):
    print('正在请求:',url)
    sleep(2)
    print('下载成功:', url)
urls = ['www.baidu.com','www.sogou.com','www.goubanjia.com']
start = time.time()
pool=Pool(3)
pool.map(request,urls)
print(time.time()-start)

测试结果:

正在请求: www.baidu.com
正在请求: www.sogou.com
正在请求: www.goubanjia.com
下载成功: www.goubanjia.com
下载成功: www.sogou.com
下载成功: www.baidu.com
2.034695625305176

(3)在程序中是否可以一味的使用多线程,多进程?

推荐:单线程+异步协程(效率最高,用的人不是很多,大量爬取数据是会用到的)

下面了解一下

协程(go和python独有的概念),,协程不会占用很高的内存

领导在乎的是把数据爬取出来.

主要还是request模块的学习.下面学习几个概念这几个概念,一会儿会在代码中有所体现

event_loop:事件循环,相当于一个无限循环,我们可以把一些特殊函数注册(放置)到这个事件循环上,当满足某些条件的时候,函数就会被循环执行。程序是按照设定的顺序从头执行到尾,
运行的次数也是完全按照设定。当在编写异步程序时,必然其中有部分程序的运行耗时是比较久的,需要先让出当前程序的控制权,让其在背后运行,让另一部分的程序先运行起来。当背后运行的程序完成后,
也需要及时通知主程序已经完成任务可以进行下一步操作,但这个过程所需的时间是不确定的,需要主程序不断的监听状态,一旦收到了任务完成的消息,就开始进行下一步。loop就是这个持续不断的监视器。 coroutine:中文翻译叫协程,在 Python 中常指代为协程对象类型,我们可以将协程对象注册到事件循环中, 它会被事件循环调用。我们可以使用
async 关键字来定义一个方法,这个方法在调用时不会立即被执行, 而是返回一个协程对象。 task:任务对象,它是对协程对象的进一步封装,包含了任务的各个状态。 future:任务对象,代表将来执行或还没有执行的任务,实际上和 task 没有本质区别。 另外我们还需要了解 async/await 关键字,它是从 Python 3.6 才出现的,专门用于定义协程。其中,async 定义一个协程,await 用来挂起阻塞方法的执行。

(4)协程基础

#asyncio是python3.6才出来的一种技术
import asyncio
async def requests(url):    #被async修饰,就会没有返回值,内部不会执行,只返回一个协程对象
    print('正在请求:',url)
    print('下载成功:',url)
c=requests('www.baidu.com')
print(c)

得到下面的结果:

<coroutine object requests at 0x000001A6E0150410>
sys:1: RuntimeWarning: coroutine 'requests' was never awaited

(5)升级初版

import asyncio
async def requests(url):  #async本质上是个生成器
    print('正在请求:',url)
    print('下载成功:',url)
c=requests('www.baidu.com')

#实例化一个事件循环对象
loop=asyncio.get_event_loop()

#将协程对象注册到时间循环对象中,并且我们需要启动事件循环对象
loop.run_until_complete(c)    #可以无限循环的位置(c...)

得到下面的结果:

正在请求: www.baidu.com
下载成功: www.baidu.com

(6)

import asyncio
async def requests(url):  #async本质上是个生成器,特殊函数async关键字
    print('正在请求:',url)
    print('下载成功:',url)
c=requests('www.baidu.com')      #协程

#实例化一个事件循环对象
loop=asyncio.get_event_loop()

#添加的新点,任务对象
#方法1:创建一个任务对象,将协程对象封装到了该对象中
task=loop.create_task(c)  #这一步是进一步的处理,.   #任务对象
#方法2:另一种形式实例化任务对象的方法,不需要上边的实例化事件循环对象
# task=asyncio.ensure_future(c)
print(task)   #pending
#将协程对象注册到事件循环对象中,并且我们需要启动事件循环对象
loop.run_until_complete(task)  #括号里边可以有多个参数,无限循环  #事件循环
print(task)     #finished
#核心:绑定回调

注意:任务对象就是对协程的一种封装

结果:

<Task pending coro=<requests() running at F:/Python_workspace_S18/papa_part/day4/2.协程基础.py:49>>
正在请求: www.baidu.com
下载成功: www.baidu.com
<Task finished coro=<requests() done, defined at F:/Python_workspace_S18/papa_part/day4/2.协程基础.py:49> result=None>

(7)给任务对象绑定回调,这个虽然简单但是及其重要

单任务异步协程

#单任务异步协程
import asyncio

async def request(url):
    print('正在请求:', url)
    print('下载成功:', url)
    return url

#回调函数必须有一个参数:task
#task.result():任务对象中封装的协程对象对应的特殊函数内部的返回值
def callbak(task):                  #必须有一个参数,并且必须是任务本身
    print('the callback')
    print(task.result())      #result就是上边函数的url
c = request('www.baidu.com')

loop=asyncio.get_event_loop()

#给任务对象绑定一个回调函数
task=asyncio.ensure_future(c)      #先执行这一步,才能执行回调
task.add_done_callback(callbak)    #这个多了一个回调函数
#什么是回调函数?当任务对象执行完成之后,可以回头调用给其绑定的另外一个函数
loop.run_until_complete(task)       #全部执行完成之后,才执行回调函数

结果:

正在请求: www.baidu.com
下载成功: www.baidu.com
the callback
www.baidu.com

另一种写法:

#单任务异步协程
import asyncio

async def request(url):
    print('正在请求:', url)
    print('下载成功:', url)
    return url

#回调函数必须有一个参数:task
#task.result():任务对象中封装的协程对象对应的特殊函数内部的返回值
def callbak(task):                  #必须有一个参数,并且必须是任务本身
    print('the callback')
    print(task.result())      #result就是上边函数的url
c = request('www.baidu.com')

loop=asyncio.get_event_loop()

#给任务对象绑定一个回调函数
# task=asyncio.ensure_future(c)      #先执行这一步,才能执行回调   #第一种写法
task=loop.create_task(c)                                        #第二种写法
task.add_done_callback(callbak)    #这个多了一个回调函数
#什么是回调函数?当任务对象执行完成之后,可以回头调用给其绑定的另外一个函数
loop.run_until_complete(task)       #全部执行完成之后,才执行回调函数

回调函数的目的:对数据进行解析

(8)多任务异步协程:(1个任务不是异步),

只有多任务异步协程才是有意义的

asyncio出现来了async和await

from time import sleep
import asyncio
import time
urls = ['www.baidu.com','www.sogou.com','www.goubanjia.com']
start = time.time()
async def request(url):
    print('正在请求:',url)
    #在多任务异步协程实现中,不可以出现不支持异步的相关代码。比如time模块中的sleep
    # sleep(2)
    await asyncio.sleep(2)    #可以用asyncio里边的sleep,才能计算出异步时间,支持异步,并且必须加上await才能等待
    print('下载成功:',url)

loop = asyncio.get_event_loop()         #通过事件循环执行
#任务列表:放置多个任务对象
tasks = []
for url in urls:
    c = request(url)
    task = asyncio.ensure_future(c)
    tasks.append(task)                      #依次追加到列表中

loop.run_until_complete(asyncio.wait(tasks))
#作用:将任务对象注册到事件当中,只能启动一次的用法
#里边的参数代表的是,多个事件循环必须是异步的因此需要加上asyncio.wait(),有阻塞就挂起,执行完再搞阻塞

print(time.time()-start)

得到下面的结果:

正在请求: www.baidu.com
正在请求: www.sogou.com
正在请求: www.goubanjia.com
下载成功: www.baidu.com
下载成功: www.sogou.com
下载成功: www.goubanjia.com
2.040722608566284

(9)多任务异步协程在flask中的应用

下面是flaskServer.py文件,首先需要安装flask模块

# Author: studybrother sun
from flask import Flask
import time

app = Flask(__name__)

@app.route('/bobo')
def index_bobo():
    time.sleep(2)    #耗时两秒
    return 'Hello bobo'

@app.route('/jay')
def index_jay():
    time.sleep(2)
    return 'Hello jay'

@app.route('/tom')
def index_tom():
    time.sleep(2)
    return 'Hello tom'

if __name__ == '__main__':
    app.run(threaded=True)

 启动:服务器上的程序,访问下面的网址,需要等待2s钟才能得到下面的结果

上边的服务器启动之后,我们再次启动下面的程序

import requests    #目的是发送请求
import asyncio
import time
#单线程+多任务异步协程
urls = [
    'http://127.0.0.1:5000/jay',
    'http://127.0.0.1:5000/bobo',
    'http://127.0.0.1:5000/tom'
]
start=time.time()
for url in urls:
    page_text=requests.get(url=url).text   #请求url得到的返回值
    print(page_text)
print(time.time()-start)

得到下面的结果:

Hello jay
Hello bobo
Hello tom
6.132113456726074

下面是单线程+异步协程在爬虫中的用法:

import requests
import asyncio
import time
#单线程+多任务异步协程
urls = [
    'http://127.0.0.1:5000/jay',
    'http://127.0.0.1:5000/bobo',
    'http://127.0.0.1:5000/tom'
]
async def get_pageText(url):
    print('正在下载:',url)
    #requests模块是不支持异步操作的。,下面是6s的原因,不支持异步
    page_text = requests.get(url).text
    print('下载完毕:',url)

    return page_text

start = time.time()
tasks = []
for url in urls:
    c = get_pageText(url)
    task = asyncio.ensure_future(c)
    tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))    #这行代码表示注册,以及启动的作用

print(time.time()-start)

得到下面的结果:

正在下载: http://127.0.0.1:5000/jay
下载完毕: http://127.0.0.1:5000/jay
正在下载: http://127.0.0.1:5000/bobo
下载完毕: http://127.0.0.1:5000/bobo
正在下载: http://127.0.0.1:5000/tom
下载完毕: http://127.0.0.1:5000/tom
6.1820220947265625

应该是2s多,怎么会这样????

#aiohttp:支持异步的一个基于网络请求的模块
# pip install aiohttp     #安装这个异步请求模块

import requests
import asyncio
import time
import aiohttp        #支持异步的网络请求内容,升级支持异步请求的requests模块
#单线程+多任务异步协程
urls = [
    'http://127.0.0.1:5000/jay',
    'http://127.0.0.1:5000/bobo',
    'http://127.0.0.1:5000/tom'
]
#代理操作:
#async with await s.get(url,proxy="http://ip:port") as response:
async def get_pageText(url):
   async with aiohttp.ClientSession() as s:         #实例化请求对象
      async with await s.get(url) as response:      #得到相应对象,在异步中有with就需要加上async
           #发起请求也是需要加上await的
           page_text = await response.text()          #注意这个地方text是个方法,得到响应数据就需要先挂起
            # 借助于回调函数进行响应数据的解析操作await
           print(page_text)
           return page_text
#封装回调函数用于数据解析


start = time.time()
tasks = []
for url in urls:
    c = get_pageText(url)
    task = asyncio.ensure_future(c)
    #给任务对象绑定回调函数用于数据解析
    tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

print(time.time()-start)

已经实现.得到下面的结果:

Hello jay
Hello bobo
Hello tom
2.1180801391601562

升级:

#aiohttp:支持异步的一个基于网络请求的模块
# pip install aiohttp

import requests
import asyncio
import time
import aiohttp        #支持异步的网络请求内容,升级支持异步请求的requests模块
#单线程+多任务异步协程
urls = [
    'http://127.0.0.1:5000/jay',
    'http://127.0.0.1:5000/bobo',
    'http://127.0.0.1:5000/tom'
]
#代理操作:写代理时候的区别
#async with await s.get(url,proxy="http://ip:port") as response:
async def get_pageText(url):
   async with aiohttp.ClientSession() as s:         #实例化请求对象
      async with await s.get(url) as response:      #得到相应对象,在异步中有with就需要加上async
           #发起请求也是需要加上await的
           page_text = await response.text()          #注意这个地方text是个方法,得到响应数据就需要先挂起
            # 借助于回调函数进行响应数据的解析操作await
           print(page_text)
           return page_text
#封装回调函数用于数据解析
def parse(task):               #回调函数
    #1.获取响应数据
    page_text = task.result()
    print(page_text+',即将进行数据解析!!!')
    #解析操作写在该位置

start = time.time()
tasks = []
for url in urls:
    c = get_pageText(url)
    task = asyncio.ensure_future(c)
    #给任务对象绑定回调函数用于数据解析
    task.add_done_callback(parse)
    tasks.append(task)
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))

print(time.time()-start)

下面得到的结果:顺序jay>bobo>tom>

  注意:这个是有一定顺序的

Hello jay
Hello jay,即将进行数据解析!!!
Hello bobo
Hello bobo,即将进行数据解析!!!
Hello tom
Hello tom,即将进行数据解析!!!
2.0991756916046143

get里边是paras,post里边是data,这个参数注意一下,还有代理还是有区别的,复数变成了单数

 音频,视频,占用的视频比较大,可以用这个,一般是不用的.

原文地址:https://www.cnblogs.com/studybrother/p/10951229.html