数据库缓存
介于磁盘缓存的限制,爬取到的数据量比较大,但又无任何复杂的连接,所以选用NoSQL数据库,这种数据库相比创痛的关系型数据库更容易扩展。
什么是NoSQL?
NoSQL全称Not Only SQL,与传统的关系型数据库不同,NoSQL数据库通常是无模式的,考虑了跨服务器无缝分片问题。有多种方式可以实现该目标,分别是列数据存储(HBase)、键值对存储(Redis)、面向文档的数据库(MongoDB)以及图型数据库(Neo4j)。
安装MongoDB
下载MongoDB
官网链接:https://www.mongodb.com/
解压
tar -zxvf mongodb-linux-x86_64-rhel70-4.0.1.tgz
创建目录并将解压好的目录移动到创建的目录
mv mongodb-linux-x86_64-rhel70-4.0.1 /usr/local/mongodb/
添加可执行文件到PATH路径
export PATH=/usr/local/mongodb/mongodb-linux-x86_64-rhel70-4.0.1/bin:$PATH
创建数据库目录
MongoDB的数据存储在data目录下的db目录中,/data/db是默认的启动数据库路径,需要手动创建
创建并设置路径
[root@foundation155 bin]# ./mongod -dbpath=/data/db
pip install pymongo
尝试链接
>>> from pymongo import MongoClient
>>> client=MongoClient('localhost',27017)
pymongo的基本用法
# coding:utf-8
from pymongo import MongoClient
import pymongo
#连接数据服务器,获取客户端信息
client=MongoClient('localhost',27017)
#获取myDB数据库对象
db=client.myDB
#获取对象集合
colletion=db.mycollection
tom={'name':'Tom','age':18,'sex':'男','hobbies':['吃饭','睡觉','打豆豆']}
alice={'name':'Alice','age':19,'sex':'女','hobbies':['读书','跑步','弹吉他']}
# #******************************************插入文档***********************************
#插入文档,返回一个ObjectID类型的_id属性
tom_id=colletion.insert(tom)
alice_id=colletion.insert(alice)
#也可以插入多条以列表形式
colletion.insert([tom,alice])
#返回的是InsertOneResult对象
#不过insert()已经不推荐使用了而使用insert_one()和insert_many()
print tom_id
# 5b757427c3cf5b37b403c9c8
print alice_id
# 5b757427c3cf5b37b403c9c9
#********************************查询文档*******************************
cursor=colletion.find()
#文档个数
print cursor.count()
# 4
# 查询name为Tom的文档
print colletion.find_one({'name':'Tom'})
# {u'age': 18, u'_id': ObjectId('5b7573edc3cf5b3784c43442'), u'sex':
# u'\u7537', u'name': u'Tom', u'hobbies': [u'\u5403\u996d', u'\u7761\u89c9',
# u'\u6253\u8c46\u8c46']}
#返回一个字典,不过多了id,这是在插入过程中自动添加的,也可以通过这个_id查询
from bson.objectid import ObjectId
result=colletion.find_one({'_id':ObjectId('5b757a46c3cf5b3b573fd0af')})
print result
# {u'age': 19, u'_id': ObjectId('5b757a46c3cf5b3b573fd0af'), u'sex': u'\u5973', u'name': u'Alice', u'hobbies': [u'\u8bfb\u4e66', u'\u8dd1\u6b65', u'\u5f39\u5409\u4ed6']}
#查询多条
#查询年龄为18的
results=colletion.find({'age':19})
print results
# # <pymongo.cursor.Cursor object at 0x7f79cd868e90>
# # 返回一个游标对象,相当于一个生成器
#
for result in results:
print result
# {u'age': 19, u'_id': ObjectId('5b757a46c3cf5b3b573fd0af'), u'sex': u'\u5973', u'name': u'Alice', u'hobbies': [u'\u8bfb\u4e66', u'\u8dd1\u6b65', u'\u5f39\u5409\u4ed6']}
#查询年龄大与17的
results=colletion.find({'age':{'$gt':17}})
print [result for result in results]
# [{u'age': 18, u'_id': ObjectId('5b757a46c3cf5b3b573fd0ae'), u'sex': u'\u7537', u'name': u'Tom', u'hobbies': [u'\u5403\u996d', u'\u7761\u89c9', u'\u6253\u8c46\u8c46']},
# {u'age': 19, u'_id': ObjectId('5b757a46c3cf5b3b573fd0af'), u'sex': u'\u5973', u'name': u'Alice', u'hobbies': [u'\u8bfb\u4e66', u'\u8dd1\u6b65', u'\u5f39\u5409\u4ed6']}]
#比较字符
"""
$lt小于;
$gt大于;
$lte小于等于
$gte大于等于
$ne不等于
$in在范围内{'age':{$in:[20,30]}}
$nin不再范围内
"""
#使用正则匹配
#查询名字里含有m的文档
results=colletion.find({'name':{'$regex':'m'}})
for i in results:
print i
# {u'age': 18, u'_id': ObjectId('5b757a46c3cf5b3b573fd0ae'), u'sex': u'\u7537', u'name': u'Tom', u'hobbies': [u'\u5403\u996d', u'\u7761\u89c9', u'\u6253\u8c46\u8c46']}
# 符号含义表示
"""
$regex匹配正则{'name':{'$regex':'^M.*'}}name以M开头
$exists属性是否存在{'name':{'$exists':True}name属性是否存在
$type类型判断{'age':{'$type':''int}}
$mod数字模操作{'age':{'$mod':[5,0]}}年龄模5余0
$text文本查询{'$text':{'$search':'Mike'}}text类型的属性中包含MIike字符串
$where高级条件查询{'$where':'condition'}
"""
#符合某个条件数据的条数
colletion.find().count()
#排序
#传入排序的字段及升降序的标志
results=colletion.find().sort('name',pymongo.ASCENDING)
print ([result['name'] for result in results])
#还可以指定偏移几个位置
results=colletion.find().sort('name',pymongo.ASCENDING).skip(2)
#指定获取结果的数量
results=colletion.find().sort('name',pymongo.ASCENDING).skip(2).limit(2)
#******************************************更新****************************
condition={'name':'Tom'}
student=colletion.find_one(condition)
student['age']=20
result=colletion.update(condition,student)
print result
# {'updatedExisting': True, u'nModified': 1, u'ok': 1.0, u'n': 1}
# ok代表执行成功,nModified代表影响数据的条数
condition={'name':'Alice'}
student=colletion.find_one(condition)
student['age']=26
result=colletion.update_one(condition,{'$set':student})
print result
# <pymongo.results.UpdateResult object at 0x7fcc5af68098>
print (result.matched_count,result.modified_count)
# 匹配的条数,影响的条数
# (1, 0)
condition={'age':{'$gt':20}}
#匹配条件年龄大于20,更新后给所有数据的年龄加1
results=colletion.update_many(condition,{'$inc':{'age':1}})
print result
print (result.matched_count,result.modified_count)
# <pymongo.results.UpdateResult object at 0x7f24204dc0e0>
# (1, 1)
# *******************************************删除数据********************
result=colletion.remove({'name':'Tom'})
print result
# {u'ok': 1.0, u'n': 1}
#ok表示删除成功,n代表影响条数
#也支持delete_one()和delete_many()
result = colletion.delete_one({'name': 'Alice'})
print(result)
print(result.deleted_count)
result = colletion.delete_many({'age': {'$lt': 25}})
print(result.deleted_count)
实现
# coding:utf-8
import pickle
from datetime import timedelta, datetime
import zlib
from bson.binary import Binary
from pymongo import MongoClient
from Link_crawler import link_crawler
class MongoCache:
def __init__(self,client=None,expires=timedelta(days=30)):
"""
client:数据库客户端
expires:文件有效期
"""
self.client=MongoClient('localhost',27017) if client is None else client
#获取数据库对象
self.db=self.client.cache
#获取集合文档
self.db.webpage.create_index('timestamp',expireAfterSeconds=expires.total_seconds())
def __contains__(self, url):
try:
self[url]
except KeyError:
return False
else:
return True
def __getitem__(self, url):
"""加载这个URL的数据"""
record=self.db.webpage.find_one({'_id':url})
if record:
#pickle.loads将获取到的数据存入内存,以便访问
#zlib.decompress解压数据
return pickle.loads(zlib.decompress(record['result']))
else:
raise KeyError(url+'does not exist')
def __setitem__(self,url,result):
"""将数据存入数据库"""
# Binary转换为二进制形式
# pickle.dumps从内存重读取
# zlib.compress压缩数据
#将数据和目前的时间创建一个字典
record={'result':Binary(zlib.compress(pickle.dumps(result))),'timestamp':datetime.utcnow()}
# 将数据更新到数据库
self.db.webpage.update({'_id':url},{'$set':record},upsert=True)
def clear(self):
self.db.webpage.drop()
if __name__=='__main__':
import time
start=time.time()
link_crawler('http://example.webscraping.com/', '/view', cache=MongoClient())
print "runing:%s" %(time.time()-start)