首页 > 其他分享 >Spider理论系列--协程(二)

Spider理论系列--协程(二)

时间:2023-03-20 16:34:38浏览次数:54  
标签:协程 name get -- Spider url session async book

aiohttp与aiofiles

1、安装与使用

pip install aiohttp

2、简单实例使用

aiohttp的自我介绍中就包含了客户端和服务器端,所以我们分别来看下客户端和服务器端的简单实例代码。

客户端:

import aiohttp
import asyncio

async def fetch(session, url):
async with session.get(url) as response:
return await response.text()


async def main():
async with aiohttp.ClientSession() as session:
html = await fetch(session, "http://httpbin.org/headers")
print(html)

asyncio.run(main())


"""输出结果:
{
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "Python/3.7 aiohttp/3.6.2"
}
}
"""

这个代码是不是很简单,一个函数用来发起请求,另外一个函数用来下载网页。

3、入门

简单示范

首先是学习客户端,也就是用来发送http请求的用法。首先看一段代码,会在代码中讲述需要注意的地方:

import aiohttp
import asyncio

async def main():
async with aiohttp.ClientSession() as session:
async with session.get('http://httpbin.org/get') as resp:
print(resp.status)
print(await resp.text())

asyncio.run(main())

代码解释:

在网络请求中,一个请求就是一个会话,然后aiohttp使用的是ClientSession来管理会话,所以第一个重点,看一下ClientSession:

class ClientSession:
"""First-class interface for making HTTP requests."""

在源码中,这个类的注释是使用HTTP请求接口的第一个类。然后上面的代码就是实例化一个ClientSession类然后命名为session,然后用session去发送请求。这里有一个坑,那就是​​ClientSession.get()​​协程的必需参数只能是​​str​​类和​​yarl.URL​​的实例。

当然这只是get请求,其他的请求都是支持的:

session.post('http://httpbin.org/post', data='data')
session.get('http://httpbin.org/get')

4、在URL中传递参数

有时候在发起网络请求的时候需要附加一些参数到url中,这一点也是支持的。

import aiohttp
import asyncio

async def main():
async with aiohttp.ClientSession() as session:
params = {'key1': 'value1', 'key2': 'value2'}
async with session.get('http://httpbin.org/get',
params=params) as resp:
print(resp.url)

asyncio.run(main())

我们可以通过​​params​​参数来指定要传递的参数,

同时如果需要指定一个键对应多个值的参数,那么​​MultiDict​​就在这个时候起作用了。你可以传递两个元祖列表来作为参数:

import aiohttp
import asyncio

async def main():
async with aiohttp.ClientSession() as session:
params = [('key', 'value1'), ('key', 'value2')]

async with session.get('http://httpbin.org/get',
params=params) as r:
expect = 'http://httpbin.org/get?key=value2&key=value1'
# assert str(r.url) == expect
print(r.url)
asyncio.run(main())

5、读取响应内容

我们可以读取到服务器的响应状态和响应内容,这也是使用请求的一个很重要的部分。通过​​status​​来获取响应状态码,​​text()​​来获取到响应内容,当然也可以之计指明编码格式为你想要的编码格式:

200
{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "httpbin.org",
"User-Agent": "Python/3.9 aiohttp/3.8.1",
"X-Amzn-Trace-Id": "Root=1-63e0a26c-2c16f88b512f2981737311be"
},
"origin": "111.25.99.24",
"url": "http://httpbin.org/get"
}

6、非文本内容格式

对于网络请求,有时候是去访问一张图片,这种返回值是二进制的也是可以读取到的:

await resp.read()

将​​text()​​方法换成​​read()​​方法就好。

7、请求的自定义

ClientResponse(客户端响应)对象含有request_info(请求信息),主要是urlheaders信息。 raise_for_status结构体上的信息会被复制给ClientResponseError实例。

(1) 自定义Headers

有时候做请求的时候需要自定义headers,主要是为了让服务器认为我们是一个浏览器。然后就需要我们自己来定义一个headers:

headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko)"
" Chrome/78.0.3904.108 Safari/537.36"
}
await session.post(url, headers=headers)

(2) 如果出现ssl验证失败的处理

import aiohttp
import asyncio
from aiohttp import TCPConnector


async def main():
async with aiohttp.ClientSession(connector=TCPConnector(ssl=False)) as session:
pass
asyncio.run(main())

(3) 自定义cookie

发送你自己的cookies给服务器,你可以为ClientSession对象指定cookies参数:

url = 'http://httpbin.org/cookies'
cookies = {'cookies_are': 'working'}
async with ClientSession(cookies=cookies) as session:
async with session.get(url) as resp:
assert await resp.json() == {
"cookies": {"cookies_are": "working"}}

(4) 使用代理

有时候在写爬虫的时候需要使用到代理,所以aiohttp也是支持使用代理的,我们可以在发起请求的时候使用代理,只需要使用关键字​​proxy​​来指明就好,但是有一个很难受的地方就是它只支持​​http​​代理,不支持HTTPS代理。使用起来大概是这样:

proxy = "http://127.0.0.1:10809
async with aiohttp.ClientSession(headers=headers) as session:
async with session.get(url=login_url, proxy=proxy) as response:
resu = await response.text()

使用起来大概是这样,然后代理记得改成自己的。

8、aiofiles文件读写

8.1 概述

平常使用的file操作模式为同步,并且为线程阻塞。当程序I/O​​并发​​次数高的时候,CPU被阻塞,形成闲置。

线程开启文件读取异步模式

用线程(Thread)方式来解决。硬盘缓存可以被多个线程访问,因此通过不同线程访问文件可以部分解决。但此方案涉及线程开启关闭的开销,而且不同线程间数据交互比较麻烦。

from threading import Thread
for file in list_file:
tr = Thread(target=file.write, args=(data,))
tr.start()

使用已编写好的第三方插件-aiofiles,支持异步模式

使用aio插件来开启文件的非阻塞异步模式。

8.2 安装方法

pip install aiofiles

这个插件的使用和python原生open 一致,而且可以支持异步迭代

8.3 实例

打开文件

import asyncio
import aiofiles

async def main():
async with aiofiles.open('first.m3u8', mode='r') as f:
contents = await f.read()
print(contents)

if __name__ == '__main__':
asyncio.run(main())

迭代

import asyncio
import aiofiles

async def main():
async with aiofiles.open('filename') as f:
async for line in f:
print(line)

if __name__ == '__main__':
asyncio.run(main())

9、并发控制

semaphore,控制并发

semaphore = asyncio.Semaphore(10)

这里的并发控制大家可以去​​Spider实战系列-爬取鬼吹灯小说_浅辄的技术博客_51CTO博客​​这一篇文章里看

Spider理论系列--协程(二)_aiofiles

这里就是使用asyncio里的并发控制包来设置多少个任务能并行运作

import asyncio
import os
import aiofiles
import aiohttp
import requests
from bs4 import BeautifulSoup


def get_page_source(web):
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36'
}
response = requests.get(web, headers=headers)
response.encoding = 'utf-8'
return response.text


def parse_page_source(html):
book_list = []
soup = BeautifulSoup(html, 'html.parser')
a_list = soup.find_all('div', attrs={'class': 'mulu-list quanji'})
for a in a_list:
a_list = a.find_all('a')
for href in a_list:
chapter_url = href['href']
book_list.append(chapter_url)
return book_list


def get_book_name(book_page):
book_number = book_page.split('/')[-1].split('.')[0]
book_chapter_name = book_page.split('/')[-2]
return book_number, book_chapter_name


async def aio_download_one(chapter_url, signal):
number, c_name = get_book_name(chapter_url)
for c in range(10):
try:
async with signal:
async with aiohttp.ClientSession() as session:
async with session.get(chapter_url) as resp:
page_source = await resp.text()
soup = BeautifulSoup(page_source, 'html.parser')
chapter_name = soup.find('h1').text
p_content = soup.find('div', attrs={'class': 'neirong'}).find_all('p')
content = [p.text + '\n' for p in p_content]
chapter_content = '\n'.join(content)
if not os.path.exists(f'{book_name}/{c_name}'):
os.makedirs(f'{book_name}/{c_name}')
async with aiofiles.open(f'{book_name}/{c_name}/{number}_{chapter_name}.txt', mode="w",
encoding='utf-8') as f:
await f.write(chapter_content)
print(chapter_url, "下载完毕!")
return ""
except Exception as e:
print(e)
print(chapter_url, "下载失败!, 重新下载. ")
return chapter_url


async def aio_download(url_list):
tasks = []
semaphore = asyncio.Semaphore(10)
for h in url_list:
tasks.append(asyncio.create_task(aio_download_one(h, semaphore)))
await asyncio.wait(tasks)


if __name__ == '__main__':
url = 'https://www.51shucheng.net/daomu/guichuideng'
book_name = '鬼吹灯'
if not os.path.exists(book_name):
os.makedirs(book_name)
source = get_page_source(url)
href_list = parse_page_source(source)
loop = asyncio.get_event_loop()
loop.run_until_complete(aio_download(href_list))

需要注意,当前对于Windows系统可能会有报错,原因出在文件名称有特殊字符,可以将特殊字符进行替换后即可

写在最后

到这里协程的部分已经写完了,总体来说,协程在理解难度上有一点难,但是速度还是很可观的。其实我们纵观全文来看,协程的操作都是固定的,我们只需要记住,在哪里需要加上await这个关键字,只要是使用了异步的操作,不论是函数还是with操作对,我们都需要加入async这个关键字,这也是我们区别于前面的代码操作,总的来说,博主认为学号协程并不难,难的是把思想从同步的操作转换为异步的操作,我认为小伙伴先梳理好协程的逻辑,在看一看实战的代码,这样你就会很深刻的理解了协程的运作方式。

标签:协程,name,get,--,Spider,url,session,async,book
From: https://blog.51cto.com/u_15915681/6133175

相关文章

  • CentOS 7.X部署Tomcat
    1、安装基础库yum-yinstallapr-develgccgcc-c++makeopenssl-developenssljava-11-openjdk-*libffi-develredhat-rpm-configlrzsz2、编译安装native(默认tomcat中......
  • mysql视图
    1.常见的数据库对象2.视图2.1为什么使用视图视图一方面可以帮我们使用表的一部分而不是所有的表,另一方面也可以针对不同的用户制定不同的查询视图。比如,针对一个公司的......
  • 盒子模型——页面布局的灵魂
       ......
  • 通过appId和appSecret生成accessToken访问api后端接口(接口授权)
    功能点(1)申请获取appId和appSecret(2)通过appId和appSecret获取accessToken(3)mysql和redis进行key-value键值对存储(4)对需要的接口url进行请求地址拦截效果图:(1)正确的accessToken......
  • Linq不经常使用但必须记住的用法
       1.匿名方法2.OfType用法3.AsEnumerable用法4.柯里化(Currying)表达式5.匿名类型的相等性6.次要排序7.Intersect扩展方法8.Union扩展方法9.Except扩展方法10.Group......
  • CentOS7.X 部署OpenVPN
    1、实验环境CentOS7.9内网ens32:172.16.9.129外网ens33:10.33.56.1002、配置路由转发echo"net.ipv4.ip_forward=1">>/etc/sysctl.d/99-sysctl.confsysctl-p3、安装openv......
  • ASEMI代理NXP汽车芯片PCF7939MA
    编辑-ZNXP汽车芯片PCF7939MA参数描述:型号:PCF7939MA制造商:NXPSemiconductors种类:NFC/RFID标签和应答器存储容量:456b工作频率:125kHz最大工作温度:-85℃最小工作温......
  • ASEMI代理瑞萨TW6869-TA1-CRH汽车芯片
    编辑-ZTW6869-TA1-CRH是一款高度集成的解决方案,支持通过PCIex1接口进行多通道视频和音频捕获,用于PCDVR系统和视频分析应用程序。它包含高质量的八通道NTSC/PAL/SECAM视......
  • 图像的双边滤波matlab仿真
    1.算法描述图像去噪是用于解决图像由于噪声干扰而导致其质量下降的问题,通过去噪技术可以有效地提高图像质量,增大信噪比,更好的体现原来图像所携带的信息。在我们的图像中常......
  • 搬运python 框架
    从GitHub中整理出的15个最受欢迎的Python开源框架。这些框架包括事件I/O,OLAP,Web开发,高性能网络通信,测试,爬虫等。 Django:PythonWeb应用开发框架Django......