从爬虫出发讲讲异步IO

本文探讨了爬虫技术中如何利用异步IO提升爬取效率,详细解析了非阻塞socket与IO多路复用的工作原理,通过自定义异步IO源码示例,展示了如何避免阻塞点,实现连续发送请求,加速数据接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

爬虫中一个很重要的性能就是爬取速度

假设我们要同时爬取好几个网站的内容,在一个线程中用requests模块实现的都是串行:给一个网站发送连接后,等待连接成功,这里是一个阻塞点,然后等待网站发送数据后来,我才能接收完数据发送另一个请求,这又是一个阻塞点,这样速度就会很慢

我们能够通过创建多进程或是多线程来解决上面的问题,但也会带来资源浪费的问题

除此之外,有很多的框架可以帮我们解决这个问题,比如gevent,Twsited,Tornado等,它们都是在一个线程中实现了异步IO,达到提高爬取速度的效果

那异步IO是什么呢?

首先我们爬取网站,发送的都是http请求,http请求基于Tcp,两者都是通过socket发送请求,只是发送数据的格式不同

tcp可直接发送字符串的字节形式,http要求是请求头加请求体字符串格式的字节形式

知道了怎么发送请求的了,也就知道了为何阻塞。
那就是socket默认在connect和recev阻塞,但可通过

sk = socket.socket()
sk.setblocking(False)

来变为非阻塞,但会相应报错(类似于队列通信),所以要用异常处理

没有了阻塞,我们就能够不等待而连续发送请求了,那么连接成功和数据到达是我们又是怎么知道,然后处理呢?

这里就引入了***IO多路复用,select模块***,它能帮助我们去监听何时连接成功了,何时数据到达了,

 while True:     #事件循环
            rlist,wlist,elist=select.select(self.conn,self.connections,self.conn,0.05)

监听的对象不一定是socket对象,但要有fileno方法,并返回一个文件描述符

这样,可以得出一个结论:

异步IO等于非阻塞socket+IO多路复用 ,有数据时自动执行回调函数

有人认为异步IO就是非阻塞IO,这也没有问题,比如在这里的IO多路复用只是监视作用,对IO阻塞什么的没有影响

下面贴上一个自定义异步IO源码:

'''socket客户端'''

import select
import socket
import time

class HttpRequest:
    def __init__(self,sk,host,callback):
        self.socket = sk
        self.host = host
        self.callback=callback
    def fileno(self):
        return self.socket.fileno()

class AsyncRequest:
    def __init__(self):
        self.conn = []
        self.connections = []   #用于检测是否已经连接成功


    def add_request(self, host,callback):
        """创建一个要请求"""

        try:
            sk = socket.socket()
            sk.setblocking(False)
            sk.connect((host, 80))  # 原本的阻塞点
            print("连接成功了")
        except BlockingIOError as e:
            pass
        request=HttpRequest(sk,host,callback)
        self.conn.append(request)
        self.connections.append(request)

    def run(self):
        while True:     #事件循环
            rlist,wlist,elist=select.select(self.conn,self.connections,self.conn,0.05)
            for w in wlist:
                print(w.host,'连接成功')
                #只要能循环得到,表示服务器端和客户端链接成功
                tpl="GET / HTTP/1.0\r\nHost: %s\r\n\r\n" % (w.host,)
                w.socket.send((bytes(tpl,encoding='utf-8')))
                self.connections.remove(w)
            for r in rlist:

                #r是Httprequest对象
                recv_data=bytes()   #空字节变量
                while True:
                    try:
                        chunk=r.socket.recv(8096)
                        recv_data+=chunk
                    except Exception as e:
                        break
                print(r.host,"有数据返回",recv_data)
                '''recv_data是httpresponse,包含请求头和请求体,可在回调函数中做相应处理'''
                r.callback(recv_data)
                r.socket.close()
                self.conn.remove(r)

            if len(self.conn)==0:
                break
def callback1(data):
    print('保存到文件',data)
def callback2(data):
    print('保存到数据库',data)


url_list = [
    {'host': 'www.baidu.com',
     'callback': callback1},
    {'host': 'cn.bing.com', 
     'callback':  callback2},
]
req = AsyncRequest()
for item in url_list:
    print(item)
    req.add_request(item['host'],item['callback'])

req.run()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值