下载大美女_爬虫aiohttp请求库讲解之高效率下载美女壁纸案例

作者: 时间:2024-05-24 13:10:48 阅读:
爬虫aiohttp请求库讲解之高效率下载美女壁纸案例

aiohttp是asyncio和Python的异步HTTP客户端/服务器。

爬虫常用的是HTTP客户端,跟requests库一样为网络请求库。

requests库是同步的,不能结合asyncio使用,aiohttp常和asyncio库结合使用。

基本使用如下:

import asyncioimport aiohttpasync def main():    data = {'name': 'germeey', 'age': 25}    # 创建ClientSession客户端,之后就可以发送请求了    async with aiohttp.ClientSession() as session:        async with session.get('https://httpbin.org/get') as response:            print('status', response.status)            print('body', await response.text())            print('bytes', await response.read())            print('json', await response.json())if __name__ == '__main__':    asyncio.get_event_loop().run_until_complete(main())    # asyncio.run(main())  3.7之后

1、ClientSession()

我们每次发送请求时,会首先创建一个客户端(类似于浏览器),利用客户端来发送请求。

我们可以以下方式创建:

async with aiohttp.ClientSession() as session:

这种方式创建就不用手动关闭。另一种是手动关闭的形式:

session = aiohttp.ClientSession()await session.close()

这种方式较为常用。因为我们一般会把session作为全局变量使用,这样就不必每个网络请求函数都传入这个session参数。后续代码会见到。

2、请求方式

请求方式的用法和requests库差不多。

get请求:

session.get(url)

post请求:

session.post(url, data=xxx)

以下讲解ClientSession()中常常定义的参数

3、headers参数

为每个请求方式添加请求头,这样我们就不必在get或post请求中再加入请求头了

headers = {    'user-agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36',}session = aiohttp.ClientSession(headers=headers)

4、timeout超时设置

防止程序在某个网络请求上一直阻塞,需要定义这个请求参数。默认设置是5分钟

我们需要先定义一个aiohttp.ClientTimeout(total)对象,之后传给timeout参数。

timeout = aiohttp.ClientTimeout(3)session = aiohttp.ClientSession(timeout=timeout, headers=headers)

如果超时会报错,这时我们在相应位置进行捕获处理。

5、ssl证书设置

如果爬取没有ssl证书的网站就会报ssl证书的错误。我们需要以下设置:

session = aiohttp.ClientSession(connector=aiohttp.TCPConnector(ssl=False))

6、获取响应

我们利用response = session.get(url)得到响应后,通过以下方式获取具体内容:

# 获取响应状态码status = response.status# 获取html代码html = await response.text()# 获取二进制内容bytes = await response.read()# 获取json格式的响应内容, 返回的是字典json = await response.json()

注意:

response.text()通常会猜测出charset编码,如果不准确需要自己指定。在 text(encoding='编码') 即可。

7、控制异步爬取的并发量

通常协程的数量是可以无限多的,但考虑到网站并发量的承受程度,如果并发量过大,可能导致网站挂掉。

我们可以通过控制并发量来解决这个问题,利用asyncio.Semaphore(CONCURRENCY)即可,括号里面的参数即自定义的并发量。

之后将此代码嵌套在get或post请求的外面

async def scrape_api(url):  async with asyncio.Semaphore(CONCURRENCY):    async with session.get(url) as response:      return await response.json()

8、异步爬取美女壁纸案例

# 抓取多页import timefrom bs4 import BeautifulSoupimport osimport atexitimport asyncioimport aiohttp# 日志模块import logginglogging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s: %(message)s')headers = {    'user-agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36',}# 并发量CONCURRENCY = 20INDEX_URL = 'http://www.netbian.com/meinv/index_{page}.htm'# 下载的总页数PAGE_NUMBER = 177# 在此声明这个变量类型,方便pycharm提示它的相应方法session: aiohttp.ClientSession# 程序停止会自动计算下载总时间和下载速度@atexit.registerdef calc_time():    """    日志    :return:    """    # 计算下载总时间    end = time.time()    duration = end - start    hour = int(duration / 3600)    minute = int((duration - hour * 3600) / 60)    seconds = int(duration - hour * 3600 - minute * 60)    # 计算下载速度    size = 0  # 单位是字节    files = os.listdir("image")    for file in files:        try:            size += os.path.getsize("./image/" + file)        except Exception as e:            print(e)    # 单位是M    size = size / 1024 / 1024    # 单位是kb/s    speed = size * 1024 / duration    print("33[31m=" * 100)    print("一共下载了{}个文件, 大小为{:.2f}M".format(len(files), size))    print("下载速度为{:.2f} kb/s".format(speed))    print("耗时{}小时{}分钟{}秒".format(hour, minute, seconds))    print("=" * 100)# 请求url,得到html代码async def get_url(url):    try:        async with asyncio.Semaphore(CONCURRENCY):            logging.info('scraping %s', url)            async with session.get(url) as response:                return await response.text('gbk')    except Exception as e:        logging.error('error occurred while scraping %snthe reason is %s', url, e, exc_info=True)# 请求url,得到响应的二进制文件async def get_bytes(url):    try:        async with asyncio.Semaphore(CONCURRENCY):            logging.info('scraping %s', url)            async with session.get(url) as response:                return await response.read()    except Exception as e:        logging.error('error occurred while scraping %snthe reason is %s', url, e, exc_info=True)# 下载图片async def download(url):    print("正在下载链接地址为 %s 的图片" % url)    # 二进制文件    content = await get_bytes(url)    # url的格式 http://img.netbian.com/file/2021/0122/2861bb5516bd41b0dfe79f6a9538892d.jpg    # 取最后一个"/"之后的字符串作为文件名    filename = url.split("/")[-1]    # 拼写完整的图片路径,其中这里的"."表示当前这个文件所在的目录    file_path = "./image/" + filename    # 将二进制数据写入文件    with open(file_path, 'wb') as f:        f.write(content)async def run(url):    html = await get_url(url)    # 利用BeautifulSoup构建解析器    soup = BeautifulSoup(html, "lxml")    # 选取所有的图片所在的块区域    aElements = soup.select('.list a')    hrefs = [i["href"] for i in aElements]    for href in hrefs:        if href.startswith("/desk"):            # url2是缩略图对应的链接            url2 = "http://www.netbian.com" + href            # 请求缩略图链接得到页面内容            html2 = await get_url(url2)            soup2 = BeautifulSoup(html2, "lxml")            src = soup2.select('.pic img')[0]["src"]            await download(src)async def main():    global session    # 判断当前目录下是否有image文件夹,没有就创建    if not os.path.exists("image"):        os.mkdir("image")    session = aiohttp.ClientSession(headers=headers)    scrape_index_tasks = [asyncio.ensure_future(run(INDEX_URL.format(page=page))) for page in range(1, PAGE_NUMBER + 1)]    scrape_index_tasks[0] = asyncio.ensure_future(run('http://www.netbian.com/meinv/'))    results = await asyncio.gather(*scrape_index_tasks)    await session.close()if __name__ == '__main__':    start = time.time()    # asyncio.get_event_loop().run_until_complete(main())    asyncio.run(main())