• 爬虫值解决效率问题的方法


    1

    本质:
    方案:
    多进程 > 多线程 > 单线程

    本质:

    问题内容:阻塞

    sk = socket()
    # 阻塞    链接的时候会有阻塞
    sk.connect(('www.cnblogs.com',80))
    
    sk.sendall(b"GET /wupeiqi http1.1
    .....
    
    ")
    sk.sendall(b"POST /wupeiqi http1.1
    .....
    
    user=alex&pwd=123")
    
    # 阻塞     获取返回数据的时候会有阻塞
    data = sk.recv(8096)
    
    sk.close()

    解决方案

    异步非阻塞

    什么是异步非阻塞?
                    - 非阻塞
                        - 不等待(报错,捕捉异常)
                        - 代码:
                            sk = socket.socket()
                            sk.setblocking(False)
                    - 异步- 回调,当达到某个指定的状态之后,自动调用特定函数。

    用基本的代码实现异步非阻塞

    import socket
    import select
    #创建一个类,内部封装了socket链接,和链接对应的回调函数
    class Request(object):
        def __init__(self,sk,callback):
            self.sk = sk
            self.callback = callback
    
        def fileno(self):
            return self.sk.fileno()
    
    class AsyncHttp(object):
    
        def __init__(self):
            self.fds = []
            self.conn = []
    
        def add(self,url,callback):
            sk = socket.socket()
            sk.setblocking(False)#写上这段话就不阻塞了   tcp协议默认是阻塞的
            try:
                sk.connect((url,80)) #这里解决了阻塞的问题
            except BlockingIOError as e:
                pass
            req = Request(sk,callback) #实例化request对象,将连接和回调函数放进去
            self.fds.append(req)
            self.conn.append(req)
    
        def run(self):
            """
            监听socket是否发生变化
            :return:
            """
            while True:
                """
                fds=[req(sk,callback),req,req]
                conn=[req,req,req]
                """
                r,w,e = select.select(self.fds,self.conn,[],0.05) # sk.fileno() = req.fileno()
    
                # w=已经连接成功的socket列表 w=[sk1,sk2]
                for req in w:
                    req.sk.sendall(b'GET /wupeiqi HTTP/1.1
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 
    (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
    ') # 已经连接成功的socket,无需再继续监听,将链接从表中删除, self.conn.remove(req) # r=服务端给用户返回数据了 r=[sk1,] #这里的数据的顺序也是不定的 那个链接先有返回值,先返回过来
    # 这里解决了第二个阻塞,那个有数据回来 就放到这个 r中 然后对返回数据进行处理
    for req in r: data = req.sk.recv(8096)#获取到响应信息 req.callback(data)#当有信息返回的时候,立刻调用回调函数,执行回调函数 #对返回的数据进行解析的过程 这里体现了异步 req.sk.close() # 断开连接:短连接、无状态 self.fds.remove(req) # 不再监听 if not self.fds: break #当服务器中返回的数据都处理完毕后,VU安谧链接 ah = AsyncHttp() #实例化运行 对象 ###############################################
    ###实际使用 可以这样使用 def callback1(data): #自定义callback函数 print(11111,data) def callback2(data): print(22222,data) def callback3(data): print(333333,data) ah.add('www.cnblogs.com',callback1) # sk1 #往里传链接和回调函数 ah.add('www.baidu.com',callback2) # sk2 ah.add('www.luffycity.com',callback3) # sk3 ah.run() #执行run方法

    1. 什么是协程?
    - 是“微线程”,不存在;是由程序员人为创造出来并控制程序:先执行某段代码、再跳到某处执行某段代码。

    - 如果遇到非IO请求来回切换:性能更低。
    - 如果遇到IO(耗时)请求来回切换:性能高、实现并发(本质上利用IO等待的过程,再去干一些其他的事)

    在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢。

    通过上述代码均可以完成对请求性能的提高,对于多线程和多进行的缺点是在IO阻塞时会造成了线程和进程的浪费,所以异步IO回事首选:

     

    asyncio
    import asyncio
    
    
    @asyncio.coroutine
    def fetch_async(host, url='/'):
        print(host, url)
        reader, writer = yield from asyncio.open_connection(host, 80)
    
        request_header_content = """GET %s HTTP/1.0
    Host: %s
    
    """ % (url, host,)
        request_header_content = bytes(request_header_content, encoding='utf-8')
    
        writer.write(request_header_content)
        yield from writer.drain()
        text = yield from reader.read()
        print(host, url, text)
        writer.close()
    
    tasks = [
        fetch_async('www.cnblogs.com', '/wupeiqi/'),
        fetch_async('dig.chouti.com', '/pic/show?nid=4073644713430508&lid=10273091')
    ]
    
    loop = asyncio.get_event_loop()
    results = loop.run_until_complete(asyncio.gather(*tasks))
    loop.close()
    View Code

    asyncio + requests  这个用的比较多

    asynci适用于帮助实现异步非阻塞的

    request用户封装http请求的

    import asyncio
    import requests
    
    
    @asyncio.coroutine
    def fetch_async(func, *args):
        loop = asyncio.get_event_loop()
        future = loop.run_in_executor(None, func, *args)
        response = yield from future
        print(response.url, response.content)
    
    
    tasks = [
        fetch_async(requests.get, 'http://www.cnblogs.com/wupeiqi/'),
        fetch_async(requests.get, 'http://dig.chouti.com/pic/show?nid=4073644713430508&lid=10273091')
    ]
    
    loop = asyncio.get_event_loop()
    results = loop.run_until_complete(asyncio.gather(*tasks))
    loop.close()
    View Code

    gevent + requests   推荐使用

    import gevent
    
    import requests
    from gevent import monkey
    
    monkey.patch_all()
    
    
    def fetch_async(method, url, req_kwargs):
        print(method, url, req_kwargs)
        response = requests.request(method=method, url=url, **req_kwargs)
        print(response.url, response.content)
    
    # ##### 发送请求 #####
    gevent.joinall([
        gevent.spawn(fetch_async, method='get', url='https://www.python.org/', req_kwargs={}),
        gevent.spawn(fetch_async, method='get', url='https://www.yahoo.com/', req_kwargs={}),
        gevent.spawn(fetch_async, method='get', url='https://github.com/', req_kwargs={}),
    ])
    
    # ##### 发送请求(协程池控制最大协程数量) #####
    # from gevent.pool import Pool
    # pool = Pool(None)
    # gevent.joinall([
    #     pool.spawn(fetch_async, method='get', url='https://www.python.org/', req_kwargs={}),
    #     pool.spawn(fetch_async, method='get', url='https://www.yahoo.com/', req_kwargs={}),
    #     pool.spawn(fetch_async, method='get', url='https://www.github.com/', req_kwargs={}),
    # ])

    grequest

    import grequests
    
    
    request_list = [
        grequests.get('http://httpbin.org/delay/1', timeout=0.001),
        grequests.get('http://fakedomain/'),
        grequests.get('http://httpbin.org/status/500')
    ]
    
    
    # ##### 执行并获取响应列表 #####
    # response_list = grequests.map(request_list)
    # print(response_list)
    
    
    # ##### 执行并获取响应列表(处理异常) #####
    # def exception_handler(request, exception):
    # print(request,exception)
    #     print("Request failed")
    
    # response_list = grequests.map(request_list, exception_handler=exception_handler)
    # print(response_list)
    View Code

    Twisted示例

    from twisted.web.client import getPage, defer
    from twisted.internet import reactor
    
    
    def all_done(arg):
        reactor.stop()
    
    
    def callback(contents):
        print(contents)
    
    
    deferred_list = []
    
    url_list = ['http://www.bing.com', 'http://www.baidu.com', ]
    for url in url_list:
        deferred = getPage(bytes(url, encoding='utf8'))
        deferred.addCallback(callback)
        deferred_list.append(deferred)
    
    dlist = defer.DeferredList(deferred_list)
    dlist.addBoth(all_done)
    
    reactor.run()
    View Code

    7.Tornado

    from tornado.httpclient import AsyncHTTPClient
    from tornado.httpclient import HTTPRequest
    from tornado import ioloop
    
    
    def handle_response(response):
        """
        处理返回值内容(需要维护计数器,来停止IO循环),调用 ioloop.IOLoop.current().stop()
        :param response: 
        :return: 
        """
        if response.error:
            print("Error:", response.error)
        else:
            print(response.body)
    
    
    def func():
        url_list = [
            'http://www.baidu.com',
            'http://www.bing.com',
        ]
        for url in url_list:
            print(url)
            http_client = AsyncHTTPClient()
            http_client.fetch(HTTPRequest(url), handle_response)
    
    
    ioloop.IOLoop.current().add_callback(func)
    ioloop.IOLoop.current().start()
    View Code

    Twisted更多

    from twisted.internet import reactor
    from twisted.web.client import getPage
    import urllib.parse
    
    
    def one_done(arg):
        print(arg)
        reactor.stop()
    
    post_data = urllib.parse.urlencode({'check_data': 'adf'})
    post_data = bytes(post_data, encoding='utf8')
    headers = {b'Content-Type': b'application/x-www-form-urlencoded'}
    response = getPage(bytes('http://dig.chouti.com/login', encoding='utf8'),
                       method=bytes('POST', encoding='utf8'),
                       postdata=post_data,
                       cookies={},
                       headers=headers)
    response.addBoth(one_done)
    
    reactor.run()
    View Code
  • 相关阅读:
    学习进度笔记06
    学习进度笔记05
    学习进度笔记04
    学习进度笔记03
    学习进度笔记02
    周总结13
    周总结12
    周总结11
    人月神话阅读笔记3
    第一阶段冲刺10
  • 原文地址:https://www.cnblogs.com/wangkun122/p/9021845.html
Copyright © 2020-2023  润新知