第五章 数据存储
-
用解析器解析出数据之后,接下来就是存储数据了。保存的形式可以多种多样,最简单的形式是直接保存为文本文件,如TXT、JSON、CSV等。另外,还可以保存到数据库中,如关系型数据库MySQL,非关系型数据库MongoDB、Redis 等。
-
爬虫——TXT文本存储
import requests from pyquery import PyQuery as pq url = 'https://www.zhihu.com/explore' headers = { 'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36' } html = requests.get(url, headers=headers).text doc = pq(html) items = doc('.explore-tab .feed-item').items() for item in items: question = item.find('h2').text() author = item.find('.author-link-line').text() answer = pq(item.find('.content').html()).text() file = open('explore.txt', 'a', encoding='utf-8') file.write('\n'.join([question, author, answer])) file.write('\n'+'=' * 50 + '\n') file.close()
-
文件的打开方法
-
JSON
JSON,全称为JavaScript Object Notation, 也就是JavaScript对象标记,它通过对象和数组的组合来表示数据,构造简洁但是结构化程度非常高,是一种轻量级的数据交换格式。
在JavaScript语言中,一切都是对象。因此,任何支持的类型都可以通过JSON来表示,例如字符串、数字、对象、数组等。
Python为我们提供了简单易用的JSON库来实现JSON文件的读写操作。
JSON库的loads()方法将JSON文本字符串转为JSON对象
dumps()方法将JSON对象转为文本字符串。
例如,这里有一段JSON形式的字符串,它是str类型,我们用Python将其转换为可操作的数据结构,如列表或字典:JSON字符串的表示需要用双引号,否则loads()方法会解析失败。
-
如果从JSON文本中读取内容,例如这里有一个data.json 文本文件,其内容是刚才定义的JSON字符串,我们可以先将文本文件内容读出,然后再利用loads()方法转化:
-
如果想将列表重新写入文本,可以使用dump()方法,将JSON对象转为字符串。
如果想保存JSON的格式,可以再加一个参数indent,代表缩进字符个数。
为了输出中文,还需要指定参数ensure_ascii为false,还要规定文件输出的编码
-
CSV文件存储
CSV,全称为Comma-Separated Values, 中文可以叫作逗号分隔值或字符分隔值,其文件以纯文本形式存储表格数据。记录间以某种换行符分隔。最常见的是逗号或制表符,CSV中不包含文本、数值、公式和格式等内容,就是特定字符分隔的纯文本,结构简单清晰。
写入:
如果想修改列与列之间的分隔符,可以传入delimiter参数。
另外,我们也可以调用writerows()方法同时写人多行,此时参数就需要为二维列表:
但是一般情况下,爬虫爬取的都是结构化数据,我们一般会用字典来表示。在csv库中字典的写入方式:
-
CSV的读取
这里我们构造的是Reader对象,通过遍历输出了每行的内容,每一行都是一个列表形式。注意,如果CSV文件中包含中文的话,还需要指定文件编码。
另外,如果接触过pandas的话,可以利用read_ csv()方法将数据从CSV中读取出来,例如:在做数据分析的时候,此种方法用得比较多,也是一种比较方便地读取CSV文件的方法。
-
关系型数据库
关系型数据库是基于关系模型的数据库,而关系模型是通过二维表来保存的,所以它的存储方式就是行列组成的表,每一列是一个字段,每一行是一条记录。表可以看作某个实体的集合,而实体之间存在联系,这就需要表与表之间的关联关系来体现,如主键外键的关联关系。多个表组成一个数据库,也就是关系型数据库。
关系型数据库有多种,如SQLite、MySQL、Oracle、SQL Server、DB2 等。 -
连接数据库
mysql.server start mysql -u root -p
这里通过PyMySQL的connect()方法声明一个 MySQL连接对象db,此时需要传人MySQL运行的host (即IP)。由于MySQL在本地运行,所以传人的是localhost。如果MySQL在远程运行,则传入其公网IP地址。后续的参数user即用户名,password 即密码,port 即端口(默认为3306 )。
连接成功后,需要再调用cursor()方法获得MySQL的操作游标,利用游标来执行SQL语句。这里我们执行了两句SQL,直接用execute()方法执行即可。第一句SQL用于获得MySQL的当前版本,然后调用fetchone()方法获得第一条数据, 也就得到了版本号。第二句SQL执行创建数据库的操作,数据库名叫作spiders,默认编码为UTF-8。由于该语句不是查询语句,所以直接执行后就成功创建了数据库spiders。接着,再利用这个数据库进行后续的操作。 -
创建数据库和表
create DATABASE spiders(表名);
-
向MySQL数据库中插入数据
构造一个动态的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()方法将表名、字段名和占位符构造出来。最终的SQL语句就被
动态构造成了:
INSERT INTO students(id, name, age) VALUES (%s, %s, %s)
最后,为execute()方法的第一个参数传人sql变量,第二个参数传人data的键值构造的元组,就可以成功插入数据了。 -
事务机制
为了保持数据的一致性,涉及到事务机制,事务含有4个属性,ACID特性。
-
向MySQL数据库中更新数据
这里构造的SQL语句其实是插入语句,但是我们在后面加了ON DUPLICATE KEY UPDATE。这行代码的意思是如果主键已经存在,就执行更新操作。比如,我们传人的数据id 仍然为20120001, 但是年龄有所变化,由20变成了21,此时这条数据不会被插人,而是直接更新id为20120001的数据。
-
删除MySQL中的数据
直接使用DELETE即可,只需要指定要删除的目标表名和删除条件,且仍需使用commit()方法。
-
查询MySQL中的数据
将年龄20岁及以上的学生查询出来:
构造一条SQL语句,将年龄20岁及以上的学生查询出来,然后将其传给execute()
方法。这里不再需要db的commit()方法。接着,调用cursor的rowcount属性获取查询结果的条数,然后我们调用了fetchone()方法,这个方法可以获取结果的第一条数据,返回结果是元组形式,元组的元素顺序跟字段一一对应 ,随后,我们又调用了fetchall()方法,它可以得到结果的所有数据。然后将其结果和类型打印出来,它是二重元组,每个元素都是一条记录,我们将其遍历输出出来。
5.3 非关系型数据库存储
-
NoSQL,全称Not Only SQL,意为不仅仅是SQL,泛指非关系型数据库。NoSQL是基于键值对的,而且不需要经过SQL层的解析,数据之间没有耦合性,性能非常高。
非关系型数据库又可细分如下。
键值存储数据库:代表有Redis、Voldemort和Oracle BDB等。
列存储数据库:代表有Cassandra、HBase 和Riak等。
文档型数据库:代表有CouchDB和MongoDB等。
图形数据库:代表有Neo4J、InfoGrid 和Infinite Graph等。 -
为什么要有非关系型数据库?/ 非关系型数据库的较比关系型数据库的优势?
对于爬虫的数据存储来说,一条数据可能存在某些字段提取失败而缺失的情况,而且数据可能随时调整。另外,数据之间还存在嵌套关系。如果使用关系型数据库存储,一是需要提前建表,二是如果存在数据嵌套关系的话,需要进行序列化操作才可以存储,这非常不方便。如果用了非关系型数据库,就可以避免一些麻烦,更简单高效。
MongoDB
-
MongoDB是由C++语言编写的非关系型数据库,是一个基于分布式文件存储的开源数据库系统,其内容存储形式类似JSON对象,它的字段值可以包含其他文档、数组及文档数组,非常灵活。
-
安装MongoDB
连接MongoDB
指定数据库
指定集合
插入数据(insert()方法,insert_one()和insert_many())
查询(find_one()或find()、比较符号:大于$gt、正则匹配查询)
计数(count())
排序(sort())
偏移(skip()、limit())
更新(update())
删除(remove()、delete_one()、delete_many())
其他操作
Redis存储
- Redis是-个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构,使用也非常简单。