【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】

1 背景

不知不觉关于 Python 3.X 爬虫系列已经介绍了如下系列:

《正则表达式基础》

《Python3.X 爬虫实战(先爬起来嗨)》

《Python3.X 爬虫实战(静态下载器与解析器)》

《Python3.X 爬虫实战(并发爬取)》

可以看到,关于 Python 静态页面爬虫的相关核心基础其实已经介绍的差不多了,关于爬虫的 URL 管理器、下载器、解析器、输出器、并发爬取思想我们已经基本介绍了,但是到这里我们要学会思考一个棘手的问题——–缓存与持久化。简单说就是 Cache 或者 Persistence 了,这玩意和爬虫有啥关系呢?想象一下如果我们需要对同一个页面进行多次解析,我们前面的代码都会重新发起真实网络请求,这是不合理的,因为短期之内这个页面是不可能有更新的,我们重复拉取是没有意义的;其次我们很多时候爬虫的输出器其实就是需要把爬取的数据依据需求多元化的持久化下来,所以我们有必要先掌握常见的爬虫相关缓存及持久化。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】

2 Python3.X 缓存与持久化

这里之所以把爬虫的常见缓存与持久化放在一起讨论是因为爬虫这里的缓存与持久化相对来说是比较相似的,缓存算是持久化的一个子集,但是缓存又有自己的过期策略和缓存级别,而持久化基本无过期策略之说。缓存与持久化并不是 Python 爬虫特有的,其他语言都有涉及,所以我们下面既然说要把缓存和持久化放在一起说是建立在持久化缓存的基础上,因为多级缓存策略的内存缓存等不在我们这篇的讨论范畴,大家一定要自己明确区分,简单理解就是我们本篇倾向于持久化,只是谈到的一些持久化方式在缓存中可用而已。

2-1 常见本地磁盘文件型

Python3.X 常见本地磁盘文件型数据持久化主要包括普通文件、DBM文件、Pickle序列化对象存储、shelve键值序列化对象存储,对于我们编写爬虫程序来说缓存的设计或者持久化方式我们可以自己依据自己的需求进行合适的评估选择,下面给出常见的本地磁盘文件型持久化样例:

[本实例 demo_local_disk_file_persistence.py 源码点我获取]

import dbm

import pickle

import shelve

'''

Python3 常用本地磁盘文件型持久化演示

'''

class NormalFilePersistence(object):

'''

普通文件持久化或者缓存持久化

'''

def save(self, data):

with open('NormalFilePersistence.txt', 'w') as open_file:

open_file.write(data)

def load(self):

with open('NormalFilePersistence.txt', 'r') as open_file:

return open_file.read()

class DBMPersistence(object):

'''

DBM字符串键值对持久化或者缓存持久化

'''

def save(self, key, value):

try:

dbm_file = dbm.open('DBMPersistence', 'c')

dbm_file[key] = str(value)

finally:

dbm_file.close()

def load(self, key):

try:

dbm_file = dbm.open('DBMPersistence', 'r')

if key in dbm_file:

result = dbm_file[key]

else:

result = None

finally:

dbm_file.close()

return result

class PicklePersistence(object):

'''

Pickle把复杂对象序列化到文件持久化或者缓存持久化

'''

def save(self, obj):

with open('PicklePersistence', 'wb') as pickle_file:

pickle.dump(obj, pickle_file)

def load(self):

with open('PicklePersistence', 'rb') as pickle_file:

return pickle.load(pickle_file)

class ShelvePersistence(object):

'''

Shelve为DBM和Pickle的结合,以键值对的方式把复杂对象序列化到文件持久化或者缓存持久化

'''

def save(self, key, obj):

try:

shelve_file = shelve.open('ShelvePersistence')

shelve_file[key] = obj

finally:

shelve_file.close()

def load(self, key):

try:

shelve_file = shelve.open('ShelvePersistence')

if key in shelve_file:

result = shelve_file[key]

else:

result = None

finally:

shelve_file.close()

return result

if __name__ == '__main__':

t_normal = NormalFilePersistence()

t_normal.save('Test NormalFilePersistence')

print('NormalFilePersistence load: ' + t_normal.load())

t_dbm = DBMPersistence()

t_dbm.save('user', 'GJRS')

t_dbm.save('age', 27)

print('DBMPersistence load: ' + str(t_dbm.load('user')))

print('DBMPersistence load: ' + str(t_dbm.load('address')))

t_pickle = PicklePersistence()

obj = {'name': 'GJRS', 'age': 27, 'skills':['Android', 'C', 'Python', 'Web']}

t_pickle.save(obj)

print('PicklePersistence load: ' + str(t_pickle.load()))

t_shelve = ShelvePersistence()

obj1 = {'name': 'WL', 'age': 27, 'skills': ['Test', 'AutoTest']}

obj2 = {'name': 'GJRS', 'age': 27, 'skills': ['Android', 'C', 'Python', 'Web']}

t_shelve.save('obj1', obj1)

t_shelve.save('obj2', obj2)

print('ShelvePersistence load: ' + str(t_shelve.load('obj1')))

print('ShelvePersistence load: ' + str(t_shelve.load('objn')))

关于这些方式的持久化爬虫实例我们在该系列文章的前几篇都有介绍,这里不再给出单独的爬虫实例,感兴趣可以自己摸索,没啥复杂的,主要是策略的设计,譬如 LRU 算法等,真正持久化其实是非常简单的,但也是非常重要的。

2-2 常见数据库方式

上面介绍了常见本地磁盘文件型的持久化,我们学习完一定会有疑惑,如果我的数据量巨大巨复杂怎么办,如果还是使用本地磁盘文件型的持久化那得多蛋疼啊,是的,所以我们现在来讨论关于 Python 爬虫的另一类缓存持久化方式 —— 数据库持久化。

2-2-1 Sqlite 持久化

首先我们要看的就是 Python3.X 中 SQLite3 的使用(单机型),从 Python2.5 开始的版本就默认自带了该模块,所以我们不用重新安装。下面给出 Python3.X 中 SQLite3 的使用例子:

[本实例 demo_sqlite3_persistence.py 源码点我获取]

'''

Python3 sqlite3数据库持久化演示

'''

import sqlite3

class Sqlite3Persistence(object):

def __init__(self):

self.db = None

def connect(self):

try:

self.db = sqlite3.connect("Sqlite3Persistence.db")

sql_create_table = """CREATE TABLE IF NOT EXISTS `DemoTable` (

`id` INTEGER PRIMARY KEY AUTOINCREMENT,

`name` CHAR(512) NOT NULL,

`content` TEXT NOT NULL)"""

self.db.execute(sql_create_table)

except Exception as e:

print("sqlite3 connect failed." + str(e))

def close(self):

try:

if self.db is not None:

self.db.close()

except BaseException as e:

print("sqlite3 close failed."+str(e))

def insert_table_dict(self, dict_data=None):

if dict_data is None:

return False

try:

cols = ', '.join(dict_data.keys())

values = '"," '.join(dict_data.values())

sql_insert = "INSERT INTO `DemoTable`(%s) VALUES (%s)" % (cols, '"'+values+'"')

self.db.execute(sql_insert)

self.db.commit()

except BaseException as e:

self.db.rollback()

print("sqlite3 insert error." + str(e))

return True

def get_dict_by_name(self, name=None):

if name is None:

sql_select_table = "SELECT * FROM `DemoTable`"

else:

sql_select_table = "SELECT * FROM `DemoTable` WHERE name==%s" % ('"'+name+'"')

cursor = self.db.execute(sql_select_table)

ret_list = list()

for row in cursor:

ret_list.append({'id': row[0], 'name': row[1], 'content': row[2]})

return ret_list

if __name__ == '__main__':

t_sqlite3 = Sqlite3Persistence()

t_sqlite3.connect()

t_sqlite3.insert_table_dict({'name': 'Test1', 'content': 'XXXXXXXXXXXXX'})

t_sqlite3.insert_table_dict({'name': 'Test2', 'content': 'vvvvvvvvvvvv'})

t_sqlite3.insert_table_dict({'name': 'Test3', 'content': 'qqqqqqqqqqqq'})

t_sqlite3.insert_table_dict({'name': 'Test4', 'content': 'wwwwwwwwwwwww'})

print('Sqlite3Persistence get Test2: ' + str(t_sqlite3.get_dict_by_name('Test2')))

print('Sqlite3Persistence get All: ' + str(t_sqlite3.get_dict_by_name()))

t_sqlite3.close()

当然了,至于你的爬虫中是否选择 Sqlite3 进行持久化就要看你自己的需求了,毕竟关系型数据库有其自己的优劣;如果你需要再更加多的了解 Sqlite,那可以看下以前我写的《Sqlite全面学习(一)》、《Sqlite全面学习(二)》、《Sqlite全面学习(三)》这几篇文章,你会发现,其实新学任何一门语言需要关注的是语言本身的语法和性能实现等,而无需关注公共的东西,因为公共的东西都是相同的,所以这也是为啥我们学应用型技术越学越快的原因。

2-2-2 MySQL 持久化

不扯了,介绍完单极型高效迷你关系型数据库 Sqlite 的持久化后我们再来看看大型服务器关系型数据库 MySQL,在 Python 3.X 中使用 MySQL 需要依赖 pymysql 模块,当然咯,你还得有台安装 MySQL 的数据库服务器和可视化管理工具,怎么安装就不多说了,如下给出一个使用案例(我的 MySQL用的是本地安装的):

[本实例 demo_mysql_persistence.py 源码点我获取]

'''

Python3 MySQL数据库持久化演示

'''

import pymysql

class MySQLPersistence(object):

def __init__(self):

self.db = None

self.cursor = None

def connect(self):

try:

self.db = pymysql.connect("localhost", "yanbober", "TQJJtaJWNbGAMU44", "database_yan_php")

self.db.set_charset('utf8')

self.cursor = self.db.cursor()

sql_create_table = """CREATE TABLE IF NOT EXISTS `StudentTable` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(512) COLLATE utf8_bin NOT NULL,

`content` TEXT COLLATE utf8_bin NOT NULL,

PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

AUTO_INCREMENT=1"""

self.cursor.execute(sql_create_table)

except Exception as e:

print("mysql connect failed." + str(e))

def close(self):

try:

if self.db is not None:

self.db.close()

if self.cursor is not None:

self.cursor.close()

except BaseException as e:

print("mysql close failed."+str(e))

def insert_table_dict(self, dict_data=None):

if self.db is None or self.cursor is None:

print('Please ensure you have connected to mysql server!')

return False

if dict_data is None:

return False

try:

cols = ', '.join(dict_data.keys())

values = '"," '.join(dict_data.values())

sql_insert = "INSERT INTO `StudentTable`(%s) VALUES (%s)" % (cols, '"'+values+'"')

self.cursor.execute(sql_insert)

self.db.commit()

except BaseException as e:

self.db.rollback()

print("mysql insert error." + str(e))

return True

def get_dict_by_name(self, name=None):

if self.db is None or self.cursor is None:

print('Please ensure you have connected to mysql server!')

return None

if name is None:

sql_select_table = "SELECT * FROM `StudentTable`"

else:

sql_select_table = "SELECT * FROM `StudentTable` WHERE name=%s" % ('"'+name+'"')

self.cursor.execute(sql_select_table)

ret_list = list()

for item in self.cursor.fetchall():

ret_list.append({'id': item[0], 'name': item[1], 'content': item[2]})

return ret_list

if __name__ == '__main__':

t_mysql = MySQLPersistence()

t_mysql.connect()

t_mysql.insert_table_dict({'name': 'Test1', 'content': 'XXXXXXXXXXXXX'})

t_mysql.insert_table_dict({'name': 'Test2', 'content': 'vvvvvvvvvvvv'})

t_mysql.insert_table_dict({'name': 'Test3', 'content': 'qqqqqqqqqqqq'})

t_mysql.insert_table_dict({'name': 'Test4', 'content': 'wwwwwwwwwwwww'})

print('MySQLPersistence get Test2: ' + str(t_mysql.get_dict_by_name('Test2')))

print('MySQLPersistence get All: ' + str(t_mysql.get_dict_by_name()))

t_mysql.close()

可以看见,MySQL 关系型数据库使用起来和 Sqlite 很相似,其实是这样的,他们本来都来自 SQL 家族,只是各自有一些细微的区别而已;通过上面代码我们就将我们的数据持久化到了 localhost 这台数据库服务器上面,使用数据时直接从这台服务器获取即可,很是方便。

2-2-3 MongoDB 持久化

上面我们主要介绍了 python3.X 中关系型数据库 mysql、sqlite 的使用,下面我们继续介绍 Python3.X 爬虫中常用的非关系型数据库,先要介绍的是 MongoDB,它是一个基于分布式文件存储的数据库,是为 WEB 应用提供可扩展的高性能数据存储而诞生的,是一个介于关系数据库和非关系数据库之间的东西,也是非关系数据库中功能最丰富、最像关系数据库的数据库。关于 MongoDB 数据库和可视化管理工具的安装配置这里就不介绍了,具体用法实例如下(我的 MongoDB 是本地的,运行下面代码前请下保证已经启动 MongoDB,譬如mongod.exe --dbpath D:\developer\MongoDB\Server\data\db):

[本实例 demo_mongodb_persistence.py 源码点我获取]

import pymongo

'''

Python3 MongoDB数据库持久化演示

'''

class MongoDBPersistence(object):

def __init__(self):

self.conn = None

self.database = None

def connect(self, database):

try:

self.conn = pymongo.MongoClient('mongodb://localhost:27017/')

self.database = self.conn[database]

except Exception as e:

print("MongoDB connect failed." + str(e))

def close(self):

try:

if self.conn is not None:

self.conn.close()

except BaseException as e:

print("MongoDB close failed."+str(e))

def insert_table_dict(self, dict_data=None):

if self.conn is None or self.database is None:

print('Please ensure you have connected to MongoDB server!')

return False

if dict_data is None:

return False

try:

collection = self.database['DemoTable']

collection.save(dict_data)

except BaseException as e:

print("MongoDB insert error." + str(e))

return True

def get_dict_by_name(self, name=None):

if self.conn is None or self.database is None:

print('Please ensure you have connected to MongoDB server!')

return None

collection = self.database['DemoTable']

if name is None:

documents = collection.find()

else:

documents = collection.find({"name": name})

document_list = list()

for document in documents:

document_list.append(document)

return document_list

if __name__ == '__main__':

t_mysql = MongoDBPersistence()

t_mysql.connect("DemoDatabase")

t_mysql.insert_table_dict({'name': 'Test1', 'content': 'XXXXXXXXXXXXX'})

t_mysql.insert_table_dict({'name': 'Test2', 'content': 'vvvvvvvvvvvv'})

t_mysql.insert_table_dict({'name': 'Test3', 'content': 'qqqqqqqqqqqq'})

t_mysql.insert_table_dict({'name': 'Test4', 'content': 'wwwwwwwwwwwww'})

print('MongoDBPersistence get Test2: ' + str(t_mysql.get_dict_by_name('Test2')))

print('MongoDBPersistence get All: ' + str(t_mysql.get_dict_by_name()))

t_mysql.close()

就这样咯,关于 Python 爬虫持久化的 MongoDB 存储基本套路就是这样的,其他类似 SQL 的增删查找等策略语法一样需要我们自己去积累,这里不可能全部说明,不懂的话一样类似 SQL 去先多看看用用 MongoDB,然后对于各种语言下的使用基本区别就是 Driver API 了,原理不变。

2-2-4 其他持久化

之所以叫其他,其实真的有太多的持久化方式,除过上面我们介绍的磁盘文件、常见数据库持久化以外我们其实还有其他的选择,譬如我们对于 NoSQL 的选择除过上面介绍的文档数据存储 MongoDB 以外还可以选择列数据存储 (HBase)、键值对存储(Redis)、图形数据库(Neo4j)等,这就取决于我们爬虫的数据需求了;其次我们还可以选择一些 ORM 框架来进行持久化,譬如 Django 或者 SQLAlchemy,这就取决于我们自己咯。

【工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请尊重作者劳动成果。私信联系我】

3 爬虫持久化选型及总结

Tips:缓存持久化前我们可以对缓存比较大的文本数据先进行压缩等处理再存储,这样可以节约存储。

通过上面常见的 Python3.X 各种持久化方式介绍我们至少应该知道在爬虫需要缓存持久化时我们可以有很多种选择,至于如上所有持久化如何选型其实是依赖于我们自己爬虫需求来决定的,不同的需求可能需要用不同的持久化类型,不过还是有一些参考策略来指导我们进行爬虫持久化选型的,即我们需要认清上面那些持久化各自的优劣点。

对于本地文件型持久化其实优劣点是很明显的,譬如上面介绍的有些支持序列化存储,有些支持同一文件下多 key-value 对存储,但是数据规模一旦庞大,本地文件存储不仅效率低下,还容易出现数据故障,备份十分麻烦,总之只适用于轻量级本地单一数据格式存储,也就是比较适合我们自己编写的一些小爬虫程序。

对于 Sqlite 数据库存储来说基本上只能认为是本地文件型存储的一个关系型升级,有效的改善了本地磁盘文件存储关系型数据的诟病,但是因为其为单机型迷你数据库,在数据存储量级和数据故障方面也是有瓶颈限制的,至于在本地文件型存储和 Sqlite 的选型时我觉得重点要衡量爬虫有用数据的关系,日后数据间关联紧密,需要互相依赖查找的情况使用 Sqlite 似乎更胜一筹。

对于 MySQL 等关系型数据库存储和 MongoDB 等非关系型数据库存储的优劣比较其实在网上已经有很多文章谈论多年了,不过在爬虫时到底如何选择其实还是取决于我们自己的需求定位,对于关系型数据库存储其具备高结构化数据、结构化查询语言、数据和关系都存储在单独的表中,而对于非关系型数据库存储其具备高可用、高性能、高伸缩性、没有声明性查询语言、使用键值对、列、文档、图形等存储、存储数据不可预知及无结构化可言。我们很多时候的爬虫需求都是爬取某一垂直需求下的海量数据来进行建模数据分析的,对于这种情况其实更加适合使用 MongoDB 来进行爬虫数据存储;而又有些时候我们爬虫数据可能具备高度的结构化封装和关联,我们想将爬取数据用来提供给其他平台进行 API 接口访问,在这种情况下似乎使用 MySQL 是一个不错的选择。

总之,Python3.X 爬虫缓存与持久化选型是需要依据我们需求来决定的,甚至有些情况下可能会出现多种持久化组合使用的情况,我们需要做到的是掌握和知道爬虫持久化可以有哪些选择,只有这样才能不变应万变。

^-^当然咯,看到这如果发现对您有帮助的话不妨扫描二维码赏点买羽毛球的小钱(现在球也挺贵的),既是一种鼓励也是一种分享,谢谢!

python3爬虫实战姚良_Python3.X 爬虫实战(缓存与持久化)相关推荐

  1. python爬虫手机app数据库_Python3网络爬虫(十三):王者荣耀那些事!(Fiddler之手机APP爬取)...

    运行平台: Windows Python版本: Python3.x IDE: Sublime text3 1 前言 暑假回家,"小皇帝"般的生活持续了几天,头几天还挺舒服,闲久了顿 ...

  2. Python3 大型网络爬虫实战 004 — scrapy 大型静态商城网站爬虫项目编写及数据写入数据库实战 — 实战:爬取淘宝

    原博文链接:http://www.aobosir.com/blog/2016/12/26/python3-large-web-crawler-taobao-com-import-to-MySQL-da ...

  3. python3 scrapy爬虫_Python3 Scrapy爬虫框架(Scrapy/scrapy-redis)

    Python3 Scrapy爬虫框架(Scrapy/scrapy-redis) 本文由 Luzhuo 编写,转发请保留该信息. 原文: https://blog..net/Rozol/article/ ...

  4. python爬取cctalk视频_python爬虫urllib使用和进阶 | Python爬虫实战二

    python爬虫urllib使用和进阶 上节课已经介绍了爬虫的基本概念和基础内容,接下来就要开始内容的爬取了. 其实爬虫就是浏览器,只不过它是一个特殊的浏览器.爬取网页就是通过HTTP协议访问相应的网 ...

  5. 爬虫python程序实例-《Python爬虫开发与项目实战》pdf完整版

    [实例简介] [实例截图] [核心代码] 目录 前言 基础篇 第1章 回顾Python编程2 1.1 安装Python2 1.1.1 Windows上安装Python2 1.1.2 Ubuntu上的P ...

  6. python爬虫项目实战教学视频_('[Python爬虫]---Python爬虫进阶项目实战视频',)

    爬虫]---Python 爬虫进阶项目实战 1- Python3+Pip环境配置 2- MongoDB环境配置 3- Redis环境配置 4- 4-MySQL的安装 5- 5-Python多版本共存配 ...

  7. 《精通Python网络爬虫:核心技术、框架与项目实战》——1.3 网络爬虫的组成...

    本节书摘来自华章出版社<精通Python网络爬虫:核心技术.框架与项目实战>一书中的第1章,第1.3节,作者 韦 玮,更多章节内容可以访问云栖社区"华章计算机"公众号查 ...

  8. 精通python网络爬虫-精通Python网络爬虫:核心技术、框架与项目实战

    -- 目录 -- 前言 第一篇 理论基础篇 第1章 什么是网络爬虫 1.1 初识网络爬虫 1.2 为什么要学网络爬虫 1.3 网络爬虫的组成 1.4 网络爬虫的类型 1.5 爬虫扩展--聚焦爬虫 1. ...

  9. 学习推荐《精通Python网络爬虫:核心技术、框架与项目实战》中文PDF+源代码

    随着大数据时代的到来,我们经常需要在海量数据的互联网环境中搜集一些特定的数据并对其进行分析,我们可以使用网络爬虫对这些特定的数据进行爬取,并对一些无关的数据进行过滤,将目标数据筛选出来.对特定的数据进 ...

最新文章

  1. import cycle not allowed_Cycle药物介绍醋酸群勃龙(2)
  2. Python filter() 函数
  3. HTTPS 的工作原理
  4. EOS 智能合约源代码解读 (3)asset.hpp
  5. php读取txt插入数据库,PHP读取TXT文件插入数据库
  6. linux看电视系统,教你如何在Linux操作系统下观看电视节目
  7. 使用内置的Gallery应用程序选择图形
  8. 算法设计与分析-实验2
  9. Linux第五章自测习题——Linux系列学习笔记
  10. RealityCapture照片建模
  11. chrome 安装 gliffy插件以及使用
  12. wx2540h配置教程_AC WX2540H 操作配置 本人小白
  13. 热血江湖游戏中断开服务器,为什么最近老是一进去游戏就提示与服务器断开 – 手机爱问...
  14. java-画出二维码
  15. mybatis-plus自动填充(创建时间\修改时间)
  16. Unity Shader graph 毒液
  17. 迁移学习在医学影像学中的应用
  18. oracle乘法运算,乘法运算
  19. 一文看懂:NAS网络存储与SAN、DAS的区别
  20. 多线程threading模块用法 -《狗嗨默示录》-

热门文章

  1. 全解Google(谷歌)基础设施架构安全设计
  2. 基于深度学习的图像识别进展:百度的若干实践
  3. 支持Micro USB安卓接口与iphone 8手机的5W无线充电芯片|无线快充芯片小封装SOP8外围简单精简
  4. ​DB-Engines 11月数据库排名:PostgreSQL坐稳同期涨幅榜冠军宝座
  5. vivox27微信无法连接服务器,vivo X27微信拍照模糊怎么办?简单一个操作轻松解决...
  6. 广义表的链式定义和基础操作
  7. 用java求1000之内的素数_java求1000以内的素数
  8. 微信公众平台开发实战
  9. 图形学篇:多边形有效边表填充算法
  10. 大数据有哪些存储方式?