保存的形式可以多种多样,最简单的形式是 接保存为文本文件,如 TXT、JSON、CSV等。还可以保存到数据库中,如关系型数据库 MySQL ,非关系型数据库 MongoDB、Redis等。
一、TXT文本存储
- 基本实例:
-
可以用 requests 将网页源代码获取下来,然后使用 pyquery 解析库解析,接下来将提取的标题、 回答者、 回答保存到文本,代码:
import requests from pyquery import PyQuery as pq url = 'https://www.zhihu.com/explore' headers = { 'User-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 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() answers = pq(item.find('.content').html()).text() file = open('explore.txt', 'a', encoding='utf-8') file.write(' '.join([question, author, answers])) file.write(' '+'='*50+' ') file.close()
用 requests 提取知乎的"发现"页面,然后将热门话题的问题、回答者、答案全文提取出来,然后利用 Python 提供的open()方法打开一个文本文件,获取一个文件操作对象,这里赋值为 file,接着利用 file 对 象的 write()方法将提取的内容写入文件,最后调用 ιlose()方法将其关闭,这样抓取的内容即可成功 写入文本中了。
-
- 打开方式
-
open()方法的第二个参数设置成了 a,这样在每次写入文本时不会清空源文件, 而是在文件末尾写入新的内容,这是一种文件打开方式。
-
r:以只读方式打开文件。 文件的指针将会放在文件的开头。 这是默认模式。
- rb:以二进制只读方式打开一个文件。 文件指针将会放在文件的开头。
- r+:以读写方式打开一个文件, 文件指针将会放在文件的开头。
- rb+:以二进制读写方式打开一个文件。 文件指针将会放在文件的开头。
- w:以写入方式打开一个文件。 如果该文件已存在,则将其覆盖。 如果该文件不存在,则创建新文件。
- wb:以二进制写入方式打开一个文件。 如果该文件已存在,则将其覆盖。 如果该文件不存 在, 则创建新文件
- w+:以读写方式打开一个文件。 如果该文件已存在,则将其覆盖。 如果该文件不存在,则创 建新文件
- wb+:以二进制读写格式打开一个文件。 如果该文件已存在, 则将其覆盖。 如果该文件不存 在, 则创建新文件。
- a: 以追加方式打开一个文件。 如果该文件已存在,文件指针将会放在文件结尾, 如果该文件不存在, 则创建新文件来写入。
- ab:以二进制追加方式打开一个文件。 如果该文件已存在,则文件指针将会放在文件结尾,如果该文件不存在,则创建新文件来写入
- a+:以读写方式打开一个文件。 如果该文件已存在,文件指针将会放在文件的结尾。 文件打 开时会是追加模式。 如果文件不存在,则创建新文件来读写。
- ab+:以二进制追加方式打开一个文件。 如果该文件已存在,则文件指针将会放在文件结尾。 如果该文件不存在,则创建新文件用于读写
-
-
- 简化写法
- 使用 with as 语法。在 with 控制块结束时,文件会自动关闭,不需要再调用 close()方法。示例:
with open('explore.txt', 'a', encoding='utf-8') as file: file.write(' '.jason([question,author,answers])) file.write(' '+'='*50+' ')
如果想保存时将原文清空,那么可以将第二个参数改写为 w,示例:
with open('explore.txt', 'w', encoding='utf-8') as file: file.write(' '.jason([question,author,answers])) file.write(' '+'='*50+' ')
- 使用 with as 语法。在 with 控制块结束时,文件会自动关闭,不需要再调用 close()方法。示例:
二、Json文件存储
JSON,全称为 JavaScript Object Notation, 是 JavaScript对象标记, 通过对象和数组的组合来表示数据,构造简洁但结构化程度非常高,是一种轻量级的数据交换格式。
- 对象和数组
-
在 JavaScript语言中,一切都是对象。 因此,任何支持的类型都可以通过 JSON来表示,如:字符串、数字、对象、数组等,对象和数组是比较特殊且常用的两种类型
- 对象:在 JavaScript 中是使用花括号{}包裹起来的内容,数据结构为{key1: value1, key2: value2,… }的键值对结构。 在面向对象的语言中, key 为对象的属性, value 为对应的值。 键名可以使用整数和字符串来表示。 值的类型可以是任意类型。
- 数组:数组在 JavaScript 中是方括号[]包裹起来的内容,数据结构为["java","javascript","vb",.... ]的索引结构。在JavaScript 中, 数组是一种比较特殊的数据类型,也可以像对象那样使用键值对,但还是索引用得多。 同样,值的类型可以是任意类型。
- 所以,一个JSON对象可以写成如下形式:
[{ "name": "Bob", "gender": "male", "birthday": "1992-10-18",}, {"name": "Snlina", "gender": "female", "birthday": "1995-10-18" }]
由中括号包围的就相当于列表类型,列表中的每个元素可以是任意类型,这个示例中它是字典类型,由大括号包围。
JSON 可以由以上两种形式自由组合而成,可以无限次嵌套,结构清晰,是数据交换的极佳方式。
- 所以,一个JSON对象可以写成如下形式:
-
- 读取JSON
-
可以调用 JSON 库 的 loads()方法将 JSON 文本字符串转为 JSON对象,可以通过 dumps()方法将 JSON 对象转为文本字符串。
例:一段 JSON 形式的字符串,是 str 类型,可以用 Python 将其转换为可操作的数据结构——列表或字典:import json str = ''' [{ "name":"Bob", "gender":"male", "birthday": "1992-10-18" },{ "name": "Selina", "gender":"female", "birthday": "1995-10-18" }] ''' print(type(str)) data = json.loads(str) print(data) print(type(data)) 输出:
<class 'str'> [{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-18'}, {'name': 'Selina', 'gender': 'female', 'birthday': '1995-10-18'}] <class 'list'>
使用 loads()方法将字符串转为 JSON 对象。 由于最外层是中括号,所以最终的类型是列表类型。
针对列表,可以用索引来获取对应的内容。 如,想取第一个元素里的 name 属性, 可以使用如下方式:
data[0]['name'] data[0].get('name') 输出: Bob
中括号加 0 索引,可以得到第一个字典元素,再调用键名即可得到相应的键值。 获取键值时有两种方式:一种是中括号加键名 ,另一种是通过 get()方法传人键名。 推荐使用 get()方法,这样如果键名不存在,则不会报错,会返回 None。 另外, get()方法还可以传入第二个参数(即 .. 默认值),示例:
data[0].get('age') data[0].get('age',25) 输出: None 25
尝试获取年龄 age,在原字典中该键名不存在,此时默认会返回 None。 如果传入第 二个参数( 即默认值),那么在不存在的情况下返回该默认值。
注意:JSON 的数据需要用双引号来包围 , 不能使用单引号。 例:若使用如下形式表示,则会出现错误:
import json str = ''' [{ 'name':'Bob', 'gender':'male', 'birthday': '1992-10-18' },{ 'name': 'Selina', 'gender':'female', 'birthday':'1995-10-18' }] ''' data = json.loads(str) 输出: json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 2 column 8 (char 8)
出现 JSON解析错误提示。注意:JSON 字符串的表示需要用双引号,否则 loads()方法会解析失败。
-
从 JSON 文本中读取内容,例:有一个 data.json 文件,内容是刚才定义的 JSON 字符串,可以先将文本文件内容读出,然后再利用 loads()方法转化:
import json with open('data.json','r') as file: str = file.read() data = json.loads(str) print(data)
-
-
输出JSON
-
可以调用 dumps()方法将 JSON 对象转化为字符串。 例:将例中的列表重新写入文本:
import json data = ''' [{ "name": "Bob", "gender": "male", "birthday": "1992-10-18" }] ''' with open('data.json','a',encoding='utf-8') as f: f.write(json.dumps(data))
利用 dumps()方法,可以将 JSON 对象转为字符串,然后再调用文件的 write()方法写入文本
-
如果想保存 JSON 的格式,可以再加一个参数 indent,代表缩进字符个数。 示例:
with open('data.json','a',encoding='utf-8') as f: f.write(json.dumps(data,indent=2))
这样得到的内容会自动带缩进,格式更加清晰。
-
如果 JSON 中包含中文字符,需要指定参数 ensure_ascii 为 False,还要规定文件输出的编码:
with open('data.json','w',encoding='utf-8') as f: f.write(json.dumps(data,indent=2,ensure_ascii= False))
-
三、CSV文件存储
- 写入
-
import csv with open('data.csv','w') as csvfile: writer =csv.writer(csvfile) writer.writerow(['id','name','age']) writer.writerow(['10001','Mike',20]) writer.writerow(['10002','Bob',22]) writer.writerow(['10003','Jordan',21])
首先打开 data.csv 文件,然后指定打开的模式为 w(写入),获得文件句柄,随后调用 csv 库 的 writer()方法初始化写人对象,传入该句柄,然后调用 writerow()方法传入每行的数据即可完成写入。
写人的文本默认以逗号分隔,调用一次 writerow()方法即可写入一行数据。 -
如果想修改列与列之间的分隔符,可以传入 delimiter 参数,代码如下:
import csv with open('data.csv','w') as csvfile: writer =csv.writer(csvfile,delimiter='') --snip--
里在初始化写入对象时传入 delimiter 为空格, 此时输出结果的每一列就是以空格分隔。
-
也可以调用 writerows()方法同时写入多行, 此时参数需要为二维列表,例:
import csv with open('data.csv','w') as csvfile: writer =csv.writer(csvfile) writer.writerow(['id','name','age']) writer.writerow(['10001','Mike',20],['10002','Bob',22],['10003','Jordan',21])
输出内容相同。
-
是一般情况下,爬虫爬取的都是结构化数据,一般会用字典来表示。在 CSV 库中也提供了字典的写入方式,示例:
import csv with open('data.csv','w') as csvfile: filenames = ['id','name','age'] writer = csv.DictWriter(csvfile,fieldnames=filenames) writer.writeheader() writer.writerow({'id':'10001','name':'Mike','age':20}) writer.writerow({'id':'10002','name':'Bob','age':22}) writer.writerow({'id':'10003','name':'Jordan','age':21})
先定义 3 个字段,用 fieldnames 表示,再将其传给 DictWriter 来初始化一个字典写人对 象,接着调用 writeheader()方法先写人头信息,然后再调用 writerow()方法传人相应字典。
-
如果想追加写人,可以修改文件的打开模式,即将 open()函数的第二个参数改成 a,示例:
import csv with open('data.csv','a') as csvfile: filenames = ['id','name','age'] writer = csv.DictWriter(csvfile,fieldnames=filenames) writer.writerow({'id':'10004','name':'Durant','age':22})
-
如果要写入中文内容,可能会遇到字符编码的问题,此时需要给 open()参数指定编码格式。 例如,再写入一行包含中文的数据,代码需要改写:
import csv with open('data.csv','a',encoding='utf-8') as csvfile: filenames = ['id','name','age'] writer = csv.DictWriter(csvfile,fieldnames=filenames) writer.writerow({'id':'10005','name':'王伟','age':22})
这里需要给 open()函数指定编码,否则可能发生编码错误。
-
如果接触过 pandas 库的话,可以调用 DataFrame 对象的 to_csv()方法来将数据写人 csv 文件中。
-
-
读取
-
同样可以使用 csv 库来读取 csv 文件。 如,将写入的文件内容读取出来,如下:
import csv with open('data.csv','r',encoding='utf-8') as csvfile: reader =csv.reader(csvfile) for row in reader: print(row)
这里构造的是 Reader 对象,通过遍历输出了每行的内容,每一行都是一个列表形式。 注意, 如果 csv 文件中包含中文的话,还需要指定文件编码。
-
如果知道 pandas库,可以利用 read_csv()方法将数据从 csv 中读取出来,如:
import pandas as pd df=pd.read_csv('data.csv') print(df)
在做数据分析的时候,此种方法用得比较多,也是一种比较方便地读取 csv 文件的方法。
-