Python爬虫理论 | (4) 数据存储

30 篇文章 19 订阅

关于爬虫数据存储问题,通常可以有三种选择:

1.文本文件(txt,json)或excel文件(csv,xlsx)

           优势:使用方便,不需要第三方支持 

                       劣势:健壮性差,扩展性差

2.数据库(mysql,mongoDB,redis,oracle…)

           优势:良好的扩展性,使用广泛

                       劣势:需要第三方支持,对技术有一定要求

3.文件系统(hadoop)

                       更自由,但技术要求会更高

接下来,我们将就上述几个常用的数据存储方式进行学习。

目录

1. 文件存储

 1.1 txt、json

1.2 csv、excel

2. 数据库存储

2.1 MySQL

2.2 MongoDB

3. 实战


1. 文件存储

读写文件是最常见的IO操作。Python内置了读写文件的函数。

在磁盘上读写文件的功能实际上是由操作系统提供的,读写文件就是请求操作系统打开一个文件对象,然后通过操作系统提供的接口从这个文件对象中读取数据(读文件),或者把数据写入这个文件对象(写文件)。

1)打开文件

使用Python内置的open()函数,传入文件名和标示符:f = open('test.txt', 'r')。如果文件不存在,open()函数就会抛出一个IOError的错误。

2)读文件

调用read()方法可以一次读取文件的全部内容,Python把内容读到内存,用一个str对象表示:f.read()

3)写文件

写文件函数:f.write(‘Hello, world!’) 可以将字符串写入到打开的文件中,但通常不会立刻写入磁盘,而是放到内存缓存起来,在调用close()方法时,才把没有写入的数据全部写入磁盘。

4)关闭文件

无论是读还是写,文件使用完毕后必须关闭,因为文件对象会占用操作系统的资源,并且同一时间能打开的文件数量是有限的:f.close()

 

由于文件读写时都有可能产生IOError,一旦出错,后面的f.close()就不会调用。所以,为了保证无论是否出错都能正确地关闭文件,使用try ... finally来实现:

try:
    f = open('test.txt', 'r')
    print(f.read()) 			#输出test.txt中的全部内容
finally:
    if f:
        f.close()

但是每次都这么写实在太繁琐,所以,Python引入了with语句来自动调用close()方法:

with open('test.txt', 'a', encoding='utf-8') as f:
    print(f.read())
    f.write(data)

 1.1 txt、json

  • txt文件
with open('data.txt','a',encoding='utf-8') as f:

如果文件很小,read()一次性读取最方便.

如果不能确定文件大小,反复调用read(size)比较保险(每次读size个字符)

如果是配置文件,调用readlines()最方便;(按行读,存储到列表中,列表的每一个元素对应文件的一行)

如果是大文件,调用readline()最方便。(读取文件第一行)

标识符:

 主要方法:

注意:如果文件以a或a+的模型打开,每次进行写操作时,文件操作标记会自动返回到文件末尾,进行追加。

实例:

#将字符串"hello world"和"I love python"追加到data.txt中
data1 = "hello world" 
data2 = "I love python"
with open('data.txt', 'a', encoding='utf-8') as f:
    f.write(data1 + "\n")
    f.write(data2 + "\n")
#读取data.txt中前3个字符
with open('data.txt', 'r', encoding='utf-8') as f:
    print(f.read(3)) 		#hel
#读取data.txt中第一行字符
with open('data.txt', 'r', encoding='utf-8') as f:
    print(f.readline())  	#hello world
#读取data.txt中所有行到列表
with open('data.txt', 'r', encoding='utf-8') as f:
    print(f.readlines())  	#['hello world\n', 'I love python\n']

  • json格式文件

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。JSON采用完全独立于语言的文本格式,这些特性使JSON成为理想的数据交换语言。易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率)。JSON在python中分别由list和dict组成。

 Json库: 用于字符串和python数据类型间进行转换,它提供了四个功能:

1)dumps(把Python数据类型转换成字符串 )

2)loads(把字符串转换成Python数据类型)

3)dump (把Python数据类型转换成字符串并存储在文件中)

4)load (把文件打开从字符串转换成Python数据类型)

json可以在不同语言之间交换数据,但只能序列化最基本的数据类型,如常用的数据类型序列化(列表、字典、元组、字符串、数字)。对于日期格式、类对象,json不支持。

注意:json格式文件中使用的是双引号(统一的,即使写入时用的是单引号。当把Python字典写入json文件时,如果字典的键是数字类型,写入json文件后会被加上双引号;再从json文件中读出到Python字典中时,会和原来的字典有所不同,就是键变成了字符串类型,此时需要转换一下。),中文使用的是Unicode。

写数据到json文件:

with open('data.json', 'a', encoding='utf-8') as f:
	f.write(json.dumps(data))

或

with open('data.json', 'a', encoding='utf-8') as f:
	json.dump(data, f)

 从json文件读数据:

with open('data.json', 'a', encoding='utf-8') as f:
	data = json.load(f)

实例:

 

import json
import time
def store(data):  #存储数据
    with open('data.json', 'w') as json_file:
        json_file.write(json.dumps(data))
        
def load():	#读取数据
    with open('data.json') as json_file:
        data = json.load(json_file)
        return data
if __name__ == "__main__":
    data = {}
    data["No."] = 123
    data["name"]="张三"
    data['a'] = "bb"
    data[1086] = 11
    data[2800] = 'uzi'
    data["date"] = time.strftime("%Y-%m-%d")
    print(data) 
    store(data)
    data = load()
    print(data)
    print(data["name"],data["date"])   #张三 2019-05-06

 

1.2 csv、excel

  • csv格式文件

CSV(Comma-Separated Values)即逗号分隔值,可以用Excel打开查看,由于是纯文本,用其它任何编辑器也都可打开。

写入列表数据:

import csv

with open('data.csv', 'w', encoding='utf-8', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['id', 'name', 'age'])   #写入字段名
    writer.writerow(['1', 'Mike', 20]) #写入一行
    writer.writerows([['2', 'Bob', 21], ['3', 'Alice', 18]]) #写入多行

写入字典数据:

 

import csv

with open('data.csv', 'w', encoding='utf-8', newline='') as f:
    fieldnames = ['id', 'name', 'age'] #字段名
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    writer.writeheader()        #写入字段名
    writer.writerow({'id': '1', 'name': 'Mike', 'age': 20}) #写入一行
    writer.writerows([{'id': '2', 'name': 'Bob', 'age': 21},  #写入多行
                                      {'id': '3', 'name': 'Alice', 'age': 19}])

读取csv文件:

 reader只能被遍历一次。由于reader是可迭代对象,可以使用next方法一次获取一行;reader.line_num代表行号。

import csv

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

import csv

with open('data.csv', 'r', encoding='utf-8') as f:
    reader = csv.reader(f)
    head_row = next(reader) #获取字段名
    print(head_row)  #打印字段名
    print("----------------")
    for row in reader: #打印除字段名之后的每一行的内容
          print(row)

 

 可以使用pandas库中的read_csv()函数,直接读取.csv文件,保存到DataFrame结构中。

data = pd.read_csv('data.csv')
  • excel格式文件

写入列表数据:

import xlwt

wb = xlwt.Workbook() 				# 创建工作薄对象
sheet = wb.add_sheet('sheet1')		# 添加工作表sheet
data = [['id', 'name', 'age'], ['1', 'Mike', 20], ['2', 'Bob', 21]]
for i in range(len(data)):
    for j in range(len(data[i])):
        sheet.write(i, j, data[i][j])		# 写入数据到工作表的指定位置
wb.save('data.xls')				# 写入excel文件

 读取数据:

import xlrd
wb = xlrd.open_workbook('data.xls') 	# 打开excel文件
sheet = wb.sheet_by_index(0)		# 打开第一个工作表
print(sheet.cell(0, 0))  			# 输出单元格对象A1
print(sheet.cell_value(0, 0))   		# 输出单元格A1中的数据内容
rows = sheet.nrows			# 获取行数
print(rows)				
print(sheet.row(0))  			# 输出第一行对象(列表)
print(sheet.row_values(0))   		# 输出由第一行中所有单元格的数据
cols = sheet.ncols   			# 获取列数
print(cols)				
print(sheet.col(1))  			# 输出第二列对象组成的列表
print(sheet.col_values(1))   		# 输出第二列中所有单元格的数据

追加数据:

#! pip install xlutils
import xlrd
import xlutils.copy
wb = xlrd.open_workbook('data.xls')
ws = xlutils.copy.copy(wb)
sheet = ws.get_sheet(0)
data =[['3', 'Alice', 19], ['4', 'Tom', 18]]
wb_sheet = wb.sheet_by_index(0)      # 打开第一个工作表
rows = wb_sheet.nrows
for i in range(len(data)):
    for j in range(len(data[i])):
        sheet.write(i+rows, j, data[i][j])   #从最后一行开始添加
ws.save('data.xls') 		           # 写入excel文件

 

2. 数据库存储

Python标准数据库接口为PythonDB-API

Python数据库接口支持多种数据库:

①Relational Databases关系型数据库

MySQL、Microsoft SQL Server 2000、IBM DB2、Informix、Interbase、Oracle、Sybase、mSQL、PostgreSQL

②Non-Relational Databases非关系型数据库

MongoDB、MetaKit、ZODB、BerkeleyDB、KirbyBase、Durus、Neo4j、4Suite server

2.1 MySQL

MySQL是一个轻量级的关系型数据库。优点:轻量级数据库,直接使用SQL语句,简单方便,可以使用图形界面查看数据非常直观。

Python和MySql的交互需要使用pymysql库。MySql的安装使用教程可以自行查询,而且可以安装一个客户端Navicat或SQLyog等,可视化数据库。

  • 创建数据库spiders
import pymysql
db = pymysql.connect(host='localhost', user='root', password='123456789', port=3306)
cursor = db.cursor()
cursor.execute('CREATE DATABASE IF NOT EXISTS spiders DEFAULT CHARACTER SET utf8') 
db.close()
  • 创建数据表students
import pymysql

#本地数据库 host='localhost' 连接远程数据库时,host为远程数据库ip
db = pymysql.connect(host='localhost', user='root', password='123456789', port=3306, db='spiders')
cursor = db.cursor()
cursor.execute('CREATE TABLE IF NOT EXISTS students (id VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, age INT NOT NULL, PRIMARY KEY (id))')
db.close()
  • 插入数据
import pymysql

db = pymysql.connect(host='localhost', user='root', password='123456789', port=3306, db='spiders')
cursor = db.cursor()
sql = 'INSERT INTO students(id, name, age) values (%s, %s, %s)'
data = [['1', 'Mike', 20], ['2', 'Bob', 21], ['3', 'Alice', 19]]
for i in data:
    try:
        cursor.execute(sql, i)
        db.commit()
    except:
        db.rollback()
db.close()
  •  更新数据
import pymysql
db = pymysql.connect(host='localhost', user='root', password='123456789', port=3306, db='spiders')
cursor = db.cursor()
sql = 'UPDATE students SET age = %s WHERE name = %s'
try:
    cursor.execute(sql, (25, 'Bob'))
    db.commit()
except:
    db.rollback()
db.close()
  • 删除数据
import pymysql
db = pymysql.connect(host='localhost', user='root', password='123456789', port=3306, db='spiders')
cursor = db.cursor()
sql = 'DELETE FROM students WHERE age=20'
try:
    cursor.execute(sql)
    db.commit()
except:
    db.rollback()
db.close()
  •  查询数据
import pymysql
db = pymysql.connect(host='localhost', user='root', password='123456789', port=3306, db='spiders')
cursor = db.cursor()
sql = 'SELECT * FROM students WHERE age=25'
try:
    cursor.execute(sql)
    row = cursor.fetchone() #获取一行
    while row:
        print(row)
        row = cursor.fetchone()
except:
    db.rollback()
db.close()

  •  其他删除操作

删除表内的所有数据       truncate table (表名)

删除表     drop table (表名)

删除数据库     drop database (数据库名)

2.2 MongoDB

通常对于爬虫获取得到的数据,无论使用文件存贮还是关系型数据库基本上都没有问题。但是当数据变大,数据量和并发都很大时,关系型数据库容量和读写能力可能会有问题。

此外,相对于严格模式的关系数据库,NoSQL在爬虫的数据存储方面,会更有优势(如MongoDB)—使用更加自由,容易横向扩展、分片及复制:

爬虫字段经常变化,有时无法预期数据的字段

爬虫数据比较脏乱,需要清洗及优化

 

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

Python和MongoDB的交互需要使用pymongo库。MongoDB的安装使用教程可以自行查询,而且可以安装一个客户端,可视化数据库。

  • 插入数据
import pymongo
# 连接MongoDB client 
client = pymongo.MongoClient(host="localhost", port=27017)
# 指定数据库test
db = client.test     #or  db=client[“test”]
# 指定集合collection  --- 指定数据表students
collection = db.students
student = {'id': '1', 'name': 'Mike', 'age': 20}
result = collection.insert_one(student)    #插入一条记录
print(result)
students = [{'id': '2', 'name': 'Bob', 'age': 21}, {'id': '3', 'name': 'Alice', 'age': 18}]
result = collection.insert_many(students)     #插入多条记录
print(result)
  • 查询数据
result = collection.find_one({'name': 'Mike'})   #查询第一条记录
print(result)

result = collection.find_one ({'age': 20}) 
print(result) 
result = collection.find({'age': {'$lte': 20}}) #查询多条记录
print(result) #<pymongo.cursor.Cursor object at 0x000002150F03B550
print("age<=20:",[r for r in result]) 
result = collection.find({'name': {'$regex': '^M*'}}) #正则表达式
print(result)
  • 比较符号

  •  功能符号

  • 更新数据
query = {'name': 'Mike'}
new_values = {'$set': {'age': 25}}
result = collection.update_one(query, new_values) #更新一条记录
print(result)
print(result.matched_count, result.modified_count)
query = {'age': {'$gt': 20}}
new_values = {'$inc': {'age': 1}}
result = collection.update_many(query, new_values) #更新多条记录
print(result.matched_count, result.modified_count)
  • 删除数据
result = collection.delete_one({'name': 'Bob'})  #删除一条记录
print(result)
print(result.deleted_count)
result = collection.delete_many({'age': {'$lt': 25}}) #删除多条记录
print(result)
print(result.deleted_count)

 

3. 实战

  • 爬取猫眼电影影评存入MySQL

爬取电影《海王》2018-01-01 00:00:00到2019-01-01 00:00:00的影评信息,包括用户ID、城市、影评内容、评分、时间,并存入MySQL数据库。

影评接口:http://m.maoyan.com/mmdb/comments/movie/249342.json?_v=yes&offset=0&startTime=2019-01-01%2000:00:00

完整项目

  • 爬取网络小说存入MongoDB

爬取笔趣阁的小说,获取小说的名字、章节名称、内容存入MongoDB数据库。

网址:http://www.xbiquge.la/xiaoshuodaquan/

完整项目

  • 爬取万方数据库论文摘要存入MongoDB

分别爬取指定关键词的期刊、学位、会议论文的摘要,获取论文的题目、摘要、作者等信息,并存入MongoDB。

网址:http://www.wanfangdata.com.cn

完整项目

 

 

 

  • 4
    点赞
  • 0
    评论
  • 15
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 1024 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值