Python爬虫類の性能に関するまとめ


ここでは、ウェブページの例を求めて、爬虫類の性能を理解していきます。
リストがあるとき、私たちはいくつかのurlを保管しています。私たちは関連データを取得する必要があります。まず考えたのは循環です。
単純なシリアル循環
この方法は相対的に一番遅いです。一つの循環のために、時間の消費が一番長いです。すべての時間の合計です。
コードは以下の通りです

import requests

url_list = [
  'http://www.baidu.com',
  'http://www.pythonsite.com',
  'http://www.cnblogs.com/'
]

for url in url_list:
  result = requests.get(url)
  print(result.text)
スレッドを通す
スレッドプールを通じてアクセスすると、全体の時間がかかります。すべての接続に一番時間がかかります。相対的にサイクルが速くなります。

import requests
from concurrent.futures import ThreadPoolExecutor

def fetch_request(url):
  result = requests.get(url)
  print(result.text)

url_list = [
  'http://www.baidu.com',
  'http://www.bing.com',
  'http://www.cnblogs.com/'
]
pool = ThreadPoolExecutor(10)

for url in url_list:
  #           ,     fetch_request  
  pool.submit(fetch_request,url)

pool.shutdown(True)
スレッドプール+コールバック関数
ここではコールバック関数を定義しています。

from concurrent.futures import ThreadPoolExecutor
import requests


def fetch_async(url):
  response = requests.get(url)

  return response


def callback(future):
  print(future.result().text)


url_list = [
  'http://www.baidu.com',
  'http://www.bing.com',
  'http://www.cnblogs.com/'
]

pool = ThreadPoolExecutor(5)

for url in url_list:
  v = pool.submit(fetch_async,url)
  #        
  v.add_done_callback(callback)

pool.shutdown()
プロセス・プールを通る
プロセスプールを通じてアクセスするのも、時間がかかりますが、スレッドに対しては、プロセスはより多くのリソースを必要とします。また、ここはurlにアクセスする際のIO操作ですので、ここでは、スレッド池はプロセスプールよりも良いです。

import requests
from concurrent.futures import ProcessPoolExecutor

def fetch_request(url):
  result = requests.get(url)
  print(result.text)

url_list = [
  'http://www.baidu.com',
  'http://www.bing.com',
  'http://www.cnblogs.com/'
]
pool = ProcessPoolExecutor(10)

for url in url_list:
  #           ,       fetch_request  
  pool.submit(fetch_request,url)

pool.shutdown(True)
プロセスプール+コールバック関数
この方法はスレッド+コールバック関数の効果と同じであり、比較的オープンスレッドよりもオープンスレッドの方がリソースが無駄です。

from concurrent.futures import ProcessPoolExecutor
import requests


def fetch_async(url):
  response = requests.get(url)

  return response


def callback(future):
  print(future.result().text)


url_list = [
  'http://www.baidu.com',
  'http://www.bing.com',
  'http://www.cnblogs.com/'
]

pool = ProcessPoolExecutor(5)

for url in url_list:
  v = pool.submit(fetch_async, url)
  #         
  v.add_done_callback(callback)

pool.shutdown()
主流のシングルスレッドを同時進行させるいくつかの方法
  • asyncico
  • gevent
  • Twisted
  • Tornado
  • 以下はそれぞれこの4つのコードの実現例である。
    asyncico例1:
    
    import asyncio
    
    
    @asyncio.coroutine #         
    def func1():
      print('before...func1......')
      #      yield from,       asyncio.sleep   time.sleep
      yield from asyncio.sleep(2)
      print('end...func1......')
    
    
    tasks = [func1(), func1()]
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.gather(*tasks))
    loop.close()
    上記の効果は、同時に二つのbeforeの内容を印刷し、2秒後にendの内容を印刷することです。
    ここでasyncicoはhttp要求を送る方法を提供していませんが、yield fromでhttp要求を作成する方法があります。
    asyncico例2:
    
    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\r
    Host: %s\r
    \r
    """ % (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', '/zhaof/'), 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()
    asyncico+aiohttpコード例:
    
    import aiohttp
    import asyncio
    
    
    @asyncio.coroutine
    def fetch_async(url):
      print(url)
      response = yield from aiohttp.request('GET', url)
      print(url, response)
      response.close()
    
    
    tasks = [fetch_async('http://baidu.com/'), fetch_async('http://www.chouti.com/')]
    
    event_loop = asyncio.get_event_loop()
    results = event_loop.run_until_complete(asyncio.gather(*tasks))
    event_loop.close()
    asyncico+requestsコードの例
    
    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()
    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={}),
    # ])
    grequestsコードの例
    これはレクネス+geventのパッケージです。
    
    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)
    twistedコードの例
    
    #getPage   requets  ,defer      ,rector      
    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()
    トラナドコード例
    
    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()
    以上はPython爬虫類の性能に関する詳細をまとめました。Python爬虫類の性能に関する資料は他の関連記事に注目してください。