zoukankan      html  css  js  c++  java
  • python之路 -- 爬虫 -- 高性能相关

    高性能爬虫方案:

      多进程

      多线程

      利用“异步非阻塞”模块实现单线程并发请求。

    本质

     1 sk = socket()
     2 # 阻塞
     3 sk.connect(('www.cnblogs.com',80))
     4 
     5 sk.sendall(b"GET /wupeiqi http1.1
    .....
    
    ")
     6 sk.sendall(b"POST /wupeiqi http1.1
    .....
    
    user=alex&pwd=123")
     7 
     8 # 阻塞
     9 data = sk.recv(8096)
    10 
    11 sk.close()

    IO多路复用:

      监听多个socket是否发生变化

    IO多路复用的作用:

      1.select,内部循环检测socket是否发生变化;最多只能检测1024个socket

      2.poll,内部循环检测socket是否发生变化;检测socket数不限

      3.epoll,通过回调的方式检测socket是否发生变化;检测socket数不限

    什么是异步非阻塞?

    非阻塞:

       不等待(可能会报错,捕捉异常)
       代码:
        sk = socket.socket()
        sk.setblocking(False)
    异步:
      回调,当达到某个指定的状态之后,自动调用特定函数。

    如何自定义异步非阻塞模块?   

    本质:socket+IO多路复用

      基于socket设置setblocking和IO多路复用来实现。
      爬虫发送Http请求本质创建socket对象;
      IO多路复用"循环"监听socket是否发生变化,一旦发生变化, 我们可以自定义操作(触发某个函数的执行)

     什么是协程?

      1. 是“微线程”,不存在;是由程序员人为创造出来并控制程序:先执行某段代码、再跳到某处执行某段代码。
      2.如果遇到非IO请求来回切换:性能更低。

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

     通过yield实现一个协程:

    def func1():
                            
                            print('adsfasdf')
                            print('adsfasdf')
                            print('adsfasdf')
                            yield 1
                            print('adsfasdf')
                            print('adsfasdf')
                            print('adsfasdf')
                            
                            yield 2
                            yield 3
                            yield 4
                            
                        def func2():
                            print('adsfasdf')
                            print('adsfasdf')
                            print('adsfasdf')
                            yield 11
                            yield 12
                            yield 19
                            
                            
                        g1=func1()
                        g2=func2()
                        
                        g1.send(None)
                        g1.send(None)
                        g2.send(None)
    View Code

    通过greenlet模块实现一个协程:

    from greenlet import greenlet
         
    
                        def test1():
                            print 12
                            gr2.switch()
                            print 34
                            gr2.switch()
                         
                         
                        def test2():
                            print 56
                            gr1.switch()
                            print 78
                         
                        gr1 = greenlet(test1)
                        gr2 = greenlet(test2)
                        gr1.switch()
    View Code

    Python内置以及第三方模块提供异步IO请求模块,使用简便大大提高效率,而对于异步IO请求的本质则是【非阻塞Socket】+【IO多路复用】:

     常用的3种:

    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()
    asyncio+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={}),
    # ])
    
    4.gevent + requests
    gevent+requests
                    from twisted.web.client import getPage, defer
                    from twisted.internet import reactor
    
    
                    def all_done(arg):
                        reactor.stop()
    
    
                    def callback(contents):
                        print(contents)
    
    
                    d_list = []
    
                    url_list = ['http://www.bing.com', 'http://www.baidu.com', ]
                    for url in url_list:
                        d = getPage(bytes(url, encoding='utf8'))
                        d.addCallback(callback)
    
                        d_list.append(d)
    
                    # 用于检查是否页面已经全部下载完成,如果已下载完成那么,就停止循环。
                    dlist = defer.DeferredList(d_list)
                    dlist.addBoth(all_done) #
    
                    reactor.run()
    Twisted示例
  • 相关阅读:
    关于二进制——lowbit运算
    代码风格
    焦作区域赛——反思及期望
    第一次参加acm区域赛
    0——1分数问题规划
    [FZYZOJ 1339] 修改密码
    [HDU 1856] More is better
    并查集小结
    [FZYZOJ 1031] 无线网络
    [FZYZOJ 1038] 隧道
  • 原文地址:https://www.cnblogs.com/aberwang/p/9301746.html
Copyright © 2011-2022 走看看