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 相关文章推荐
使用 Python 获取 Linux 系统信息的代码
Jul 13 Python
Python 40行代码实现人脸识别功能
Apr 02 Python
python爬虫入门教程--利用requests构建知乎API(三)
May 25 Python
Python使用asyncio包处理并发详解
Sep 09 Python
Python数据可视化编程通过Matplotlib创建散点图代码示例
Dec 09 Python
Python快速查找list中相同部分的方法
Jun 27 Python
决策树剪枝算法的python实现方法详解
Sep 18 Python
Python 用turtle实现用正方形画圆的例子
Nov 21 Python
Python阶乘求和的代码详解
Feb 14 Python
几款Python编译器比较与推荐(小结)
Oct 15 Python
2021年pycharm的最新安装教程及基本使用图文详解
Apr 03 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
php笔记之:数据类型与常量的使用分析
2013/05/14 PHP
php判断手机访问还是电脑访问示例分享
2014/01/20 PHP
PHP的Yii框架中行为的定义与绑定方法讲解
2016/03/18 PHP
php is_writable判断文件是否可写实例代码
2016/10/13 PHP
使用onbeforeunload属性后的副作用
2007/03/08 Javascript
js jquery数组介绍
2012/07/15 Javascript
jquery获取div宽度的实现思路与代码
2013/01/13 Javascript
setTimeout()与setInterval()方法区别介绍
2013/12/24 Javascript
javascript弹性运动效果简单实现方法
2016/01/08 Javascript
Vue实现textarea固定输入行数与添加下划线样式的思路详解
2018/06/28 Javascript
vue的过滤器filter实例详解
2018/09/17 Javascript
vue实现弹框遮罩点击其他区域弹框关闭及v-if与v-show的区别介绍
2018/09/29 Javascript
js嵌套的数组扁平化:将多维数组变成一维数组以及push()与concat()区别的讲解
2019/01/19 Javascript
深入解析vue 源码目录及构建过程分析
2019/04/24 Javascript
MockJs结合json-server模拟后台数据
2020/08/26 Javascript
layer 刷新某个页面的实现方法
2019/09/05 Javascript
JavaScript实现点击切换验证码及校验
2021/01/10 Javascript
wxpython 最小化到托盘与欢迎图片的实现方法
2014/06/09 Python
Python下使用Psyco模块优化运行速度
2015/04/05 Python
python实现网站的模拟登录
2016/01/04 Python
Python实现TCP协议下的端口映射功能的脚本程序示例
2016/06/14 Python
详解Python 序列化Serialize 和 反序列化Deserialize
2017/08/20 Python
Python根据指定日期计算后n天,前n天是哪一天的方法
2018/05/29 Python
pandas DataFrame实现几列数据合并成为新的一列方法
2018/06/08 Python
python global关键字的用法详解
2019/09/05 Python
python画图时设置分辨率和画布大小的实现(plt.figure())
2021/01/08 Python
python matplotlib工具栏源码探析二之添加、删除内置工具项的案例
2021/02/25 Python
澳大利亚买卖正宗二手奢侈品交易平台:Luxe.It.Fwd
2019/10/16 全球购物
建筑设计师岗位职责
2013/11/18 职场文书
大学毕业感言50字
2014/02/07 职场文书
大二法学专业职业生涯规划范文
2014/02/12 职场文书
舞蹈兴趣小组活动总结
2014/07/07 职场文书
工程承包协议书范本
2014/09/29 职场文书
2014年人事专员工作总结
2014/11/19 职场文书
干货干货!2019最新优秀创业计划书
2019/03/21 职场文书
元素水平垂直居中的方式
2021/03/31 HTML / CSS