之前出于兴趣写了个爬虫专门爬取Wallpaper Abyss上的图片,写完之后发现有点慢,仔细查看发现时间大多数都花费在了http请求上了。最近异步编程也是热门,很多语言也都有这个特性,打算使用异步请求的方法改写之前的爬虫,正好也学习学习。
同步的请求
网络爬虫,无非就是使用代码来模拟人的操作,发起http请求,获取网页源码,根据规律找到自己想要的东西。
在这里使用requests库来发起请求,使用BeautifulSoup来解析html,爬取这页的图片为例子:
发送请求获取网页源代码,提取出img标签的src属性,也就是图片的链接,根据图片的链接发送请求获取图片数据,保存到本地中即可。
# 解析某一页并下载
def parse_one_page_wall_papers():
# 获取入口
url = 'https://wall.alphacoders.com/by_sub_category.php?id=172910&name=Aria+Wallpapers'
session.mount(url, adapter)
response = session.get(url, headers=headers, timeout=30)
soup = BeautifulSoup(response.content, 'html.parser') # 解析
thumbs = soup.find_all('div', {'class':'thumb-container'}) # 获取div(图片所在地)
for index, thumb in enumerate(thumbs):
save_pic(urljoin(url, thumb.a.get('href')))
def save_pic(a_url):
session.mount(a_url, adapter)
ori_img_res = session.get(a_url, headers=headers, timeout=20)
# 保存并下载
.......
可以看到,在这里下载图片时,他都是顺序执行的。也就是只有当第一张图片请求完成后,才能进行第二张图片的请求。
自己测试了一下,下载60张图片花费了206秒,虽然方便了操作,但是速度实在太慢。
使用异步
将时间花费在等待io上显然是不划算的,在第一个请求未到达之前,我们应该接着发送第二个、第三个请求,就好比在与qq好友聊天时,发送消息后不必一直等着好友的回复,而去找下一个好友聊天,等到好友消息回复后再接着聊天,显然是一个道理。
改用aiohttp发起请求,将save_pic方法改写成异步的方法:
async def save_pic(a_url):
async with session.get(img.get('src'), headers=headers) as resp:
# 保存并下载
# 以byte形式将图片数据写入
# 创建二级文件目录
# 如果文件存在 则写入存在文件
with open(folder_path, 'wb') as file:
file.write(await resp.read())
file.flush()
file.close() # 关闭文件
.......
注意到await关键字,await之后代表的就是堵塞操作,当遇到await时,方法就放弃执行,直到await之后的操作结束再返回执行await之后的代码。如此一来,在发起请求之后,请求未结束之前,save_pic方法便放弃执行,此时便可以再执行save_pic获取下一张图片。
async def parse_one_page_wall_papers():
# 获取入口
url = 'https://wall.alphacoders.com/by_sub_category.php?id=172910&name=Aria+Wallpapers'
session.mount(url, adapter)
response = session.get(url, headers=headers, timeout=30)
soup = BeautifulSoup(response.content, 'html.parser') # 解析
thumbs = soup.find_all('div', {'class':'thumb-container'}) # 获取div(图片所在地)
get_origin_url_fn = []
for index, thumb in enumerate(thumbs):
get_origin_url_fn.append( save_pic(urljoin(url, thumb.a.get('href'))))
await asyncio.gather(*get_origin_url_fn)
在这里使用asyncio.gather将所有的异步方法设为一个一个异步方法,也就说当所有的图片都下载完毕后,parse_one_page_wall_papers方法才会结束等待。
在使用了异步之后,下载60张图片仅仅花费了25秒,相比于同步的206秒速度提升了8倍,不由得让我深深感叹异步的强大。