zoukankan      html  css  js  c++  java
  • 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 "安装路径datadb"

    输出信息 证明启动了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-x64lib ubygems2.5.0gems edis-dump-0.4.0lib edis' 目录下记事本打开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对数据的导入导出操作

  • 相关阅读:
    python gui
    20189217 2018-2019-2 《密码与安全新技术专题》第11周作业
    20189217 2018-2019-2 本地音乐播放器cocoMusic 代码分析
    20189217 2018-2019-2 《移动平台开发实践》第12周学习总结
    20189217 2018-2019-2 《移动平台开发实践》课程总结
    20189217 2018-2019-2 《移动平台开发实践》第11周学习总结
    20189217 2018-2019-2 《密码与安全新技术专题》第9周作业
    20189217 2018-2019-2 《移动平台开发实践》第10周学习总结
    20189217 2018-2019-2 《移动平台开发实践》第9周学习总结
    AES
  • 原文地址:https://www.cnblogs.com/liuxiaosong/p/10337199.html
Copyright © 2011-2022 走看看