python之路 -- 爬虫 -- 高性能相关

高性能爬虫方案:

  多进程

  多线程

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

本质

 1 sk = socket()
 2 # 阻塞
 3 sk.connect(('www.cnblogs.com',80))
 4 
 5 sk.sendall(b"GET /wupeiqi http1.1\r\n.....\r\n\r\n")
 6 sk.sendall(b"POST /wupeiqi http1.1\r\n.....\r\n\r\nuser=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示例

猜你喜欢

转载自www.cnblogs.com/aberwang/p/9301746.html
今日推荐