参考 《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 个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构,使用也非常简单。