Python 爬虫性能相关总结


Posted in Python onAugust 03, 2020

这里我们通过请求网页例子来一步步理解爬虫性能

当我们有一个列表存放了一些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)

线程池+回调函数

这里定义了一个回调函数callback

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()

主流的单线程实现并发的几种方式

  1. asyncio
  2. gevent
  3. Twisted
  4. Tornado

下面分别是这四种代码的实现例子:

asyncio例子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内容
这里asyncio并没有提供我们发送http请求的方法,但是我们可以在yield from这里构造http请求的方法。

asyncio例子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\nHost: %s\r\n\r\n""" % (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()

asyncio + 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()

asyncio+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代码例子
这个是讲requests+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()

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()

以上就是Python 爬虫性能相关总结的详细内容,更多关于Python 爬虫性能的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Pyramid将models.py文件的内容分布到多个文件的方法
Nov 27 Python
Python中对象迭代与反迭代的技巧总结
Sep 17 Python
python记录程序运行时间的三种方法
Jul 14 Python
python安装numpy&安装matplotlib& scipy的教程
Nov 02 Python
速记Python布尔值
Nov 09 Python
Python+matplotlib+numpy绘制精美的条形统计图
Jan 02 Python
解决新django中的path不能使用正则表达式的问题
Dec 18 Python
解决安装pycharm后不能执行python脚本的问题
Jan 19 Python
Python3之外部文件调用Django程序操作model等文件实现方式
Apr 07 Python
基于python实现计算两组数据P值
Jul 10 Python
Python datetime模块的使用示例
Feb 02 Python
Pyhton模块和包相关知识总结
May 12 Python
python接口自动化之ConfigParser配置文件的使用详解
Aug 03 #Python
Python 利用OpenCV给照片换底色的示例代码
Aug 03 #Python
Python3基于plotly模块保存图片表格
Aug 03 #Python
详解Python的爬虫框架 Scrapy
Aug 03 #Python
Python利用Faiss库实现ANN近邻搜索的方法详解
Aug 03 #Python
Python pexpect模块及shell脚本except原理解析
Aug 03 #Python
python爬虫使用正则爬取网站的实现
Aug 03 #Python
You might like
攻克CakePHP系列三 表单数据增删改
2008/10/22 PHP
ThinkPHP 3.2 版本升级了哪些内容
2015/03/05 PHP
django中的ajax组件教程详解
2018/10/18 PHP
JavaScript 学习笔记(六)
2009/12/31 Javascript
jQuery的animate函数学习记录
2014/08/08 Javascript
JS简单操作select和dropdownlist实例
2014/11/26 Javascript
iframe里使用JavaScript控制主页转向的方法
2015/04/03 Javascript
JavaScript中Form表单技术汇总(推荐)
2016/06/26 Javascript
Reactjs实现通用分页组件的实例代码
2017/01/19 Javascript
javascript 中设置window.location.href跳转无效问题解决办法
2017/02/09 Javascript
原生javascript上传图片带进度条【实例分享】
2017/04/06 Javascript
Angular 2.x学习教程之结构指令详解
2017/05/25 Javascript
详解webpack3如何正确引用并使用jQuery库
2017/08/26 jQuery
基于vue2实现左滑删除功能
2017/11/28 Javascript
Python中的日期时间处理详解
2016/11/17 Python
python如何使用unittest测试接口
2018/04/04 Python
Django框架 信号调度原理解析
2019/09/04 Python
详解python第三方库的安装、PyInstaller库、random库
2021/03/03 Python
CSS3 media queries + jQuery实现响应式导航
2016/09/30 HTML / CSS
惠普加拿大在线商店:HP加拿大
2017/09/15 全球购物
Marriott国际:万豪国际酒店查询预订
2017/09/25 全球购物
Rosetta Stone官方网站:语言学习
2019/01/05 全球购物
Windows和Linux动态库应用异同
2016/07/28 面试题
国培教师自我鉴定
2014/02/12 职场文书
安全生产实施方案
2014/02/23 职场文书
小学生作文评语大全
2014/04/21 职场文书
2014年党的群众路线活动个人整改措施
2014/10/28 职场文书
社区学习党的群众路线教育实践活动心得体会
2014/11/03 职场文书
护士业务学习心得体会
2016/01/25 职场文书
调解协议书范本
2016/03/21 职场文书
手把手教你从零开始react+antd搭建项目
2021/06/03 Javascript
MySQL 常见的数据表设计误区汇总
2021/06/07 MySQL
Python中time与datetime模块使用方法详解
2022/03/31 Python
Alexa停服!网站排名将何去何从?目前还没有替代品。
2022/04/15 杂记
python通过新建环境安装tfx的问题
2022/05/20 Python