Python爬虫3-数据持久化


一、json文件操作

什么是json:

JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。更多解释请见:https://baike.baidu.com/item/JSON/2462549?fr=aladdin

JSON支持数据格式:

  1. 对象(字典)。使用花括号。
  2. 数组(列表)。使用方括号。
  3. 整形、浮点型、布尔类型还有null类型。
  4. 字符串类型(字符串必须要用双引号,不能用单引号)。

多个数据之间使用逗号分开。
注意:json本质上就是一个字符串。

字典和列表转JSON:

import json
books = [
    {
        'title': '钢铁是怎样练成的',
        'price': 9.8
    },
    {
        'title': '红楼梦',
        'price': 9.9
    }
]
json_str = json.dumps(books,ensure_ascii=False)
print(json_str)

因为jsondumps的时候,只能存放ascii的字符,因此会将中文进行转义,这时候我们可以使用ensure_ascii=False关闭这个特性。
Python中。只有基本数据类型才能转换成JSON格式的字符串。也即:intfloatstrlistdicttuple

将json数据直接dump到文件中:

json模块中除了dumps函数,还有一个dump函数,这个函数可以传入一个文件指针,直接将字符串dump到文件中。示例代码如下:

books = [
    {
        'title': '钢铁是怎样练成的',
        'price': 9.8
    },
    {
        'title': '红楼梦',
        'price': 9.9
    }
]
with open('a.json','w') as fp:
    json.dump(books,fp)

将一个json字符串loads成Python对象:

json_str = '[{"title": "钢铁是怎样练成的", "price": 9.8}, {"title": "红楼梦", "price": 9.9}]'
books = json.loads(json_str,encoding='utf-8')
print(type(books))
print(books)

直接从文件中读取json:

import json
with open('a.json','r',encoding='utf-8') as fp:
    json_str = json.load(fp)
    print(json_str)

区别:json.load 和json.loads json.dump和json.dumps

参考:https://www.cnblogs.com/bigtreei/p/10466518.html
loads: 是将string转换为dict
dumps: 是将dict转换为string
load: 是将里json格式字符串转化为dict,读取文件
dump: 是将dict类型转换为json格式字符串,存入文件

二、csv文件操作

1.读取csv文件:

def read_csv_demo1():
    with open('stock.csv', 'r') as fp:
        # reader是一个迭代器
        reader = csv.reader(fp)
        next(reader)        #跳过第一行
        for x in reader:
            name = x[3]     #secShortName
            volumn = x[-1]     	#turnoverVol 
            print({'name': name, 'volumn': volumn})

如果csv文件的列更换了顺序,再通过下标获取会出错,那么可以使用DictReader通过标题来获取数据。示例代码如下:

import csv
with open('stock.csv','r') as fp:
    reader = csv.DictReader(fp)
    # 使用DictReader创建的reader对象
    # 不会包含标题那行的数据,即从索引为1开始
    # reader是一个迭代器,遍历这个迭代器,返回来的是一个有序的字典(即与csv文件中列表顺序保持一致)
    for x in reader:
        value = {"name":x['secShortName'],'volumn':x['turnoverVol']}
        print(value)

2.写入数据到csv文件:

写入数据到csv文件,需要创建一个writer对象,主要用到两个方法。一个是writerow,这个是写入一行。一个是writerows,这个是写入多行。示例代码如下:

import csv
headers = ['name','age','classroom']	#表头可以是列表或元组
values = [
    ('zhiliao',18,'111'),
    ('wena',20,'222'),
    ('bbc',21,'111')
]
#encoding='utf-8' 指定编码, newline='' 默认是'\n' 指定不用空行
with open('test.csv','w',encoding='utf-8', newline='') as fp:
    writer = csv.writer(fp)
    writer.writerow(headers)		#写入一行
    writer.writerows(values)	#写入多行

也可以使用字典的方式把数据写入进去。这时候就需要使用DictWriter了。示例代码如下:

import csv
headers = ['name','age','classroom']
values = [
    {"name":'wenn',"age":20,"classroom":'222'},
    {"name":'abc',"age":30,"classroom":'333'}
]
with open('test.csv','w',newline='') as fp:
    writer = csv.DictWriter(fp,headers)
    # 写入表头数据的时候,需要调用writeheader方法
    writer = csv.writeheader()
    writer.writerow({'name':'zhiliao',"age":18,"classroom":'111'})
    writer.writerows(values)

三、pymysql 操作数据库

1、准备工作

安装mysql:

  1. 在官网:https://dev.mysql.com/downloads/windows/installer/5.7.html
  2. 推荐下载免安装版本的MySQL

navicat:
navicat是一个操作mysql数据库非常方便的软件。使用他操作数据库,就跟使用excel操作数据是一样的。
安装驱动程序:
Python要想操作MySQL。必须要有一个中间件,或者叫做驱动程序。驱动程序有很多。比如有mysqldbmysqlclientpymysql等。在这里,我们选择用pymysql。安装方式也是非常简单,通过命令pip install pymysql即可安装。

2、基本用法

2.1 数据库连接:

数据库连接之前。首先先确认以下工作完成,这里我们以一个pymysql_test数据库.以下将介绍连接mysql的示例代码:

import pymysql
#1、连接数据库
conn = pymysql.connect(
    host="127.0.0.1",
    user='root',
    password='root',
    database='pymysql_test',
    port=3306	#必须是整型
)
#2、获取游标 即mysql >
cursor = conn.cursor()

#3、执行sql语句
sql='select * from user1 where id = %s;'
rows = cursor.execute(sql,1)		#返回受影响行数rows		

#4、关闭
cursor.close()
conn.close()

可以通过Navicat软件或cmd命令行创建数据库表,也可以通过python代码创建表结构

c_sql = '''create table test1(
                    id int(3) zerofill primary key auto_increment,
                    name varchar(15) not null
                )engine=InnoDB,charset=utf8;
         '''
cur.execute(c_sql)

2.2 插入数据:

import pymysql
db = pymysql.connect(
    host="127.0.0.1",
    user='root',
    password='root',
    database='pymysql_test',
    port=3306
)
cursor = db.cursor()
sql = """
insert into user(
    id,username,gender,age,password
  ) 
  values(null,'abc',1,18,'111111');
"""
cursor.execute(sql)
db.commit()
db.close()

补充:""" """三引号之间可以写入多行数据。
如果在数据还不能保证的情况下,可以使用以下方式来插入数据:

sql = """
insert into user(
    id,username,gender,age,password
  ) 
  values(null,%s,%s,%s,%s);
"""
cursor.execute(sql,('spider',1,20,'222222'))		#参数可以是元组、列表、字典
db.commit()
db.close()

注意:execute() 插入的是一条数据

def execute(self, query, args=None):
    """Execute a query
    :param str query: Query to execute.
    :param args: parameters used with query. (optional)
    :type args: tuple, list or dict(元组 列表 字典)
    :return: Number of affected rows	返回受影响的行数
    :rtype: int
如果参数是列表或元组,%s 在query字符串中可以被用作占位符
If args is a list or tuple, %s can be used as a placeholder in the query.
如果参数是字典,%(name)s 在query字符串中可以被用作占位符
If args is a dict, %(name)s can be used as a placeholder in the query.
    """
    
注意:其中name是传入的字典的key

executemany插入多条数据:

sql = """
	insert into user (username,password,age) values (%s,%s,%s)
"""
params = [('zhao','111111',22),('sun','111111',45)]
cursor.executemany(sql,params)

conn.commit()       #提交到数据库
cursor.close()
conn.close()

2.3 查找数据:

使用pymysql查询数据。可以使用fetch*方法。

  1. fetchone():这个方法每次之获取一条数据。
  2. fetchall():这个方法接收全部的返回结果。
  3. fetchmany(size):可以获取指定条数的数据。
    示例代码如下:
    cursor = db.cursor()
    sql = """
    select * from user
    """
    cursor.execute(sql)
    while True:
        result = cursor.fetchone()
        if not result:
            break
        print(result)
    db.close()
    或者是直接使用fetchall,一次性可以把所有满足条件的数据都取出来:
    cursor = db.cursor()
    sql = """
    select * from user
    """
    cursor.execute(sql)
    results = cursor.fetchall()
    for result in results:
        print(result)
    db.close()
    或者是使用fetchmany,指定获取多少条数据:
    cursor = db.cursor()
    sql = """
    select * from user
    """
    cursor.execute(sql)
    results = cursor.fetchmany(1)
    for result in results:
        print(result)
    db.close()

    2.4 删除数据:

    cursor = db.cursor()
    sql = """
    delete from user where id=1
    """
    cursor.execute(sql)
    db.commit()
    db.close()

    2.5 更新数据:

    sql = """
    update user set username='aaa' where id=1
    """
    cursor.execute(sql)
    conn.commit()
    conn.close()

    2.6 关闭数据库连接

    需要分别的关闭指针对象和连接对象,他们有名字相同的方法
    cursor.close()
    conn.close()

    3、pymysql操作MySQL占位符问题

    使用占位符可以解决SQL注入

    # 原来是我们对sql进行字符串拼接
    # sql="select * from userinfo where name='%s' and password='%s'" %(user,pwd)
    # rows=cursor.execute(sql)
    
    #改写为(execute帮我们做字符串拼接,我们无需且一定不能再为%s加引号了)
    #注意%s需要去掉引号,因为pymysql会自动为我们加上
    sql="select * from userinfo where name=%s  and password=%s" 
    rows=cursor.execute(sql,[user,pwd])
    
    #pymysql模块自动帮我们解决sql注入的问题,只要我们按照pymysql的规矩来。

    SQL语句编写(占位符的应用)

    pymysql执行的关键还是在SQL语句的编写,有几种方式:
    1、完整的SQL语句,直接调用
    sql_select = "select * from tablename"
    cursor.execute(sql_select)
    2、利用占位符传递参数。这里要注意,无论整数、字符串,占位符都为 %s,且不需加引号 ,在sql语句中借助占位符组成完整SQL
    tabel = 'persons'
    sql = "select * from %s" % table
    cursor.execute(sql)
    3、参数替代
    tabel = 'persons'
    sql = "select * from %s" 
    cursor.execute(sql, table)
    
    # 参数多于1个时,execute()传入参数应为list或者tuple类型
    sql_update = "update persons set birthday=%s where ID=%s"
    cursor.execute(sql_update, ('2001/7/5', 3))
    4、字典类型传递变量,这里要保证占位符的keys要包含在传递的字典keys中
    # 这里的占位符%s修改为%(字典keyname)s
    sql_insert = "insert into persons(ID, LastName, FirstName) values (%(ID)s, %(LastName)s, %(FirstName)s)"
    message = {
        "ID": 7,
        "LastName": "Jone",
        "FirstName": "Bob",
    }
    cursor.execute(sql_insert, message)

    4、通过示意图理解pymysql

    参考:https://www.cnblogs.com/fanks99/p/9713873.html
    https://blog.csdn.net/guofeng93/article/details/53994112

    概念理解

    pymysql操作数据库流程图解

    理解Connection、Cursor

    Connection() 的参数列表
    加粗参数是必须的
  • host,连接的数据库服务器主机名,默认为本地主机(localhost)。
  • user,连接数据库的用户名,默认为当前用户。
  • password,连接密码,没有默认值。
  • db,连接的数据库名,没有默认值。
  • port,指定数据库服务器的连接端口,默认是3306。
  • conv,将文字映射到Python类型的字典。
  • MySQLdb.converters.conversions
  • cursorclass,cursor()使用的种类,默认值为MySQLdb.cursors.Cursor,可以设置为DictCursor
  • from
  • compress,启用协议压缩功能。
  • named_pipe,在windows中,与一个命名管道相连接。
  • init_command,一旦连接建立,就为数据库服务器指定一条语句来运行。
  • read_default_file,使用指定的MySQL配置文件。
  • read_default_group,读取的默认组。
  • unix_socket,在unix中,连接使用的套接字,默认使用TCP。
  • charset,编码方式

连接对象的db.close()方法可关闭数据库连接,并释放相关资源。
连接对象的db.cursor([cursorClass])方法返回一个指针对象,用于访问和操作数据库中的数据。
连接对象的db.begin()方法用于开始一个事务,如果数据库的AUTOCOMMIT已经开启就关闭它,直到事务调用commit()和rollback()结束。
连接对象的db.commit()和db.rollback()方法分别表示事务提交和回退。
指针对象的cursor.close()方法关闭指针并释放相关资源。
指针对象的cursor.execute(query[,parameters])方法执行数据库查询。
指针对象的cursor.fetchall()可取出指针结果集中的所有行,返回的结果集一个元组(tuples)。
指针对象的cursor.fetchmany([size=cursor.arraysize])从查询结果集中取出多行,我们可利用可选的参数指定取出的行数。
指针对象的cursor.fetchone()从查询结果集中返回下一行。
指针对象的cursor.arraysize属性指定由cursor.fetchmany()方法返回行的数目,影响fetchall()的性能,默认值为1。
指针对象的cursor.rowcount属性指出上次查询或更新所发生行数。-1表示还没开始查询或没有查询到数据。

Cursor对象常用方法

  1. close() :关闭此游标对象
  2. fetchone() :得到结果集的下一行
  3. fetchmany([size = cursor.arraysize]) :得到结果集的下几行
  4. fetchall() :得到结果集中剩下的所有行
  5. excute(sql[, args]) :执行一个数据库查询或命令
  6. excutemany(sql, args) :执行多个数据库查询或命令



使用步骤

1、建立数据库连接

import pymysql
conn = pymysql.connect(
    host='127.0.0.1',
	user='root',
	password="root",
    database='spider_mysql',
	port=3306,	#必须是int
	charset='utf8'
)
cursor=conn.cursor()

2、执行数据库操作
rows=cursor.execute(sql,param)
我们要使用连接对象获得一个cursor对象,接下来,我们会使用cursor提供的方法来进行工作.
这些方法包括两大类:1.执行命令,2.接收返回值
3、cursor用来执行命令的方法:

  • callproc(self, procname, args):用来执行存储过程,接收的参数为存储过程名和参数列表,返回值为受影响的行数
  • execute(self, query, args):执行单条sql语句,接收的参数为sql语句本身和使用的参数列表,返回值为受影响的行数
  • executemany(self, query, args):执行单挑sql语句,但是重复执行参数列表里的参数,返回值为受影响的行数
    • nextset(self):移动到下一个结果集

4、cursor用来接收返回值的方法:

  • fetchall(self):接收全部的返回结果行.
  • fetchmany(self, size=None):接收size条返回结果行.如果size的值大于返回的结果行的数量,则会返回cursor.arraysize条数据.
  • fetchone(self):返回一条结果行.
  • scroll(self, value, mode=’relative’):移动指针到某一行.如果mode=’relative’,则表示从当前所在行移动value条,如果mode=’absolute’,则表示从结果集的第一 行移动value条.

5、下面的代码是一个完整的例子.
使用sql语句,这里要接收的参数都用%s占位符,要注意的是,无论你要插入的数据是什么类型,占位符永远都要用%s

sql="insert into cdinfo values(%s,%s,%s,%s,%s)"
#param应该为tuple或者list
param=(title,singer,imgurl,url,alpha)
#执行,如果成功,n的值为1
n=cursor.execute(sql,param)
#再来执行一个查询的操作
cursor.execute("select * from cdinfo")
#我们使用了fetchall这个方法.这样,cds里保存的将会是查询返回的全部结果.每条结果都是一个tuple类型的数据,这些tuple组成了一个tuple
cds=cursor.fetchall()
#因为是tuple,所以可以这样使用结果集
print(cds[0][3])
#或者直接显示出来,看看结果集的真实样子
print(cds)
#如果需要批量的插入数据,就这样做
sql="insert into cdinfo values(0,%s,%s,%s,%s,%s)"
#每个值的集合为一个tuple,整个参数集组成一个tuple,或者list
param=((title,singer,imgurl,url,alpha),(title2,singer2,imgurl2,url2,alpha2))
#使用executemany方法来批量的插入数据.这真是一个很酷的方法!
n=cursor.executemany(sql,param)

需要注意的是(或者说是我感到奇怪的是),在执行完插入或删除或修改操作后,需要调用一下conn.commit()方法进行提交.这样,数据才会真正保 存在数据库中.我不清楚是否是我的mysql设置问题,总之,今天我在一开始使用的时候,如果不用commit,那数据就不会保留在数据库中,但是,数据 确实在数据库呆过.因为自动编号进行了累积,而且返回的受影响的行数并不为0.
6、关闭数据库连接
需要分别的关闭指针对象和连接对象.他们有名字相同的方法
cursor.close()
conn.close()
Django操作数据库
django是一个出色的用于python的web框架。django连接有操作数据库的api,使用起来十分简洁。我们在settings.py中配置好所要连接的数据库,然后在modules、view、urls中分别写好业务逻辑。

四、MongoDB数据库操作

菜鸟教程:https://www.runoob.com/mongodb/mongodb-tutorial.html

1.准备工作

MongoDB是一个基于分布式文件存储的NoSQL数据库,在处理海量数据的时候会比MySQL更有优势,爬虫如果数据量十分庞大,推荐使用MongoDB,当然没有达到海量的数据完全也可以使用MongoDB来存储数据。因此学会MongoDB也是爬虫工程师必须要掌握的一个技能。
Windows下安装MongoDB数据库:
注意:安装路径中不要出现中文 空格

运行MongoDB:
1.创建数据目录:
启动MongoDB之前,首先要给它指定一个数据存储路径,比如在MongoDB的安装路径下新建一个data文件夹,专门用来存储数据的。
2.把MongoDB的bin目录加入到环境变量中
3.执行命令mongod --dbpath C:\Program Files\MongoDB\Server\3.4\data启动
连接MongoDB:
在环境变量设置好的前提下,执行mongo即可进入到mongo的操作终端了。
使用Compass软件连接MongoDB:
compass是一个GUI操作MongoDB的客户端,类似与Navicat
将MongoDB制作成Windows服务:
启动MongoDB后如果想要让MongoDB在后台一直运行,而不是一直开着一个cmd终端运行着mongod.exe服务端,我们可以将MongoDB制作成一个类似于Windows后台服务,以后通过一行命令即可运行MongoDB服务端,下面是制作步骤:
1.创建配置文件:在MongoDB安装的路径下创建配置文件/log/mongod.cfg(路径和名字不是必须和我保持一致,但是后缀名必须是cfg),然后再配置文件中添加如下代码:

logpath=C:\programApps\mongodb\log\mongod.log
dbpath=C:\programApps\mongodb\data

logpath是日志的路径,dbpath的MongoDB数据库的存储路径。
2.安装MongoDB服务:
使用下面命令即可将MongoDB安装成一个服务:

mongod --config 'cfg配置文件所在的路径' --install
例如:
mongod --config 'C:\programApps\mongodb\mongod.cfg' --install

3.启动MongoDB:net start mongodb
4.关闭MongoDB:net stop mongodb
5.移除MongoDB:"" --remove

2.MongoDB基本使用:

2.1 MongoDB概念介绍

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
joins ××× 表连接/MongoDB不支持表连接
primary key primary key 主键/MongoDB自动将_id设置为主键

2.2 MongoDB三元素:

三元素:数据库、集合、文档
1.文档(document):就是关系型数据库中的一行row。文档是一个对象由键值对构成,是json的扩展形式:

{'name':'xiong','age':18}

2.集合(collection):就是关系型数据库中的表。可以存储多个文档,结构可以不固定,比如可以存储如下文档在一个集合中:

{'name':'xiong','age':18}
{'name':'xiong','gender':1}
{'title':'美食','price':128}

2.3 MongoDB基本操作命令

1.db:查看当前数据库
2.show dbs:查看所有数据库
3.use 数据库:切换数据库,如果不存在则创建一个(只是在内存中创建需要插入一条数据才算真正写入到磁盘)
4.db.dropDatabase():删除当前使用的数据库
5.db.集合名.insert(value):添加数据到指定的集合中

  • db.collection.insertOne() 插入一条数据
  • db.collection.insertMany()插入多条数据

6.db.集合名.find():从指定的集合中查找数据
7.db.createCollection(name, options):创建集合,name: 要创建的集合名称,options: 可选参数, 指定有关内存大小及索引的选项,db.createCollection("runoob")
8.show collections:查看所有集合
9.db.集合名.drop():删除集合
更多命令操作见:https://www.runoob.com/mongodb/mongodb-create-database.html

2.4 Python操作MongoDB

https://www.runoob.com/python3/python-mongodb.html
安装pip install pymongo
连接mongodb:

import pymongo
#获取连接的对象
client = pymongo.MongoClient(
	host='127.0.0.1',
	port=27017,
)
#获取数据库
db = client.zhihu
#获取集合
collection = db.qa
#插入一条数据到集合中
collection.insert_one({
	'username':'xiong',
	'age':18
})

数据类型:

类型 数字 备注
Double 1 浮点型
String 2 字符串,最常用,必须是有效的utf8
Object 3 用于嵌入式的文档,即一个值为一个文档
Array 4 数组/列表
Binary data 5
Undefined 6 已废弃。
Object id 7 文档ID
Boolean 8 true/false
Date 9 日期或时间的UNIX时间格式
Null 10 Null值
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
integer 16 可以是32/64位,取决于服务器
Timestamp 17 时间戳,从1970-1-1到现在的秒数
Min key 255 Query with -1
.
Max key 127

操作MongoDB的主要方法:
1.insert_one:插入一条文档到集合中

collection.insert_one({
	'username':'xiong',
	'age':18
})

2.insert_many:插入多条文档数据到集合中:

collection.insert_one({
	'username':'xiong',
	'age':18
	},
    {
	'goods':'苹果',
	'price':5.88
	},
)

3.find_one:查找一条文档对象:

result = collection.find_one()
result = collection.find_one({'username':'xiong'}) #按条件查找的

4.更新一条数据

collection.update_one({"username":'xiong'},{"$set":{"username":"xiaoxiong"}})

5.更新多条数据

import pymongo
 
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = myclient["runoobdb"]		#或者 myclient.runoobdb
mycol = mydb["sites"]		#或者 mydb.sites
 
myquery = { "name": { "$regex": "^xiong" } }	#所有名字以xiong开头的
newvalues = { "$set": { "xiaoxiong": "123" } }
 
x = mycol.update_many(myquery, newvalues)
 
print(x.modified_count, "文档已修改")

6.delete_one删除一条文档对象

myquery = { "name": "Taobao" }
mycol.delete_one(myquery)

7.delete_many删除多条文档对象

myquery = { "name": {"$regex": "^F"} } 
x = mycol.delete_many(myquery)
print(x.deleted_count, "个文档已删除")		#5个文档已删除

delete_many() 方法如果传入的是一个空的查询对象,则会删除集合中的所有文档:
x = mycol.delete_many({})删除集合mycol中的所有文档
使用 drop() 方法来删除一个集合:
mycol.drop()


文章作者: CoderXiong
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 CoderXiong !
  目录