爬虫基础--IO多路复用单线程异步非阻塞
最近一直的学习爬虫 ,进行基础的学习
性能相关 参考
https://www.cnblogs.com/wupeiqi/p/6229292.html
# 目标:单线程实现并发HTTP请求
#
# socket
# IO多路复用
# HTTP协议
#
# 流程
# http://www.163.com/new/
# 1. sk连接 IP 禾端口进行连接
# 2.请求信息
# 请求头
# k=v\r\n
# k=v\r\n
# k=v\r\n
# \r\n\r\n
# 请求体 import select
import socket
import time class AsyncTimeoutException(TimeoutError):
"""
请求超时异常类
""" def __init__(self, msg):
self.msg = msg
super(AsyncTimeoutException, self).__init__(msg) class HttpContext(object):
"""封装请求和相应的基本数据""" def __init__(self, sock, host, port, method, url, data, callback, timeout=5):
"""
sock: 请求的客户端socket对象
host: 请求的主机名
port: 请求的端口
method: 请求方式
url: 请求的URL
data: 请求时请求体中的数据
callback: 请求完成后的回调函数
timeout: 请求的超时时间
"""
self.sock = sock #sock: 请求的客户端socket对象
self.callback = callback #callback: 请求完成后的回调函数
self.host = host #host: 请求的主机名
self.port = port # port: 请求的端口
self.method = method #method: 请求方式
self.url = url #url: 请求的URL
self.data = data #data: 请求时请求体中的数据 self.timeout = timeout #timeout: 请求的超时时间 self.__start_time = time.time() #当前时间
self.__buffer = [] #在buffer中写入响应内容 def is_timeout(self):
"""当前请求是否已经超时"""
current_time = time.time()
if (self.__start_time + self.timeout) < current_time:
return True def fileno(self):
"""请求sockect对象的文件描述符,用于select监听"""
return self.sock.fileno() def write(self, data):
"""在buffer中写入响应内容"""
self.__buffer.append(data) def finish(self, exc=None):
"""在buffer中写入响应内容完成,执行请求的回调函数"""
if not exc:
response = b''.join(self.__buffer)
self.callback(self, response, exc)
else:
self.callback(self, None, exc) def send_request_data(self): #发送请求 伪造请求头 请求体
content = """%s %s HTTP/1.0\r\nHost: %s\r\n\r\n%s""" % (
# 请求方式 请求的URL 请求的主机名 请求时请求体中的数据
self.method.upper(), self.url, self.host, self.data,) return content.encode(encoding='utf8') class AsyncRequest(object):
def __init__(self):
self.fds = [] #用于存放 连接有返回值的请求
self.connections = []#用于存放需要连接的请求 def add_request(self, host, port, method, url, data, callback, timeout):
"""创建一个要请求"""
client = socket.socket()
client.setblocking(False)
try:
client.connect((host, port))
except BlockingIOError as e:
pass
# print('已经向远程发送连接的请求')
req = HttpContext(client, host, port, method, url, data, callback, timeout)
self.connections.append(req)
self.fds.append(req) def check_conn_timeout(self):
"""检查所有的请求,是否有已经连接超时,如果有则终止"""
timeout_list = [] #超时列表
for context in self.connections:
if context.is_timeout(): #进行超时检测 如果是超时
timeout_list.append(context) #加入超时列表
for context in timeout_list: #进行超时处理
context.finish(AsyncTimeoutException('请求超时'))
self.fds.remove(context) #进行移除 请求 待返回列表
self.connections.remove(context) #进行移除 请求 待发送列表 def running(self):
"""事件循环,用于检测请求的socket是否已经就绪,从而执行相关操作"""
while True:
if not self.fds: #如果没有请求 直接返回
return
r, w, e = select.select(self.fds, self.connections, self.fds, 0.05) #监测socket对象的变化 for context in r:
sock = context.sock #接收请求 连接
while True:
try:
data = sock.recv(8096)# 取返回值
if not data:#如果没有返回值
self.fds.remove(context) #移除等待返回值 的请求
context.finish()#完成请求
break
else:
context.write(data)
except BlockingIOError as e:
break
except TimeoutError as e: #如果超时,,移除 发送的请求和接收的请求 取消请求
self.fds.remove(context)
self.connections.remove(context)
context.finish(e)
break for context in w:
# 已经连接成功远程服务器,开始向远程发送请求数据
if context in self.fds:
data = context.send_request_data()#请求头 请求体
context.sock.sendall(data)#进行连接
self.connections.remove(context) #移除已经连接成功的请求 self.check_conn_timeout() #检测 是否超时 if __name__ == '__main__':
def callback_func(context, response, ex):
"""
:param context: HttpContext对象,内部封装了请求相关信息
:param response: 请求响应内容
:param ex: 是否出现异常(如果有异常则值为异常对象;否则值为None)
:return:
"""
print(context, response, ex) obj = AsyncRequest()
url_list = [
{'host': 'www.google.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,
'callback': callback_func},
{'host': 'www.baidu.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,
'callback': callback_func},
{'host': 'www.bing.com', 'port': 80, 'method': 'GET', 'url': '/', 'data': '', 'timeout': 5,
'callback': callback_func},
]
for item in url_list:
print(item)
obj.add_request(**item) obj.running()
爬虫基础--IO多路复用单线程异步非阻塞的更多相关文章
- IO多路复用与异步非阻塞
1.基于socket,发送http请求 import socket import requests # 方式一 list=['li','gh ','nn'] for i in list: ret=re ...
- 为什么IO多路复用需要采用非阻塞式IO
近段时间开始学习<Unix网络编程>,代码实现了一个简单的IO多路复用+阻塞式的服务端,在学习了非阻塞式IO后,有一个疑问,即: 假如调用了select,并且关注了几个描述字,当关注的描述 ...
- node 单线程异步非阻塞
链接:http://www.runoob.com/nodejs/nodejs-callback.html 首先什么是单线程异步非阻塞? 单线程的意思整个程序从头到尾但是运用一个线程,程序是从上往下执行 ...
- Python异步非阻塞IO多路复用Select/Poll/Epoll使用,线程,进程,协程
1.使用select模拟socketserver伪并发处理客户端请求,代码如下: import socket import select sk = socket.socket() sk.bind((' ...
- 转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】
下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架, ...
- java的高并发IO原理,阻塞BIO同步非阻塞NIO,异步非阻塞AIO
原文地址: IO读写的基础原理 大家知道,用户程序进行IO的读写,依赖于底层的IO读写,基本上会用到底层的read&write两大系统调用.在不同的操作系统中,IO读写的系统调用的名称可能不完 ...
- IO同步、异步与阻塞、非阻塞
一.同步与异步同步/异步, 它们是消息的通知机制 1. 概念解释A. 同步所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回. 按照这个定义,其实绝大多数函数都是同步调用(例如si ...
- nodejs的异步非阻塞IO
简单表述一下:发启向系统IO操作请求,系统使用线程池IO操作,执行完放到事件队列里,node主线程轮询事件队列,读取结果与调用回调.所以说node并非真的单线程,还是使用了线程池的多线程. 上个图看看 ...
- nginx学习(二)——基础概念之异步非阻塞
上面讲了很多关于nginx的进程模型,接下来,我们来看看nginx是如何处理事件的. 有人可能要问了,nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发 ...
随机推荐
- [Swift]LeetCode1019. 链表中的下一个更大节点 | Next Greater Node In Linked List
We are given a linked list with head as the first node. Let's number the nodes in the list: node_1, ...
- 学习HTML5 canvas遇到的问题
学习HTML5 canvas遇到的问题 1. 非零环绕原则(nonzZero rule) 非零环绕原则是canvas在进行填充的时候是否要进行填充的判断依据. 在判断填充的区域拉一条线出来,拉到图形的 ...
- ElasticSearch(1)---Mysql同步数据到ElSearch
ElasticSearch同步Mysql 先讲项目需求:对于资讯模块添加搜索功能 这个搜索功能我就是采用ElasticSearch实现的,功能刚实现完,所以写这篇博客做个记录,让自己在记录下整个步骤和 ...
- Entitiy Framework Core中使用ChangeTracker持久化实体修改历史
背景介绍 在我们的日常开发中,有时候需要记录数据库表中值的变化, 这时候我们通常会使用触发器或者使用关系型数据库中临时表(Temporal Table)或数据变更捕获(Change Data Capt ...
- 【Java基础】【22IO(其他流)&Properties】
22.01_IO流(序列流)(了解) 1.什么是序列流 序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推. 2.使用方 ...
- Linux基础知识第三讲,拷贝文件跟移动文件命令
目录 Linux基础知识第三讲,拷贝文件跟移动文件命令 一丶常用命令 1.tree命令常用选项 2.cp复制文件命令 3.mv 命令的使用 Linux基础知识第三讲,拷贝文件跟移动文件命令 一丶常用命 ...
- Linux 项目上线管理 MAVEN + expect 一台机器管理所有机器的应用程序
一.目的 在一台服务器上面管理所有机器的应用程序. 设想是通过一条命令能够知道所有应用程序是否running 如果not running 查看具体项目的log 跟踪具体原因,程序问题汇报相关负责人 二 ...
- ASP.NET Core 2.1 : 十二.内置日志、使用Nlog将日志输出到文件
应用离不开日志,虽然现在使用VS有强大的调试功能,开发过程中不复杂的情况懒得输出日志了(想起print和echo的有木有),但在一些复杂的过程中以及应用日常运行中的日志还是非常有用. ASP.NET ...
- 痞子衡嵌入式:飞思卡尔i.MX RT系列MCU启动那些事(8)- 从Raw NAND启动
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是飞思卡尔i.MX RT系列MCU的Raw NAND启动. 前面铺垫了七篇启动系列文章,终于该讲具体Boot Device了,我们知道i. ...
- Jenkins结合.net平台综合之监听git仓库并自动摘取最新代码编译
前面章节我们讲解了Jenkins结合.net平台工具以及一些第三方工具实现项目自动还原,自动编译,自动测试和自动发布.然而实现自动化还有一个关键的步骤就是监听源码仓库变化然后从仓库拉取最新代码,然后再 ...