爬虫提高效率方式
提高效率的方式
- 线程池
- map(func,alist)
- 多任务的异步协程
- asyncio
- 特殊的函数
- 协程
- 任务对象
- 任务对象绑定
- 事件循环
- asyncio
- from multiprocessing.dummy import Pool
- map(func,alist):
- 可以让func回调函数处理alist中的每一个列表元素,这个处理的过程是基于异步
特殊函数:
- 就是async关键字修饰的一个函数的定义
- 特殊之处:
- 特殊函数被调用后会返回一个协程对象
- 特殊函数调用后内部的程序语句没有被立即执行
- 协程
- 对象。协程==特殊的函数。协程表示的就是一组特定的操作。
- 任务对象
- 高级的协程(对协程的进一步的封装)
- 任务对象==协程==特殊的函数
- 任务对象==特殊的函数
- 绑定回调:
- task.add_done_callback(task)
- 参数task:当前回调函数对应的任务对象
- task.result():返回的就是任务对象对应的特殊函数的返回值
- 事件循环对象
- 创建事件循环对象
- 将任务对象注册到该对象中并且开启该对象
- 作用:loop可以将其内部注册的所有的任务对象进行异步执行
- 挂起:就是交出cpu的使用权。
await:被用做在特殊函数的内部,在被阻塞的时候
wait:给每一个任务赋予一个可被挂起的权限
#【重点】在特殊函数内部的实现中,不可以出现不支持异步的模块(例如time,requests)代码,如果出现了,则会中断整个的异步效果!!!
单线程+多任务异步协程
单线程+多任务异步协程asyncio
-
特殊函数:
- 就是async关键字修饰的一个函数的定义
- 特殊之处:
- 特殊函数被调用后会返回一个协程对象
- 特殊函数调用后内部的程序语句没有被立即执行
-
协程
- 对象。协程==特殊的函数。协程表示的就是一组特定的操作。
-
任务对象
- 高级的协程(对协程的进一步的封装)
- 任务对象协程特殊的函数
- 任务对象==特殊的函数
- 任务对象协程特殊的函数
- 绑定回调:
- task.add_done_callback(task)
- 参数task:当前回调函数对应的任务对象
- task.result():返回的就是任务对象对应的特殊函数的返回值
- task.add_done_callback(task)
- 高级的协程(对协程的进一步的封装)
-
事件循环对象
- 创建事件循环对象
- 将任务对象注册到该对象中并且开启该对象
- 作用:loop可以将其内部注册的所有的任务对象进行异步执行
-
挂起:就是交出cpu的使用权。
-
【重点】在特殊函数内部的实现中,不可以出现不支持异步的模块代码,如果出现了,
则会中断整个的异步效果!!! -
requests一定是不支持异步
-
aiohttp是一个支持异步的网络请求模块
-
环境安装
-
编码流程:
-
大致的架构:
with aiohttp.ClientSession() as s:s.get(url,headers,params,proxy="http://ip:port")
with s.get(url) as response:
#response.read()二进制(.content)
page_text = response.text()
return page_text -
补充细节
-
在每一个with前加上async
-
需要在每一个阻塞操作前加上await
async with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
async with await s.get(url) as response:
#response.read()二进制(.content)
page_text = await response.text()
return page_text
-
-
-
协程基础
将一个任务对象注册到 事件循环对象中 并且开启该任务对象
import asyncio
from time import sleep
#特殊的函数 步骤1
async def get_request(url):
print('正在下载:',url)
sleep(2)
print('下载完毕:',url)
return 'page_text'
#特殊函数的调用 步骤2 此时特殊函数 不远行 并将对象返回给c
c = get_request('www.1.com')
#创建一个任务对象 步骤3 高级的协程(对协程的进一步的封装) 任务对象==协程==特殊的函数
task = asyncio.ensure_future(c)
#创建一个事件循环对象 步骤4 作用:loop可以将其内部注册的所有的任务对象进行异步执行 提高效率
loop = asyncio.get_event_loop()
#将任务对象注册到该对象中并且开启该对象 步骤5
loop.run_until_complete(task)#让loop执行了一个任务
ps注册好后 这个任务对象 就可以异步了
绑定回调
绑定回调函数 可以使 任务函数执行完 自动执行回调函数
import asyncio
from time import sleep
#特殊的函数 步骤1
async def get_request(url):
print('正在下载:',url)
sleep(2)
print('下载完毕:',url)
return 'page_text'
#回调函数的定义(普通的函数) 步骤3
def parse(task):
#参数表示的就是任务对象 task.result():返回的就是任务对象对应的特殊函数的返回值 比如爬取到的文本数据
print('i am callback!!!',task.result())
#特殊函数的调用 步骤2 此时特殊函数 不远行 并将对象返回给c
c = get_request('www.1.com')
#创建一个任务对象 步骤3 高级的协程(对协程的进一步的封装) 任务对象==协程==特殊的函数
task = asyncio.ensure_future(c)
#给任务对象绑定一个回调函数 步骤3 加了回调函数 parse是一个普通函数 也是回调函数
task.add_done_callback(parse)
#创建一个事件循环对象 步骤4 作用:loop可以将其内部注册的所有的任务对象进行异步执行 提高效率
loop = asyncio.get_event_loop()
#将任务对象注册到该对象中并且开启该对象 步骤5
loop.run_until_complete(task)#让loop执行了一个任务
多任务协程+回调
import asyncio
from time import sleep
import time
#特殊的函数
async def get_request(url):
print('正在下载:',url)
await asyncio.sleep(2) #注意
print('下载完毕:',url)
return 'i am page_text!!!'
def parse(task):
page_text = task.result()#拿到任务对象返回值
print(page_text)
start = time.time()
urls = ['www.1.com','www.2.com','www.3.com']
tasks = []#存储的是所有的任务对象。多任务!
for url in urls:
c = get_request(url)#特殊函数的调用
task = asyncio.ensure_future(c)#创建任务对象
task.add_done_callback(parse)#增加回调函数
tasks.append(task)
loop = asyncio.get_event_loop()
#asyncio.wait(tasks):给每一个任务对象赋予一个可被挂起的权限 将列表的任务对象 注册到 事件循环对象
loop.run_until_complete(asyncio.wait(tasks))
print('总耗时:',time.time()-start)
ps
在特殊函数内部的实现中,不可以出现不支持异步的模块代码,如果出现了,
则会中断整个的异步效果!!!
实践
requests 不支持异步模块
换aiohttp
-
aiohttp是一个支持异步的网络请求模块
-
环境安装
-
编码流程:
-
大致的架构:
with aiohttp.ClientSession() as s:s.get(url,headers,params,proxy="http://ip:port")
with s.get(url) as response:
#response.read()二进制(.content)
page_text = response.text()
return page_text -
补充细节
-
在每一个with前加上async
-
需要在每一个阻塞操作前加上await
async with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
async with await s.get(url) as response:
#response.read()二进制(.content)
page_text = await response.text()
return page_text
-
-
-
import asyncio
import aiohttp
import time
from bs4 import BeautifulSoup
#将被请求的url全部整合到一个列表中
urls = ['http://127.0.0.1:5000/bobo','http://127.0.0.1:5000/jay','http://127.0.0.1:5000/tom']
start = time.time()
async def get_request(url):
async with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
async with await s.get(url) as response:
#response.read()二进制(.content)
page_text = await response.text()
return page_text
def parse(task):
page_text = task.result()
soup = BeautifulSoup(page_text,'lxml')
data = soup.find('div',class_="tang").text
print(data)
tasks = []
for url in urls:
c = get_request(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)
aiohttp学习
-
aiohttp是一个支持异步的网络请求模块
-
环境安装
-
编码流程:
-
大致的架构:
with aiohttp.ClientSession() as s:s.get(url,headers,params,proxy="http://ip:port")
with s.get(url) as response:
#response.read()二进制(.content)
page_text = response.text()
return page_text -
补充细节
-
在每一个with前加上async
-
需要在每一个阻塞操作前加上await
async with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
async with await s.get(url) as response:
#response.read()二进制(.content)
page_text = await response.text()
return page_text
-
-
-
大致的架构:
with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
with s.get(url) as response:
#response.read()二进制(.content)
page_text = response.text()
return page_text
补充
在每一个with前加上async
需要在每一个阻塞操作前加上await
async def get_request(url):
async with aiohttp.ClientSession() as s:
#s.get(url,headers,params,proxy="http://ip:port")
async with await s.get(url) as response:
#response.read()二进制(.content)
page_text = await response.text()
return page_text