1.协程初步
协程就是一个函数,但是满足以下特征:
1.有I/O依赖的操作,
2.可以在进行I/O操作时暂停,
3.无法直接执行
它的作用就是对有大量I/O操作的程序进行加速
Python协程属于可等待对象,可以在其他协程中被等待。
说白了,就是在一个有I/O操作的线程等待I/O执行时,去切换下一个线程运行。
小栗子
import asyncio
# async 标记函数是异步函数
async def net():
return 11
async def main():
# net() 没办法直接调用
res = await net()
print(res)
asyncio.run(main())
用睡眠模仿异步I/O操作
"""
用睡眠模仿异步I/O函数
"""
import asyncio
async def hello(i):
print("Hello ", i)
# 不能使用常规的睡眠机制
await asyncio.sleep(3)
print("World ", i)
if __name__ == '__main__':
tasks = [] #创建一个任务列表
for i in range(4):
tasks.append(hello(i))
# 获取事件循环
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
2.使用异步协程来爬取笑话大全
import asyncio
import aiohttp
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.13'
'0 Safari/537.36'
}
# http://xiaohua.zol.com.cn/lengxiaohua/
async def crawl(i):
url = "http://xiaohua.zol.com.cn/lengxiaohua/{}.html".format(str(i))
print("正在爬取。。。", i)
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(url) as resp:
print(resp.status)
text = await resp.text()
soup = BeautifulSoup(text, 'lxml')
list = soup.select('.article-list .article-title a')
for li in list:
print(li.get_text())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
tasks = [crawl(i) for i in range(1, 10)]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
3.使用线程池的异步操作
"""
使用requests的异步
"""
import asyncio,requests
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.13'
'0 Safari/537.36'
}
def crawl(i):
print("正在执行",i)
url = "http://xiaohua.zol.com.cn/lengxiaohua/{}.html".format(i)
html = requests.get(url,headers=headers)
print(html.status_code)
soup = BeautifulSoup(html.text,'lxml')
list = soup.select('.article-list .article-title a')
for li in list:
print(li.get_text())
async def main():
loop = asyncio.get_event_loop()
tasks = []
with ThreadPoolExecutor(max_workers=10) as t:
for i in range(1,10):
tasks.append(loop.run_in_executor(
t, # 线程池执行器
crawl, # 要执行的方法,
i #传入的参数
))
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
4.分布式Celery的简单操作
分布式计算思想:
将大任务分散成几个小任务,交给分布式网络中的计算机去完成。
在分布式计算的环境中,必须保证网络中计算机的可用性(避免网络延迟,非预知的崩溃等)。
所以就需要可以可持续的监控框架。
有一个分布式系统基础特征产生的问题:网络由不同操作系统的计算机组成,很多互不兼容。
所以有了兼容不同环境的框架,比如Celery。
说白了,Celery就是将提交的高并发量的任务,转交给其余服务端来处理。
分布式爬取笑话大全的一个小栗子
# 服务端
from celery import Celery
import requests
from bs4 import BeautifulSoup
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.13'
'0 Safari/537.36'
}
app = Celery(
'tasks',
backend='redis://127.0.0.1:6379/0', # Celery处理完结果后存储到哪里
broker='redis://127.0.0.1:6379/1' # broker 消息代理分发器,分发任务
)
# 装饰器,表明这是一个Celery的任务 ,这里只定义了一个任务
@app.task
def get_html():
print("正在执行")
url = "http://xiaohua.zol.com.cn/lengxiaohua/1.html"
html = requests.get(url, headers=headers)
print(html.status_code)
soup = BeautifulSoup(html.text, 'lxml')
list = soup.select('.article-list .article-title a')
result = '' #任务返回的结果
for li in list:
print(li.get_text())
result += li.get_text()
return result
# 客户端
from server import get_html
from celery.result import AsyncResult
tasks = []
for i in range(10):
task = get_html.delay() # 这是celery获取结果的ID
tasks.append(task) # 将这些ID存储起来,为了以后能找到相应结果
for t in tasks:
print(AsyncResult(t.get()))
这里只是一个小栗子。了解一下Celery。