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通过ssh-powershell监控windows的方法
Jun 02 Python
Python2中的raw_input() 与 input()
Jun 12 Python
python删除列表内容
Aug 04 Python
Request的中断和ErrorHandler实例解析
Feb 12 Python
Python模拟简单电梯调度算法示例
Aug 20 Python
pandas条件组合筛选和按范围筛选的示例代码
Aug 26 Python
Django中modelform组件实例用法总结
Feb 10 Python
Python递归调用实现数字累加的代码
Feb 25 Python
150行python代码实现贪吃蛇游戏
Apr 24 Python
tensorflow转换ckpt为savermodel模型的实现
May 25 Python
python GUI模拟实现计算器
Jun 22 Python
在 Python 中利用 Pool 进行多线程
Apr 24 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 session有效期问题
2009/04/26 PHP
PHP 9 大缓存技术总结
2015/09/17 PHP
PHP开发APP端微信支付功能
2017/02/17 PHP
Add Formatted Data to a Spreadsheet
2007/06/12 Javascript
jQuery jqgrid 对含特殊字符json 数据的 Java 处理方法
2011/01/01 Javascript
类似天猫商品详情随浏览器移动的示例代码
2014/02/27 Javascript
百度判断手机终端并自动跳转js代码及使用实例
2014/06/11 Javascript
node.js中的buffer.toString方法使用说明
2014/12/14 Javascript
PHP实现的各种中文编码转换类分享
2015/01/23 Javascript
AngularJS基础 ng-cut 指令介绍及简单示例
2016/08/01 Javascript
让浏览器崩溃的12行JS代码(DoS攻击分析及防御)
2016/10/10 Javascript
javascript滚轮控制模拟滚动条
2016/10/19 Javascript
详解基于vue-cli优化的webpack配置
2017/11/06 Javascript
JS实现留言板功能[楼层效果展示]
2017/12/27 Javascript
浅谈Vuejs中nextTick()异步更新队列源码解析
2017/12/31 Javascript
vue移动端UI框架实现QQ侧边菜单组件
2018/03/09 Javascript
vue使用jsonp抓取qq音乐数据的方法
2018/06/21 Javascript
通过vue手动封装on、emit、off的代码详解
2019/05/29 Javascript
vue项目中将element-ui table表格写成组件的实现代码
2019/06/12 Javascript
JS查找孩子节点简单示例
2019/07/25 Javascript
ES5和ES6中类的区别总结
2020/12/21 Javascript
python 连接sqlite及简单操作
2017/06/30 Python
浅析PHP与Python进行数据交互
2018/05/15 Python
django从请求到响应的过程深入讲解
2018/08/01 Python
Python实现SQL注入检测插件实例代码
2019/02/02 Python
详解pandas的外部数据导入与常用方法
2019/05/01 Python
Python日志syslog使用原理详解
2020/02/18 Python
python爬虫可以爬什么
2020/06/16 Python
分享29个基于Bootstrap的HTML5响应式网页设计模板
2015/11/19 HTML / CSS
纽约21世纪百货官网:Century 21
2016/08/27 全球购物
荷叶圆圆教学反思
2014/02/01 职场文书
五年后的职业生涯规划
2014/03/04 职场文书
幼儿园六一主持词开场白
2015/05/28 职场文书
心灵捕手观后感
2015/06/02 职场文书
电影红河谷观后感
2015/06/11 职场文书
Android Studio实现简易进制转换计算器
2022/05/20 Java/Android