前言
aiohttp是一个为Python提供异步HTTP 客户端/服务端编程,基于asyncio(Python用于支持异步编程的标准库)的异步库。
aiohttp强调的是异步并发。提供了对asyncio/await的支持,可以实现单线程并发IO操作。
python异步aiohttp和同步requests
- 下面以爬取豆瓣top250为例
requests抓取:
import requests
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
}
start = time.time()
for page in range(0, 250, 25):
data = requests.get(f"https://movie.douban.com/top250?start=0", headers=headers)
html = etree.HTML(data.text)
title_list = html.xpath('//div[@class="item"]/div/a/img/@alt')
print(title_list)
end = time.time()
print(f"Time consuming:{end -start}")
执行结果:最终消耗时间是1.7s左右
['肖申克的救赎', '霸王别姬', '阿甘正传', '这个杀手不太冷', '美丽人生', '泰坦尼克号', '千与千寻', '辛德勒的名单', '盗梦空间', '忠犬八公的故事', '海上钢琴师', '楚门的世界', '三傻大闹宝莱坞', '机器人总动员', '放牛班的春天', '星际穿越', '大话西游之大圣娶亲', '熔炉', '疯狂动物城', '无间道', '龙猫', '教父', '当幸福来敲门', '怦然心动', '触不可及']
['蝙蝠侠:黑暗骑士', '控方证人', '活着', '乱世佳人', '寻梦环游记', '末代皇帝', '摔跤吧!爸爸', '指环王3:王者无敌', '何以为家', '少年派的奇幻漂流', '飞屋环游记', '十二怒汉', '鬼子来了', '天空之城', '哈尔的移动城堡', '大话西游之月光宝盒', '素媛', '天堂电影院', '罗马假日', '闻香识女人', '辩护人', '哈利·波特与魔法石', '搏击俱乐部', '我不是药神', '死亡诗社']
['教父2', '指环王2:双塔奇兵', '狮子王', '窃听风暴', '大闹天宫', '指环王1:魔戒再现', '两杆大烟枪', '美丽心灵', '猫鼠游戏', '饮食男女', '飞越疯人院', '黑客帝国', '钢琴家', 'V字仇杀队', '本杰明·巴顿奇事', '看不见的客人', '让子弹飞', '西西里的美丽传说', '小鞋子', '拯救大兵瑞恩', '海豚湾', '情书', '穿条纹睡衣的男孩', '音乐之声', '绿皮书']
['美国往事', '海蒂和爷爷', '致命魔术', '低俗小说', '七宗罪', '沉默的羔羊', '蝴蝶效应', '春光乍泄', '禁闭岛', '心灵捕手', '被嫌弃的松子的一生', '布达佩斯大饭店', '阿凡达', '剪刀手爱德华', '摩登时代', '勇敢的心', '天使爱美丽', '喜剧之王', '致命ID', '加勒比海盗', '断背山', '杀人回忆', '狩猎', '哈利·波特与死亡圣器(下)', '幽灵公主']
['请以你的名字呼唤我', '阳光灿烂的日子', '入殓师', '重庆森林', '小森林 夏秋篇', '第六感', '7号房的礼物', '消失的爱人', '红辣椒', '小森林 冬春篇', '爱在黎明破晓前', '侧耳倾听', '玛丽和马克思', '一一', '唐伯虎点秋香', '告白', '超脱', '蝙蝠侠:黑暗骑士崛起', '倩女幽魂', '大鱼', '阳光姐妹淘', '射雕英雄传之东成西就', '萤火之森', '甜蜜蜜', '驯龙高手']
['无人知晓', '超能陆战队', '幸福终点站', '借东西的小人阿莉埃蒂', '菊次郎的夏天', '爱在日落黄昏时', '恐怖直播', '完美的世界', '神偷奶爸', '怪兽电力公司', '玩具总动员3', '血战钢锯岭', '功夫', '风之谷', '傲慢与偏见', '人生果实', '上帝之城', '时空恋旅人', '教父3', '电锯惊魂', '天书奇谭', '喜宴', '英雄本色', '谍影重重3', '被解救的姜戈']
['岁月神偷', '七武士', '哪吒闹海', '我是山姆', '疯狂原始人', '头号玩家', '纵横四海', '三块广告牌', '釜山行', '心迷宫', '达拉斯买家俱乐部', '萤火虫之墓', '真爱至上', '荒蛮故事', '贫民窟的百万富翁', '东邪西毒', '记忆碎片', '爆裂鼓手', '你的名字。', '黑天鹅', '花样年华', '哈利·波特与阿兹卡班的囚徒', '卢旺达饭店', '忠犬八公物语', '黑客帝国3:矩阵革命']
['模仿游戏', '头脑特工队', '一个叫欧维的男人决定去死', '哈利·波特与密室', '雨人', '你看起来好像很好吃', '未麻的部屋', '无敌破坏王', '恋恋笔记本', '冰川时代', '新世界', '海街日记', '二十二', '海边的曼彻斯特', '虎口脱险', '房间', '惊魂记', '恐怖游轮', '魔女宅急便', '奇迹男孩', '人工智能', '雨中曲', '疯狂的石头', '罗生门', '爱在午夜降临前']
['小偷家族', '海洋', '终结者2:审判日', '初恋这件小事', '魂断蓝桥', '燃情岁月', '可可西里', '绿里奇迹', '穿越时空的少女', '2001太空漫游', '牯岭街少年杀人事件', '城市之光', '完美陌生人', '无耻混蛋', '阿飞正传', '新龙门客栈', '源代码', '香水', '谍影重重2', '青蛇', '地球上的星星', '谍影重重', '色,戒', '战争之王', '血钻']
['遗愿清单', '猜火车', '大佛普拉斯', '疯狂的麦克斯4:狂暴之路', '步履不停', '彗星来的那一夜', '朗读者', '浪潮', '小萝莉的猴神大叔', '再次出发之纽约遇见你', '驴得水', '聚焦', '东京物语', '追随', '九品芝麻官', '一次别离', '哈利·波特与火焰杯', '千钧一发', '我爱你', '黑鹰坠落', '四个春天', '网络谜踪', '发条橙', 'E.T. 外星人', '波西米亚狂想曲']
Time consuming:1.792207956314087
aiohttp抓取:
import asyncio
from aiohttp import ClientSession
import time
from lxml import etree
async def load(url, semaphore):
'''
请求函数
:param url: 请求页面路由
:param semaphore: 限制最大并发对象
:return:返回页面信息
'''
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
}
async with semaphore:
async with ClientSession() as session:
async with session.get(url, headers=headers) as response:
return await response.text()
pass
pass
pass
pass
async def pars(url, semaphore):
'''
解析函数
:param url: 请求页面路由
:param semaphore: 限制最大并发对象
:return:
'''
text = await load(url, semaphore) # 获取异步请求返回的值
html = etree.HTML(text)
title_list = html.xpath('//div[@class="item"]/div/a/img/@alt')
print(title_list)
pass
tasks = []
semaphore = asyncio.Semaphore(500) # 限制并发次数,设置并发数为500,处理速度更快
loop = asyncio.get_event_loop() # 创建事件循环对象
start = time.time()
for page in range(0, 250, 25):
url = f"https://movie.douban.com/top250?start={page}"
task = asyncio.ensure_future(pars(url, semaphore)) # 加入到任务列表
tasks.append(task)
pass
loop.run_until_complete(asyncio.wait(tasks)) # 执行异步任务
end = time.time()
print(f"Time consuming:{end -start}")
执行结果:最终耗时0.39,速度提升了四倍多
['蝙蝠侠:黑暗骑士', '控方证人', '活着', '乱世佳人', '寻梦环游记', '末代皇帝', '摔跤吧!爸爸', '指环王3:王者无敌', '何以为家', '少年派的奇幻漂流', '飞屋环游记', '十二怒汉', '鬼子来了', '天空之城', '哈尔的移动城堡', '大话西游之月光宝盒', '素媛', '天堂电影院', '罗马假日', '闻香识女人', '辩护人', '哈利·波特与魔法石', '搏击俱乐部', '我不是药神', '死亡诗社']
['肖申克的救赎', '霸王别姬', '阿甘正传', '这个杀手不太冷', '美丽人生', '泰坦尼克号', '千与千寻', '辛德勒的名单', '盗梦空间', '忠犬八公的故事', '海上钢琴师', '楚门的世界', '三傻大闹宝莱坞', '机器人总动员', '放牛班的春天', '星际穿越', '大话西游之大圣娶亲', '熔炉', '疯狂动物城', '无间道', '龙猫', '教父', '当幸福来敲门', '怦然心动', '触不可及']
['遗愿清单', '猜火车', '大佛普拉斯', '疯狂的麦克斯4:狂暴之路', '步履不停', '彗星来的那一夜', '朗读者', '浪潮', '小萝莉的猴神大叔', '再次出发之纽约遇见你', '驴得水', '聚焦', '东京物语', '追随', '九品芝麻官', '一次别离', '哈利·波特与火焰杯', '千钧一发', '我爱你', '黑鹰坠落', '四个春天', '网络谜踪', '发条橙', 'E.T. 外星人', '波西米亚狂想曲']
['小偷家族', '海洋', '终结者2:审判日', '初恋这件小事', '魂断蓝桥', '燃情岁月', '可可西里', '绿里奇迹', '穿越时空的少女', '2001太空漫游', '牯岭街少年杀人事件', '城市之光', '完美陌生人', '无耻混蛋', '阿飞正传', '新龙门客栈', '源代码', '香水', '谍影重重2', '青蛇', '地球上的星星', '谍影重重', '色,戒', '战争之王', '血钻']
['美国往事', '海蒂和爷爷', '致命魔术', '低俗小说', '七宗罪', '沉默的羔羊', '蝴蝶效应', '春光乍泄', '禁闭岛', '心灵捕手', '被嫌弃的松子的一生', '布达佩斯大饭店', '阿凡达', '剪刀手爱德华', '摩登时代', '勇敢的心', '天使爱美丽', '喜剧之王', '致命ID', '加勒比海盗', '断背山', '杀人回忆', '狩猎', '哈利·波特与死亡圣器(下)', '幽灵公主']
['岁月神偷', '七武士', '哪吒闹海', '我是山姆', '疯狂原始人', '头号玩家', '纵横四海', '三块广告牌', '釜山行', '心迷宫', '达拉斯买家俱乐部', '萤火虫之墓', '真爱至上', '荒蛮故事', '贫民窟的百万富翁', '东邪西毒', '记忆碎片', '爆裂鼓手', '你的名字。', '黑天鹅', '花样年华', '哈利·波特与阿兹卡班的囚徒', '卢旺达饭店', '忠犬八公物语', '黑客帝国3:矩阵革命']
['教父2', '指环王2:双塔奇兵', '狮子王', '窃听风暴', '大闹天宫', '指环王1:魔戒再现', '两杆大烟枪', '美丽心灵', '猫鼠游戏', '饮食男女', '飞越疯人院', '黑客帝国', '钢琴家', 'V字仇杀队', '本杰明·巴顿奇事', '看不见的客人', '让子弹飞', '西西里的美丽传说', '小鞋子', '拯救大兵瑞恩', '海豚湾', '情书', '穿条纹睡衣的男孩', '音乐之声', '绿皮书']
['模仿游戏', '头脑特工队', '一个叫欧维的男人决定去死', '哈利·波特与密室', '雨人', '你看起来好像很好吃', '未麻的部屋', '无敌破坏王', '恋恋笔记本', '冰川时代', '新世界', '海街日记', '二十二', '海边的曼彻斯特', '虎口脱险', '房间', '惊魂记', '恐怖游轮', '魔女宅急便', '奇迹男孩', '人工智能', '雨中曲', '疯狂的石头', '罗生门', '爱在午夜降临前']
['无人知晓', '超能陆战队', '幸福终点站', '借东西的小人阿莉埃蒂', '菊次郎的夏天', '爱在日落黄昏时', '恐怖直播', '完美的世界', '神偷奶爸', '怪兽电力公司', '玩具总动员3', '血战钢锯岭', '功夫', '风之谷', '傲慢与偏见', '人生果实', '上帝之城', '时空恋旅人', '教父3', '电锯惊魂', '天书奇谭', '喜宴', '英雄本色', '谍影重重3', '被解救的姜戈']
['请以你的名字呼唤我', '阳光灿烂的日子', '入殓师', '重庆森林', '小森林 夏秋篇', '第六感', '7号房的礼物', '消失的爱人', '红辣椒', '小森林 冬春篇', '爱在黎明破晓前', '侧耳倾听', '玛丽和马克思', '一一', '唐伯虎点秋香', '告白', '超脱', '蝙蝠侠:黑暗骑士崛起', '倩女幽魂', '大鱼', '阳光姐妹淘', '射雕英雄传之东成西就', '萤火之森', '甜蜜蜜', '驯龙高手']
Time consuming:0.39294934272766113
假如你的并发达到2000个,程序会报错:ValueError: too many file descriptors in select()。报错的原因字面上看是 Python 调取的 select 对打开的文件有最大数量的限制,这个其实是操作系统的限制,linux打开文件的最大数默认是1024,windows默认是509,超过了这个值,程序就开始报错。这里我们有三种方法解决这个问题:
1.限制并发数量。(一次不要塞那么多任务,或者限制最大并发数量)
2.使用回调的方式。
3.修改操作系统打开文件数的最大限制,在系统里有个配置文件可以修改默认值,具体步骤不再说明了。
不修改系统默认配置的话,个人推荐限制并发数的方法,设置并发数为500,处理速度更快。