3.3 保存爬虫数据至Excel

在从Web上爬取数据以后,我们需要对数据进行保存,以便后续进一步的数据分析。由于数据库的读写速度很快,所以一般爬虫数据会存储在数据库中,除此之外,简单的爬虫数据可以保存在文本,Json文件或者Excel文件中。本文为学习崔庆才爬虫教程总结。

1.保存数据至TXT文本中

跟其他编程语言一样,保存txt文本,只需打开一个文本,指定读写方式,写入数据,然后关闭即可。在Python中还可以用with open方式来简化。

 with open('save_data_to_txt.txt', 'a', encoding='utf-8') as f:
        f.write('\n'.join([question, author, answer]))

下面给一个爬取知乎数据的例子,爬取知乎探索页面,获取问题作者答案三部分。

import requests
from pyquery import PyQuery as pq

base_url = 'https://www.zhihu.com/explore'

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36',
}

html = requests.get(base_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()
    with open('zhihu-explore.txt', 'a', encoding='utf-8') as f:
        f.write('\n'.join([question, author, answer]))
        f.write('\n' + '=' * 50 + '\n')

注:文本打开方式可以随意组合如下。

  • r,以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。

  • rb,以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。

  • r+,打开一个文件用于读写。文件指针将会放在文件的开头。

  • rb+,以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。

  • w,打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

  • wb ,以二进制格式打开一个文件只用于写入。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

  • w+, 打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

  • wb+,以二进制格式打开一个文件用于读写。如果该文件已存在则将其覆盖。如果该文件不存在,创建新文件。

  • a,打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。

  • a+,打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写。

  • ab+,以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写。

2. 保存数据至JSON文件

Json,全称为 JavaScript Object Notation, 也就是 JavaScript 对象标记,通过对象和数组的组合来表示数据,构造简洁但是结构化程度非常高,它是一种轻量级的数据交换格式。

2.1. 对象和数组

在 JavaScript 语言中,一切都是对象。因此,任何支持的类型都可以通过 Json 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型。

  • 对象,对象在 JavaScript 中是使用花括号 {} 包裹起来的内容,数据结构为 {key1:value1, key2:value2, ...} 的键值对结构,与Python中的字典结构一致。在面向对象的语言中,key 为对象的属性,value 为对应的值。键名可以使用整数和字符串来表示。值的类型可以是任意类型。

  • 数组,数组在 JavaScript 中是方括号 [] 包裹起来的内容,数据结构为 ["java", "javascript", "vb", ...] 的索引结构。在 JavaScript 中,数组是一种比较特殊的数据类型,它也可以像对象那样使用键值对,但还是索引使用得多。同样,值的类型可以是任意类型。

所以一个 Json 对象可以写为如下形式:

[{
    "name": "Bob",
    "gender": "male",
    "birthday": "1992-10-18"
}, {
     "name": "Selina",
    "gender": "female",
    "birthday": "1995-10-18"
}]

由中括号包围的就相当于列表类型,列表的每个元素可以是任意类型,在示例中它是字典类型,由大括号包围。Json 可以由以上两种形式自由组合而成,可以无限次嵌套,结构清晰,是数据交换的极佳方式

2.2 读取Json

Python 为我们提供了简单易用的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'
}]
'''
data = json.loads(str)

运行结果:

json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 3 column 5 (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)

运行结果:

[{'name': 'Bob', 'gender': 'male', 'birthday': '1992-10-18'}, {'name': 'Selina', 'gender': 'female', 'birthday': '1995-10-18'}]

以上是读取 Json 文件的方法。

2.3 写入Json

我们可以调用 dumps() 方法来将 Json 对象转化为字符串。

例如我们将刚上例中的列表重新写入到文本。

import json

data = [{
    'name': 'Bob',
    'gender': 'male',
    'birthday': '1992-10-18'
}]
with open('data.json', 'w') as file:
    file.write(json.dumps(data))

利用 dumps() 方法我们可以将 Json 对象转为字符串,然后再调用文件的 write() 方法即可写入到文本,结果如下:

[{"name": "Marco", "gender": "male", "birthday": "1992-10-18"}]

如果我们想保存 Json 的格式,可以再加一个参数 indent,代表缩进字符个数。

with open('data.json', 'w') as file:
    file.write(json.dumps(data, indent=2))

写入结果如下:

[
  {
    "name": "Marco",
    "gender": "male",
    "birthday": "1992-10-18"
  }
]

这样得到的内容会自动带有缩进,格式会更加清晰。

另外如果 Json 中包含中文字符,例如我们将之前的 Json 的部分值改为中文,再用之前的方法写入到文本。

import json

data = [{
    'name': '王伟',
    'gender': '男',
    'birthday': '1992-10-18'
}]
with open('data.json', 'w') as file:
    file.write(json.dumps(data, indent=2))

写入结果如下:

[
  {
    "name": "\u738b\u4f1f",
    "gender": "\u7537",
    "birthday": "1992-10-18"
  }
]

可以看到中文字符都变成了 Unicode 字符,这并不是我们想要的结果。

为了输出中文,我们还需要指定一个参数 ensure_asciiFalse,另外规定文件输出的编码为utf-8

with open('data.json', 'w', encoding='utf-8') as file:
    file.write(json.dumps(data, indent=2, ensure_ascii=False))

写入结果如下:

[
  {
    "name": "王伟",
    "gender": "男",
    "birthday": "1992-10-18"
  }
]

总结:

  • Python中json对象其实就是一个字典,获取key对应的值的时候,可以使用dict['key']得到,更推荐使用字典的函数get()函数获取,因为它会返回值,也可以指定默认值

  • 字符串类型转换为json对象时,使用loads()函数

  • json对象转换为字符串时,使用dumps()函数

3. 读写Excel文件

使用openpyxl库进行读写excel文件,首先需要在官网下载安装该库。

3.1 读取excel文件

>>> from openpyxl import load_workbook
>>> wb = load_workbook(filename = 'empty_book.xlsx')
>>> sheet_ranges = wb['range names']
>>> print(sheet_ranges['D18'].value)

即可读取empty_book.xlsx表格中range names对应的sheet,打印出列D18的值。

3.2 写入excel文件

from openpyxl import Workbook
from openpyxl.compat import range
from openpyxl.utils import get_column_letter

# 创建一个workbook对象
wb = Workbook()

# 每个workbook至少有一个sheet,通过openpyxl.workbook.Workbook.active()属性来获取该sheet
ws1 = wb.active

# 也可以通过openpyxl.workbook.Workbook.create_sheet()来创建一个sheet
ws2 = wb.create_sheet('新sheet')
# 重命名sheet名称
ws2.title = 'sheet_rename'

# 设置ws2 sheet的tab颜色
ws2.sheet_properties.tabColor = "1072BA"

# 写A1单元格内容为hello,A2内容为10
ws2['A1'] = 'Hello'
ws2.cell(1,2,10)

# 最后需要保存workbook对象,指定文件名,输出excel文件
wb.save('final_one.xlsx')

Last updated