05爬虫_数据存储

参考 《Python 3网络爬虫开发实战 》崔庆才著

文件存储形式多种多样,比如可以保存成 TXT 纯文本形式,也可以保存为 JSON 格式 csv格式等 ,本节就来了解下文本文件的存储方式

  • txt文件兼容性高,但不利于检索

    with open('explore.txt', 'w', encoding='utf-8') as f:
        f.write('\n'.join([question,author,answer]))
        f.write('\n' + '=' *50 + '\n')
        
    

json

全称JavaScript Object Notation js对象标记

通过对象和数组的组合来表示数据,构造简洁但是结构化程度非常高,是一种轻量级的数据交换格式。

  • 对象和数组
    在js语言中,一切都是对象。因此,任何支持的类型都可通过json来表示
    字符串、数字、对象、数组等,但是对象和数组是比较特殊且常用的两种类型。

对象:它在 JavaScript 中是使用花括号{ }包裹起来的内容,数据结构为{ keyl va luel, key2: value2 }的键值对结构 在面向对象的语言中, key 为对象的属性, value为对应的值,键名可以使用整数、字符串来表示 值的类型可以是任意类型

数组:数组在 JavaScript 是方括号[]包裹起来的内容,数据结构为[ “java”,“javascript ”,"vb”, … ]的索引结 。在 JavaScript 中, 数组是一种比较特殊的数据类型,它也可以像对象那样使用键值对,但还是索引用得多 同样,值的类型可以是任意类型

JSON 可以由以上两种形式自由组合 成,可以无限次嵌套,结构清晰,是数据交换的极佳方式

读取json

python中调用json库中的loads()方法将json文本字符串加载为json对象
通过dumps() 将json对象转化为字符串

输出json

使用dumps() 如果想要保存json的格式,可以加一个参数 indent,代表缩进字符个数

CSV文件存储

Comma-Separated Values 逗号分隔值或字符分隔值,其文本以纯文本形式存储表格数据。该文件是一个字符序列,可以由任意数目的记录组成,记录间以某种换行符分隔,每条记录由字段组成,字段间的分隔符是其他字符或字符串,最常见的是逗号或制表符。不过所有记录都有完全相同的字段序列 ,相当于一个结构化表的纯文本形式,它比 Excel 文件更加简介, XLS文本是电子表格,它包含了文本、数值、公式和格式等内容,而 csv 中不包含这些 容,就是特定字符分隔的纯文本,结构简单清晰 所以,有时候用 csv 来保存数据是比较方便的

写入
import csv
with open('data.csv','w') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerrow(['id','name', 'age'])
    writer.writerrow(['1001','Bob', '20'])

如果想修改列与列之间的分隔符,可以传入delimiter参数。

writer = csv.writer(csvfile, delimiter=' ')
  • 字典的写入方式

    import csv
    with open('data.csv','w') as csvfile:
        fieldnames = ['id', 'name', 'age']
        writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerrow({'id':'1001','name':'Mike', 'age':'20'})
    

    这里先定义 个字段,用 fieldnames 表示,然后将其传给 DictWriter 来初始化一个字典写人对象,接着可以调用 writeheader () 方法先写人头信息,然后再调用 writerow ()方法传人相应字典即可

读取

使用csv库来读取文件

import csv 
with open ('data.csv','r', encoding='utf-8') as csvfile: 
reader = csv.reader(csvfile) 
for row in reader: 
	print(row)

如果接触过 pandas 的话,可以利用 read_csv() 方法将数据从 csv 中读取出来,例如:

import pandas as pd 
df = pd.read csv ('data.csv') 
print(df)

关系型数据库存储MySQL

  • 连接数据库
import pymysql
db = pymysql.connect(host='host', user='user', password='password', database='database')
cursor = db.cursor()
cursor.execute('select version()')
data = cursor.fetchone()
print(data)
cursor.close()
db.cursor()

这里通过 PyMySQL的connect () 方法声明一个 MySQL 连接对象,此时需要传入MySQL 运行的host (即 IP) 如果MySQL 在本地运行,传入 localhost 。如果 MySQL 在远程运行,则传入其公网地址,后续的参数 user 即用户名, password 即密码, database 即数据库名

连接成功后,需要再调用 cursor () 方法获得MySQL 操作游标,利用游标来执行 SQL 语句,然后调用 fetchone ()方法获得第一条数据,

  • 数据库操作
    可以使用python上下文来操作

     class DbOpera:
        """
        with DbOpera as (db,cur):
        """
        def __init__(self):
            self.db = pymysql.connect(host=current_app.config['DATABASE_HOST'],
                                       user=current_app.config['DATABASE_USER'],
                                       password=current_app.config['DATABASE_PASSWORD'],
                                       database=current_app.config['DATABASE_NAME'])
            self.cur = self.db.cursor()
    
        def __enter__(self):
            return self.db, self.cur
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            self.cur.close()
            self.db.close()
    
            
     
    sql = "insert into {} values('{}','{}','{}')""
    with DbOpera() as (db, cur):
        try:
            cur.execute(sql.format(tablename,values1, values2, values3))
            db.commit()
        except:
            db.rollback()
    
    
  • 插入数据

    插入需要执行 db 对象的 commit ()方法才可实现数据插入,对于数据插入、更新、删除操作,都需要调用该方法才能生效。

    我们加了一层异常处理 如果执行失败,则调用 rollback ()执行数据回滚,相当于什么都没有发生过

    在很多情况下,我们要达到的效果是插入方法元需改动,做成个通用方法,只需要传入一个动态变化的 典就好了 比如,构造这样个字典:

    {
        'id':'2012001'
        'name':'bob'
        'age':20
    }
    

    然后SQL语句会根据字典动态构造,这样才能实现通用的插入方式

    table = 'students'
    keys = ','.join(data.keys())
    values = ','.join(['%s']*len(data))
    sql = 'insert into {table}({keys}) values ({values})'.format(table=table, keys=keys,values=values)
    try: 
        cur.execute(sql,tuple(data.values()))
        db.commit()
    except:
        db.rollback()
    

    这里我们传人的数据是字典,并将其定义为 data 变量 表名也定义成变量 table 接下来,就要构造个动态的 SQL 语句了

    首先 需要构造插入的字段 id name age 这里只需要将 data 的键名拿过来,然后用逗号分隔即可,所以','.join(data.keys())的结果就是 id,name, age ,然后需要构造多个%s 当作占位符,几个字段构造几个即可,比如,这里有三个字段,就需要构造%s, %s, %s 这里首先定义了长度为1的数组[’%s’], 然后用乘法将其扩充为 [’%s ’,’%s’,’%s’] , 再调用 join() 方法 最终变成 %s,%s,%s。最后,利用字符串的 format ()方法将表名、字段名和占位符构造出来。最终的 QL 语句就被动态构造成了

    INSERT INTO students(id, name, age) VALUES (%s, %s, %s)

    最后,为 execute () 方法第一个参数传入 sql 变量,第二个参数传人 data 的键值构造的元组,就可以成功插入数据

  • 更新数据

    需要关心的是会不会出现重复的数据,如果出现了重复的数据,那我们希望更新数据而不是重复保存一次。

    data = {
        'id':'2012001'
        'name':'bob'
        'age':20
    }
    table = 'students'
    keys = ','.join(data.keys())
    values = ','.join(['%s']*len(data))
    sql = 'insert into {table}({keys} '\
    'values({values}) on duplicate key update'.format(table=table, keys=keys, values=values)
    update = ','.join([" {key} = %s".format(key=key) for key in data])
    sql +=update
    try:
        cur.execute(sql,tuple(data.value())*2)
        db.commit()
    except:
        db.rollback()
    

    在插入语句中加入 on duplicate key update 这行代码意思是 如果主键已经存在,就执行更新操作。
    比如:我们传人的数据 id 仍然为 20120001 ,但是年龄有所变化,由 20 变成了 21 ,此时这条数据不会被插入,而是直接更新 id 为20120001 的数据
    如此一来,我们就可以实现主键不存在便插入数据。存在则更新数据的功能

  • 删除数据
    直接使用delete语句即可。

非关系数据库存储 NoSQL

NoSQL基于键值对,而且不需要经历SQL层的解析,数据之间没有耦合性。

键值存储数据库:Redis、Voldemort
列存储数据库:Cassandra、HBase、Riak
文档型数据库:MongoDB、CouchDB
图形数据库:Neo4J、InfoGrid和 Infinite Graph

MongoDB

由C++语言编写的非关系数据库,是一个基于分布式文件存储的开源数据库,其内容存储形式类似JSON对象

  • 连接
    使用pyMongo库

    import pymongo
    client = pymongo.MongoClient(host='localhost', port=27017)
    

    host也可传入MongoDB的连接字符串

    import pymongo
    client = pymongo.MongoClient('mongodb://localhost:27017/')
    
  • 指定数据库test

    db = client.test
    # 或者
    db = client['test']
    
  • 指定集合students
    mongoDB中的每个数据库又包含许多集合(collection),它们类似关系型数据库中的表。

    collection = db.students
    # 或者
    collection = db['students']
    
  • 插入数据

    data ={
        'id': '20170101',
        'name': 'Jordan',
        'age': 20,
        'gender': 'male'
    }
    

    直接调用insert方法即可

    collection.insert(data)
    

    MongoDB 中,每条数据其实都有一个_id 属性来唯一标识 如果没有显式指明该属性, MongoDB会自动产生 ObjectId 类型的_id 属性。insert() 方法会在执行后返回_id值
    插入多条数据时只需以列表形式传达即可。

    data1={
        'id':'20170101',
        .......
    }
    data2={
        'id':'20170102',
        .......
    }
    collection.insert([data1,data2])
    

    返回结果是对应的_id集合

    注意:pyMongo3.x版本中,官方推荐使用insert_one()和insert_many()

  • 查询
    find_one() 或者find()方法进行查询。find()返回一个生成器对象

Redis

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值