- 自学 python 中的异步编程 asyncio (一):学习基本概念
- 自学 python 中的异步编程 asyncio (二):asyncio模块与核心组件
- 自学 python 中的异步编程 asyncio (三):asyncio 实现基本异步编程
- 自学 python 中的异步编程 asyncio (四):基本的异步IO编程
- 自学 python 中的异步编程 asyncio (五):asyncio 与 线程thread
- 自学 python 中的异步编程 asyncio (六):高级异步编程
- 自学 python 中的异步编程 asyncio:实战(一)爬虫
备注:
- 本文主要用于
协程
实战,重点用于理解协程,理解异步编程 - 重点不是爬虫的全流程代码书写,关于爬虫的技能请参考我的爬虫自学专栏
这个异步爬虫的实现方式如下:
- 首先定义了一个
fetch
函数,用于异步获取指定url
的页面内容。- 该函数使用
aiohttp
库发送异步 HTTP 请求 - 并使用
async with
语句保证资源在使用完成后自动释放,最终返回页面内容。
- 该函数使用
- 然后定义了一个
get_links
函数,用于解析指定页面中的所有链接。- 该函数使用
BeautifulSoup
库解析页面内容,并查找其中所有的<a>
标签 - 然后将其中的
href
属性值作为链接添加到列表中并返回
- 该函数使用
- 接着定义了一个递归的异步函数
crawl
,用于异步爬取指定网站的所有页面- 该函数接受四个参数:
- 当前要爬取的
url
- 最大深度
- 当前使用的
aiohttp.ClientSession
实例 - 已访问过的链接集合以及当前的深度
- 当前要爬取的
- 该函数接受四个参数:
- 函数首先判断是否达到了最大深度或者已经访问过该链接
- 如果满足其中任意一个条件,则直接返回。
- 否则,将该链接添加到已访问过的集合中,然后获取该页面中的所有链接,使用递归的方式异步爬取其中的页面
以下是另一种异步爬虫的示例代码:
import asyncio
import aiohttp
from bs4 import BeautifulSoup
# 在 fetch 中实现对 html 的访问和下载
async def fetch(url, session):
"""
异步获取指定url的页面内容
"""
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299",
"Referer": "https://www.google.com/"
}
async with session.get(url, headers=headers) as response:
return await response.text()
# 解析当前页面的所有 href
async def parse_links(url, session):
"""
异步解析页面内容,获取其中的所有链接
"""
html = await fetch(url, session)
soup = BeautifulSoup(html, 'lxml')
links = []
for link in soup.find_all('a'):
links.append(link.get('href'))
print(soup.select("head > title")[0].get_text())
return links
async def crawl(url, max_depth, session, seen_urls, depth=0):
"""
递归地异步爬取指定网站的所有页面,直到达到最大深度或所有页面都被访问过为止
"""
if depth == max_depth:
return
# 避免重复访问同一个链接
if url in seen_urls:
return
seen_urls.add(url)
# 获取页面中的所有链接
links = await parse_links(url, session)
# 递归地爬取链接中的页面
tasks = []
for link in links:
try:
if link.startswith('http'):
task = asyncio.ensure_future(crawl(link, max_depth, session, seen_urls, depth + 1))
tasks.append(task)
except BaseException:
continue
await asyncio.gather(*tasks)
async def main():
"""
程序入口,异步启动爬虫
"""
async with aiohttp.ClientSession() as session:
seen_urls = set()
await crawl('https://movie.douban.com/top250', 2, session, seen_urls)
if __name__ == '__main__':
asyncio.run(main())
流程:
- 运行协程事件循环
- 入口
main
函数,利用aiohttp
创建clientsession
对象,传入spider,让spider爬行 - 爬行:获取当前网页里面包含的所有地址链接
href
- 判断当前传入地址是否已经访问过,否则加入
seen_urls
集合中 - 获取新地址:利用
ClientSession
实例发起请求,得到html - 获取新地址:解析html,
findall
标签<a>
- 提取出标签里面的
href
属性,添加到links
数组返回
- 判断当前传入地址是否已经访问过,否则加入
- 爬行:遍历当前所有链接,将链接创建为任务,加入task列表
- 爬行:
gather
运行所有协程任务