4

之前出于兴趣写了个爬虫专门爬取Wallpaper Abyss上的图片,写完之后发现有点慢,仔细查看发现时间大多数都花费在了http请求上了。最近异步编程也是热门,很多语言也都有这个特性,打算使用异步请求的方法改写之前的爬虫,正好也学习学习。

同步的请求

网络爬虫,无非就是使用代码来模拟人的操作,发起http请求,获取网页源码,根据规律找到自己想要的东西。
在这里使用requests库来发起请求,使用BeautifulSoup来解析html,爬取这页的图片为例子:
image.png

发送请求获取网页源代码,提取出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倍,不由得让我深深感叹异步的强大。

参考链接:
协程与任务api
理解 JavaScript 的 async/await


鲸冬香
456 声望27 粉丝

引用和评论

0 条评论