Python3编写网络爬虫12-数据存储方式五-非关系型数据库存储

非关系型数据库存储

NoSQL 全称 Not Only SQL 意为非SQL 泛指非关系型数据库。
基于键值对 不需要经过SQL层解析 数据之间没有耦合性 性能非常高。

非关系型数据库可细分如下:

键值存储数据库: 代表有Redis、Voldemort、和Oracle BDB等。
列存储数据库:代表有Cassandra、HBase、和Riak等。
文档型数据库:代表有CouchDB、Mongodb等。
图形数据库:代表有Neo4J、InfoGrid、Infinite、Graph等。

 

对于爬虫的数据存储来说,一条数据可能存在某些字段提取失败而缺失的情况,而且数据可能随时调整。
如果使用关系型数据库 需要提前建表 如果存在数据嵌套关系 需要序列化操作才可以存储。
如果使用非关系型数据库就可以避免一些麻烦,更简单高效。

1.MongoDB存储

简介:MongoDB 是由c++ 语言编写的非关系型数据库 是一个基于分布式文件存储的开源数据库系统,
其内容存储形式类似JSON对象,字段值可以包含文档、数组、非常灵活。

安装 : 安装MongoDB 并确保已经运行

官网 https:www.mongodb.com 下载安装

windows : https://www.mongodb.com/download-center/community

安装后 进入MongoDB安装目录 在bin目录下新建同级目录data 进入data 新建子文件夹db 用于存储数据目录

打开命令行 进入bin目录 运行MongoDB服务 : mongod --dbpath "安装路径\data\db"

输出信息 证明启动了mongodb服务(缺点不能关闭命令行)

想办法设置成可以管理的

1.在bin目录下新建同级目录logs 进入目录新建文件mongodb.log 用于保存mongodb日志
(如不过能在目录新建可以在外新建复制进去)

2.打开管理员运行命令行 输入如下内容:
mongod --bind_ip 0.0.0.0 --logpath "mongodb.log文件路径+文件名" --logappend --dbpath "db目录路径" --port 27017 --serviceName "MongoDB" --serviceDisplayName "MongoDB" --install

(含义:绑定IP为任意ip均可访问 指定日志路径,数据库路径和端口 指定服务名) 无报错信息表示服务安装成功。
3. 打开计算机管理 -> 服务 找到MongoDB 启动服务。
4. 添加环境变量 mongo.exe
5. 打开命令行 输入mongo 进入交互模式。

安装: python的PyMongo库

pip install pymongo

 

验证:

import pymongo
pymongo.version

输出版本信息
证明安装成功


2.连接MongoDB

示例:

import pymongo
client = pymongo.MongoClient(host='localhost',port=27017)#创建mongodb对象

#也可以写成

client = MongoClient('mongodb://localhost:27017/')

 

3.指定数据库

db = client.test

#也可以写成 
db = client['test']

 

4.指定集合

collection = db.students
collection = db['students']

 

5.插入数据

student = {
  'id':'20180001',
  'name':'Bod',
  'age':20,
  'gender':'male'
}

result = collection.insert(student)
print(result)#返回_id值

 

在mongodb中 每一条数据都会有一个_id属性来唯一标识 如果没有会自动生成一个Object类型的_id属性。
insert() 方法会在执行后返回_id值

插入多条数据

student1 = {
  'id':'20180001',
  'name':'Bod',
  'age':20,
  'gender':'male'
}
student2 = {
  'id':'20180002',
  'name':'Mike',
  'age':22,
  'gender':'male'
}

result = collection.insert([student1,student2])
print(result)#返回对应的_id集合列表

 

实际上 在3.X版本中 官方不推荐使用了 但是使用也没什么问题

官方推荐 insert_one() 和 insert_many() 区分插入单条数据和多条数据

示例:

result = collection.insert_one(student)
print(result)#返回InsertOneResult对象
print(result.inserted_id)#利用inserted_id 返回_id

result = collection.insert_many([student1,student2])
print(result)#返回InsertManyResult对象
print(result.inserted_ids)#利用inserted_ids 返回_id列表

 

6.查询数据

find_one() 返回单个结果 查询不到返回None
find() 返回一个生成器对象

示例:

result = collection.find_one({'name':'Mike'})
print(type(result))#dict
print(result)

 

拓展:
也可以根据ObjectId 来查询 需要使用bson库 objectid

from bson import ObjectId

result = collection.find_one({'_id':ObjectId('5c126cdf11bede10f06c6184')})
print(result)

 

示例:

results = collection.find({'age':20})
print(results)#结果为cursor类型 相当于生成器
for result in results:
print(result)

 

如果要查询年龄大于20

示例:

results = collection.find({'age':{'$gt':20}})

 

比较符号

$lt 大于    {'age':{'$lt':20}}
$gt 小于    ..
$lte    小于等于    ..
$gte 大于等于    ..
$ne 不等于 ..
$in 在范围内    {'age':{'$in':[20,25]}}
$nin 不再范围    {'age':{'$nin':[20,25]}}

 

也可以用正则匹配查询

例如查询名字以M开头的学生数据

results = collection.find({'name':{'$regex':'^M.*'}})

 

功能符号归类

符号 含义 示例 示例含义

$regex    匹配正则    {'name':{'$regex':'^M.*'}}    name以M开头

$exists 属性是否存在    {'name':{'$exists':True}}    name属性存在

$type 类型判断    {'name':{'$type':'int'}}    age的类型为int

$mod 数字模操作    {'name':{'$mod':[5,0]}}    年龄模5余0

$text 文本查询    {'$text':{'$search':'Mike'}}    text类型的属性中包含Mike字符串

$where 高级条件查询    {'$where':'obj.fans_count == obj.follows_count'} 自身粉丝数等于关注数

 

更多 https://docs.mongodb.com/manual/reference/operator/query/

7.计数

count() 统计查询结果有多少条数据

示例: 查询所有数据条数

count = collection.find().count()
print(count)

 

异或 加条件

8.排序

sort() 传入排序的字段 及 升降序标志

示例:

results = collection.find().sort('name',pymongo.ASCENDING)#升序 降序可以使用DESCENDING
# print(results)
print([result['name'] for result in results])

 

9.偏移

有时我们可能只想获取某几个元素 可以利用 skip() 方法偏移几个位置

示例:偏移2 忽略前两个元素,得到第三个及以后的元素

results = collection.find().sort('name',pymongo.ASCENDING).skip(2)
print([result['name'] for result in results])

 

另外也可以使用 limit() 方法指定要取得结果个数

示例:

results = collection.find().sort('name',pymongo.ASCENDING).skip(2).limit(2)
print([result['name'] for result in results])

 

注意:在数据库数据量非常庞大的时候 如千万、亿级别 最好不要使用大的偏移量来查询数据 很可能会导致内存溢出

此时可以使用类似如下操作来查询

from bson.objectid import ObjectId
collection.find({'_id':{'$gt':ObjectId('5c126cdf11bede10f06c6184')}})

 

10.更新

update() 方法 指定更新的条件和更新后的数据

示例:

condition = {'name':'Mike'}
student = collection.find_one(condition)
sutdent['age'] = 25
result = collection.update(condition,student)
print(result)#ok 执行成功 nModified 影响数据条数

 

另外也可以使用$set 操作符对数据进行更新

result = collection.update(condition,{'$set':student})

 

官方推荐 update_one he update_many

result = collection.update_one(condition,{'$set':student})
print(result.matched_count,result.modified_count)#获得匹配数据条数和影响数据条数

 

11.删除

remove() 指定删除条件 符合条件的所有数据都会被删除

示例:

result = collection.remove({'name':'Bod'})
print(result)

 

官方推荐:

delete_one() 删除一条符合条件的数据 delete_many() 删除所有符合条件的数据

示例:

result = collection.delete_one({'name':'Mike'})
print(result)#DeleteResult类型
print(result.deleted_cout)#删除条数
results = collection.delete_many({'age':20})
print(result)
print(results.deleted_cout)#删除条数

 

12.其他操作

PyMongo 还提供了其他组合方法如:

find_one_and_delete() 查找后删除
find_one_and_replace() 查找后替换
find_one_and_update() 查找后更新 用法与上面方法基本一致

详细用法参考官方文档 此处不再赘述

http://api.mongodb.com/python/current/api/pymongo/collection.html

数据库与集合操作

http://api.mongodb.com/python/current/api/pymongo/

 

2.Redis 存储

简介:Redis 是一个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构,使用也非常简单。

安装 Redis 地址:https://github.com/MicrosoftArchive/redis/releases

完成后到计算机管理 -> 服务 查看 redis服务

另外也可以下载可视化管理工具 地址官网:https://redisdesktop.com/download 或者github:https://github.com/uglide/RedisDesktopManager/releases

安装redis-py

 pip install redis

 

验证:

import redis
redis.VERSION #输出版本信息 表示安装成功

 

如果要做数据导入导出 还需要安装RedisDump

RedisDump是一个用于redis数据导入导出的工具 是基于Ruby实现的 所以要安装redisdump 要先安装Ruby

Ruby 安装地址参考:http://www.ruby-lang.org/zh_cn/documentation/installation 根据平台不同选择不同安装方式

安装完成后 执行gem命令(类似于python中的pip) gem install redis-dump

验证:

redis-dump -V

 


可以调用表示安装成功


redis 和 strictredis

redis-py库提供两个类Redis 和 Strictredis 来实现Redis的命令操作

Strictredis 实现绝大部分官方命令 例如 set()对应Redis命令的set方法
Redis是Strictredis 的子类 主要功能是向后兼容旧版本库里的方法 例如 Item() 将value 和 num 参数位置互换
官方推荐 Strictredis

2.1 连接Redis

确保本地已经安装Redis 运行在6379端口 密码设置为 foobared

示例:

from redis import StrictRedis

redis = StrictRedis(host='localhost',port=6379,db=0,password='foobared')

redis.set('name','Bob')#设置键值对
print(redis.get('name'))#输出结果b'Bob'

#也可以使用ConnectionPool

from redis import StrictRedis,ConnectionPool

pool = ConnectionPool(host='localhost',port='6379',db=0,password='foobared')
redis = StrictRedis(connection_pool=pool)
print(redis.get('name'))

 

同样可以获取到

另外ConnectionPool 还支持三种URL来构造 password没有可以省略

redis://[:password]@host:port/db #TCP连接
rediss://[:password]@host:port/db#TCP+SSL连接
unix://[:password]@/path/to/socket.sock?db=db#UNIX socket连接

 

示例:

from redis import StrictRedis,ConnectionPool

url = 'redis://:foobared@localhost:6379/0'
pool = ConnectionPool.from_url(url)
redis = StrictRedis(connection_pool=pool)
print(redis.get('name'))

2.2 键操作

键的一些判断和操作方法

方法 作用

exists(name)    #判断一个键是否存在

delete(name)    #删除一个键

type(name)    #判断键类型

keys(pattern)    #获取所有符合规则的键

randomkey()    #获取随机的一个键

rename(src,dst)    #重命名键

dbsize()    #获取当前数据库中键的数目

expire(name,time)    #设键的过期时间 单位是秒

ttl(name)    #获取键的过期时间 -1为永不过期

move(name,db)#将键移动到其他数据库

flushdb()    #删除当前选择数据库中所有键

flushall()    #删除所有数据库中的所有键

 

 


2.3 字符串操作 最基本的键值对形式存储

set(name,value)    #给数据库中键为name的string赋值为value

get(name)    #返回数据库中键为name的string的value

getset(name,value)    #返回name的string的赋值value 和 上次的value

mget(keys,*args)    #返回多个键对应的value keys为列表

setnx(name,value)    #如果不存在这个键值对 则更新value 否则不变

setex(name,time,value)    #设置对应的值为string类型的value并指定有效期

setrange(name,offset,value)    #设置指定键的value值为子字符串

mset(mapping)    #批量赋值 mapping为字典

msetnx(mapping)    #键均不存在时才批量赋值

incr(name,amount=1)    #键为name的value增值操作默认为1,键不存在则被创建并设为amount

decr(name,amount=1)    #键为name的value减值操作 默认为1,键不存在则创建并将value设置为-amount

append(key,value)    #键为name的string的值附加为value 追加内容

substr(name,start,end=-1)    #返回键为name的string的子串 截取索引

getrange(key,start,end)    #获取键的value值从start到end的子字符串 截取索引

 


2.4 列表操作 列表内的元素可以重复 而且可以从两端存储

rpush(name,*vaules)    #在键为name的列表末尾添加值为value的元素,value:值可以传多个

lpush(name,*values)    #在键为name的列表头添加值为value的元素,value:值可以传多个

llen(name)    #返回键为name的列表的长度

lrange(name,start,end)    #返回键为name的列表中start至end之间的元素

ltrim(name,start,end)#    截取键为name的列表保留索引为start到end的内容

lindex(name,index)    #返回键为name的列表中index位置的元素

lset(name,index,value)    #给键为name的列表中index位置的元素赋值,越界则报错

lrem(name,count,value)    #删除count个键的列表中值为value的元素

lpop(name)    #返回并删除键为name的列表中的首元素

rpop(name)    #返回并删除键为name的列表中的尾元素

blpop(keys,timeout=0)    #返回并删除名称在keys中的list中的首个元素,如果列表为空,则会一直阻塞等待

brpop(keys,timeout=0)    #返回并删除名称在keys中的list中的尾元素,如果列表为空,则会一直阻塞等待

rpoplpush(src,dst)    #返回并删除名称为src的列表的尾元素,并将该元素添加到名称为dst的列表头部

 

 


2.5 集合操作 集合内的元素都是不重复的

sadd(name,*values)    #向键为name的集合中添加元素

srem(name,*values)    #从键为name的元素中删除元素

spop(name)    #随机返回并删除键为name的集合中的一个元素

smove(src,dst,value)    #从src对应的集合中移除元素并将其添加到dst对应的集合中

scard(name)    #返回键为name的集合的元素个数

sismember(name,value)    #测试member是否是键为name的集合的元素

sinter(keys,*args)    #返回所有给定键keys的集合

sinterstore(dest,keys,*args)    #求交集并将交集保存到dest的集合

sunion(keys,*args)    #返回所有给定键的集合的并集

sunionstore(dest,keys,*args)    #求并集并将并集保存到dest的集合

sdiff(keys,*args)    #返回所有给定键的集合的差集

sdiffstore(dest,keys,*args)    #求差集并将差集保存到dest集合

smembers(name)    #返回键为name的集合的所有元素

srandmember(name)    #随机返回键为name的集合中的一个元素,但不删除元素

 


2.6 有序集合操作

有序集合比集合多了一个分数字段 利用它可以对集合中的数据进行排序

zadd(name,*args,**kwargs)    #向键为name的zset中添加元素member,score用于排序。如果该元素存在则更新其顺序。

zrem(name,*values)    #删除键为name的zset中的元素

zincrby(name,value,amount=1)    #如果在键为name的zset中已经存在元素value,则将该元素的score增加amount否则向该元素集合中添加该元素,其score的值为amount

zrank(name,value)    #返回键为name的zset中元素的排名,按score从小到大排序

zrevrange(name,start,end,withscores=False)    #返回键为name的zset(按score从大到小)中index从start到end的所有元素

zrangebyscore(name,min,max,start=None,num=None,withscores=False) #返回键为name的zset中score在给定区间的元素

zcount(name,min,max)    #返回键为name的zset中score在给定区间的数量

zcard(name)    #返回键为name的zset的元素个数

zremrangebyrank(name,min,max)    #删除键为name的zset中排名在给定区间的元素

zremrangebyscore(name,min,max)    #删除键为name的zset中score在给定区间的元素

 

 

2.7 散列操作 可以用name指定一个散列表的名称,表内存储了各个键值对

hset(name,key,value)    #向键为name的散列表中添加映射

hsetnx(name,key,value)    #如果映射键名不存在,则向键为name的散列表中添加映射

hget(name,key)    #返回键为name的散列表中key对应的值

hmget(name,keys,*args)    #返回键为name的散列表中各个键对应的值

hmset(name,mapping)    #向键为name的散列表中批量添加映射

hincrby(name,key,amount=1) #将键为name的散列表中映射的值增加amount

hexists(name,key)    #键为name的散列表中是否存在键名为键的映射

hdel(name,*keys)    #在键为name的散列表中删除键名为键的映射

hlen(name)    #从键为name的散列表中获取映射个数

hkeys(name)    #从键为name的散列表中获取所有映射键名

hvals(name)    #从键为name的散列表中获取所有映射键值

hgetall(name)    #从键为name的散列表中获取所有映射键值对

 


2.8 RedisDump
(若执行redis-dump报错 :Error connecting to Redis on localhost:6379 (Redis::TimeoutError))
到 'C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\redis-dump-0.4.0\lib\redis' 目录下记事本打开dump.rb
ps -o rss= -p #{Process.pid}.to_i 加注释#
执行

redis-dump -h

 

-u 代表redis连接字符串

-d 代表数据库代号

-a 密码

-s 代表导出之后的休眠时间

-b 将键值编码为base64(对二进制值有用)

-c 代表分块大小 默认是10000

-f 代表导出时的过滤器

-O 代表禁用运行时优化

-V 显示版本

-D 表示开启调试

 

执行命令

redis-dump -u 127.0.0.1:6379

 


打印出redis库所有数据

导出为json行文件

redis-dump -u 127.0.0.1:6379 > ./redis_data.jl

 

也可以指定库 -d 1 空格 | -d1 表示1号数据库
-f 过滤 例如adsl开头 -f adsl:*


redis-load

 查看帮助

redis-load -h
-u 代表redis连接字符串

-d 代表数据库代号

-a 密码

-s 代表导出之后的休眠时间

-b 将键值编码为base64(对二进制值有用)

-n 不检测UTF-8编码

-V 显示版本

-D 表示开启调试

 

导入json行文件

< ./redis_data.jl redis-load -u 127.0.0.1:6379

 

总结 :
了解redis-py对redis数据库的一些基本操作 还演示了RedisDump对数据的导入导出操作

posted @ 2019-01-30 11:04  贫道从来不吃素  阅读(449)  评论(0编辑  收藏  举报