网络爬虫 —— 配置文件解析

网络爬虫 ------ 配置文件解析

ini

iniInitialization File )顾名思义,主要用于初始化应用程序。这是一种无固定标准格式的配置文件,且文件没有固定的后缀,常见的有 .ini.conf.cfg,甚至还可以是 .txt。主要用于 Windows 操作系统上,但也有很多程序会采用 INI 文件作为配置文件使用。

文件格式

ini 格式在不同的解析器中会具有不同的特征,比较固定的是:

  • 节:使用中括号包裹的字符串,其后面的所有参数(到下一个小结的开始之前),都归属于该节
ini 复制代码
[section]
  • 参数:以键值对的方式设置参数值,即程序中需要访问的参数所对应的值
ini 复制代码
name = value
  • 注释:以 ;# 开头的行表示的是注释信息
ini 复制代码
; comment text
# comment line
  • 大小写不敏感
  • section 中的参数顺序与其在文件中出现的顺序无关

除了上面的基本特征之外,不同的解析器可能还提供了

  • 全局属性
  • 冒号(:)或空格分隔的键值对声明方式
  • 层次或嵌套形式的 section
  • 重复参数名称
  • 参数引用等

示例

demo.ini 中输入下面的文本并保存

ini 复制代码
[DEFAULT]
; default directory
path = /usr/bin
# P value cut off
cut_off = 0.05

[PROGRAMS]
python = /path/to/python
R = /path/to/R

R 解析文件

R 中,我们使用 configr 包来读写常见的 4 种配置文件,该包是对 inijsonliteyamlRcppTOML4 个包的封装,内核还是通过调用它们来对不同格式的文件进行解析的。

首先需要安装该包

r 复制代码
# CRAN
install.packages('configr')
# Github
# install.packages("devtools")
devtools::install_github("Miachol/configr")

导入包

r 复制代码
library(configr)  # version 0.3.5
读取文件

使用 read.config 函数即可读取文件

r 复制代码
conf <- read.config("demo.ini")
class(conf)
# [1] "list"
str(conf)
# List of 2
#  $ DEFAULT :List of 2
#   ..$ path   : chr "/usr/bin"
#   ..$ cut_off: chr "0.05"
#  $ PROGRAMS:List of 2
#   ..$ python: chr "/path/to/python"
#   ..$ R     : chr "/path/to/R"

该函数返回一个嵌套的 list 对象,第一层为节,第二层为参数值。可以在程序中以访问 list 的形式获取配置文件中的参数信息。

该解析器提供了一些额外的特征,通过预先在文件中设置一些占位符,并在占位符中添加相应的命令,然后在读取时进行解析。主要通过几个参数来控制

参数 功能
extra.list 用字符串 list 来替换 {``{}} 包裹的字符
other.config 导入其他配置文件
rcmd.parse 是否将 @>@ 包裹的字符串作为 R 命令执行
bash.parse 是否将 #># 包裹的字符串作为 bash 命令执行
glue.parse 是否执行 glue 命令
glue.flag 设置 glue 命令的起始标志,默认为 !!glue

demo.ini 文件里面写入如下内容

ini 复制代码
[DEFAULT]
debug = {{debug}}
[other]
name = {{others:name}}
[rcmd_parse]
time = @>@ Sys.Date() @<@
[bash_parse]
home = #>#echo $HOME#<#
[mulitple_parse]
multi = @>@str_replace('config','g$','gr')@<@, #>#echo configr#<#, {{others:name}}
[glue_parse]
range_chr = !!glue {1:10}
range_num = !!glue_numeric {1:10}

other.ini 中添加如下内容

ini 复制代码
[others]
name = Tom

读取并解析

r 复制代码
conf <- read.config("demo.ini", extra.list = list(debug = "true"), 
            other.config = "other.ini", rcmd.parse = TRUE,
            bash.parse = TRUE, glue.parse = TRUE)
str(conf)
# List of 6
#  $ DEFAULT       :List of 1
#   ..$ debug: chr "true"
#  $ other         :List of 1
#   ..$ name: chr "Tom"
#  $ rcmd_parse    :List of 1
#   ..$ time: chr "2022-10-07"
#  $ bash_parse    :List of 1
#   ..$ home: chr "/Users/dengxsh"
#  $ mulitple_parse:List of 1
#   ..$ multi: chr "configr, configr, Tom"
#  $ glue_parse    :List of 2
#   ..$ range_chr: chr [1:10] "1" "2" "3" "4" ...
#   ..$ range_num: num [1:10] 1 2 3 4 5 6 7 8 9 10
写入文件

一般情况下,都是手动创建配置文件,很少会将数据直接写入到配置文件中,除非需要自动化创建默认的配置文件。我们可以将 list 对象写入到配置文件中,例如

r 复制代码
data <- list(
  user = list(username = "admin", passwd = "123456"),
  info = list(type = "init")
)
write.config(config.dat = data, file.path = "test.ini", write.type = "ini")

test.ini 中将会看到如下内容

ini 复制代码
[user]
username=admin
passwd=123456

[info]
type=init

Python 解析文件

Python 中,我们可以使用标准库中的 configparser 包来解析 ini 格式的配置文件。例如,创建如下名为 setting.ini 的配置文件

ini 复制代码
[default]
path = /default/path

[server]
address = 127.0.0.1
port = 8080

导入包并创建配置解析器

python 复制代码
import configparser

config = configparser.ConfigParser()

使用配置解析器来读取配置文件

python 复制代码
config.read('setting.ini')
# ['setting.ini']
config.sections()          # 获取所有 section
# ['default', 'server']
config['default']['path']  # 获取属性值
# '/default/path'

我们可以将配置解析器当做字典来使用,其具有字典的一般行为,但也存在一些差异。例如,解析器支持同时传入 section 和属性名称来获取属性值。

配置解析器并不会自动推测配置文件中值的类型,总是将它们存储为字符串,我们可以手动转换或使用解析器提供的便捷函数

python 复制代码
config['server'].get('port')
# '8080'
int(config.get('server', 'port'))
# 8080
config.getint('server', 'port')
# 8080

除了 getint 之外,还有 getfloatgetbooleanget 函数也可以提供默认值。

python 复制代码
config['server'].get('name', 'Tom')
# 'Tom'

该解析器也支持一些特性,例如

  • 支持冒号 : 声明键值对
  • 支持配置文件内插值,值可以在被 get 调用返回之前进行预处理

我们在配置文件中 setting.ini 添加如下内容

ini 复制代码
[Paths]
home_dir: /Home/user
my_dir: %(home_dir)s/my
my_pictures: %(my_dir)s/Pictures
percent: 80%%

其中 %(home_dir)s 将会替换为 home_dir 属性的值,%% 会替换为百分号 %

python 复制代码
config.read('setting.ini')
config.sections()
# ['default', 'server', 'Paths']
config.get('Paths', 'my_dir')
# '/Home/user/my'
config.get('Paths', 'my_pictures')
# '/Home/user/my/Pictures'
config.get('Paths', 'percent')
# '80%'
自定义解析器

ini 格式的变种非常多,默认的解析器只具备一些常用的功能,我们可以在创建解析器是指定一些行为。例如

  • allow_no_value 参数可用于指明是否接受不带值的属性
  • delimiters 用于指定分隔键和值的子字符串
  • comment_prefixes 注释的前缀
  • interpolation 用于指定插值方式,可以使用 ExtendedInterpolation 类指定更高级的语法,或使用 None 来禁用插值语法

扩展插值插值可以跨越多个层级,使用方式形如 ${section:option},如果省略 section:,则表示作用于当前小节。例如,对如下配置文件进行解析

ini 复制代码
[default]
path = /Home/user

[Paths]
my_dir: ${default:path}/my
my_pictures: ${my_dir}/Pictures

[Others]
money: $$80
python 复制代码
config = configparser.ConfigParser(
    interpolation=configparser.ExtendedInterpolation()
)
config.read('setting.ini')
config.get('Paths', 'my_dir')
# '/Home/user/my'
config.get('Others', 'money')
# '$80'
写入文件

我们可以使用类似于为字典赋值的方式,为解析器添加配置,例如

python 复制代码
config['Others'] = {
    'name': 'Tom',
    'age': 28
}
config['default']['salary'] = '10000'

写出解释器对象的值

python 复制代码
with open('setting.ini', 'w') as configfile:
    config.write(configfile)

setting.ini 中的内容将变为

ini 复制代码
[default]
path = /Home/user
salary = 10000

[Paths]
my_dir = ${default:path}/my
my_pictures = ${my_dir}/Pictures

[Others]
name = Tom
age = 28

json

jsonJavaScript Object Notation)是一种开放的标准文件格式,易于机器创建和解析,是一种实用的轻量级数据交换格式。虽然它起源于 JavaScript,但它独立于 JavaScript,现在很多语言都提供了相应的包来用于处理这种类型的数据。一些生物数据库也会将数据存储为 json 格式,便于解析。

文件格式

json 文件的格式包含两种,可以理解为将 Python 中的字典或列表直接存储到文件中,例如

字典形式的文件格式(dict.json

json 复制代码
{
  "name": ["John"],
  "age": [19],
  "children": ["Catherine", "Thomas", "Trevor"]
}

列表形式的文件格式(list.json

json 复制代码
[
    {"name": "Tom", "age": 18},
    {"name": "Jim", "age": 20}
]

R 解析文件

R 中有几个用于解析 json 的包,上面的 configr 也可以,使用方法一样,只需将文件类型参数改为 json 即可

r 复制代码
read.config("dict.json", file.type = "json")

读取 json 文件时,会根据 json 里面的数据内容自动将其转换为 listdata.frame 类型,例如

r 复制代码
j <- read.config("Downloads/dict.json", file.type = "json")
str(j)
# List of 3
#  $ name    : chr "John"
#  $ age     : int 19
#  $ children: chr [1:3] "Catherine" "Thomas" "Trevor"
read.config("Downloads/list.json", file.type = "json")
#   name age
# 1  Tom  18
# 2  Jim  20

可以将 listdata.frame 数据转换为 json 对象,pretty 参数用于控制是否美化输出

r 复制代码
d <- data.frame(
  name = c("Tom", "Jim"),
  age = c(18, 20)
)
write.config(d, "Downloads/list.json", write.type = "json")
# [1] TRUE

Python 解析文件

Python 标准库提供了 json 包可以用于处理 json 数据,可以将文件其转换为内置的 listdict 类型对象。

使用 load 函数导入文件对象中的数据

python 复制代码
import json

json.load(open("dict.json"))
# {'name': 'John', 'age': 19, 'children': ['Catherine', 'Thomas', 'Trevor']}

或者使用 dump 函数将数据写入到文件中

python 复制代码
data = [
    {"name": "Tom", "age": 18},
    {"name": "Jim", "age": 20}
]
json.dump(data, open('list.json', 'w'))

可直接将字典或列表数据写入文件中,导入数据时也会自动解析为字典或列表类型的对象。这两个函数分别有一个加 s 的版本,用于处理对象与字符串之间的转换,如

python 复制代码
json.dumps(data)
# '[{"name": "Tom", "age": 18}, {"name": "Jim", "age": 20}]'
d = '{"name": "John", "age": 19, "children": ["Catherine", "Thomas", "Trevor"]}'
json.loads(d)
# {'name': 'John', 'age': 19, 'children': ['Catherine', 'Thomas', 'Trevor']}

注意,将 json 格式的字符串转换为对象,其中的字符串需要为双引号。同时,在转换时也可以设置输出格式,设置缩进的空格数 (indent),或者对键进行排序(sort_keys

python 复制代码
j = json.dumps(data, indent=4, sort_keys=True)
print(j)
# [
#     {
#         "age": 18,
#         "name": "Tom"
#     },
#     {
#         "age": 20,
#         "name": "Jim"
#     }
# ]

yaml

YAMLYet Another Markup Language) 是一种数据序列化的语言,以数据为中心,且更注重于数据的描述和展示。其可读性高、易于理解,可灵活地与其他编程语言结合使用。相较于前面两种文件格式,其既可以定义复杂的结构化数据,也支持注释、属性引用等,还可以引用其他配置文件,降低代码的冗余。

基本语法

yaml 中数据类型的概念与其他高级编程语言类似,包括三种数据类型

  • 数组(列表)
  • 映射(字典或哈希)
  • 标量(包括字符串、整数和浮点数等)。

yaml 是大小写敏感的,使用与 Python 类似的缩进(使用空格而不是 TAB 控制缩进)来表示数据的层级关系(一般不使用括号或引号)、键值对使用冒号作为分隔标识(冒号后面必须紧跟一个空格)、# 符号之后的内容作为注释不会被解析。文件后缀推荐使用 .yaml,也有人会使用 .yml 作为文件后缀。

其与 json 格式非常类似,可以认为是 json 的超集,在 yaml 文件中也可以使用 json 的语法。

例如,我们定义如下关系映射数据

yaml 复制代码
server: # web server
  name: work
  host: 127.0.0.1
  port: 8080

注意,缩进的空格数没有限制,只要保证同一层级中所有的元素左对齐即可。上面的内容将被解析为

python 复制代码
{'server': {'name': 'work', 'host': '127.0.0.1', 'port': 8080}}

上面文件的内容也可以写在一行,但需要使用花括号包裹起来

yaml 复制代码
server: {name: work, host: 127.0.0.1, port: 8080}

数组使用横杠 - 标识每一个元素(横杠后面必须紧跟一个空格),例如

yaml 复制代码
address_list:
- 127.0.0.1
- 192.168.0.1
- 0.0.0.0

上面的内容将被解析为

python 复制代码
{'address_list': ['127.0.0.1', '192.168.0.1', '0.0.0.0']}

列表也可以写在一行内,但是需要用方括号包裹起来

yaml 复制代码
address_list: [127.0.0.1, 192.168.0.1, 0.0.0.0]

如果要在一个文件中编写多个文档,可以使用三横杠 --- 来标识文档的开始,三个点 ... 来标识文档的结束。例如

yaml 复制代码
---
name: Sam
age: 20
weight: 60kg
...
---
name: Ban
age: 21
weight: 55kg
...

yaml 文件中,字符串并不需要用使用引号,如果存在转义字符可以用双引号将字符串包裹,而单引号只能用两个单引号来转义自身,即 'don''t' 会被解析为 "don't"。而对于多行文本,有两个字符可以指定不同的行为

  • |:多行内容之间保留插入的换行符
  • >:多行内容之间使用空格连接
yaml 复制代码
preserve: |
  Beautiful is better than ugly.
  Explicit is better than implicit.
  Simple is better than complex.
  
fold: >
  Complex is better than complicated.
  Flat is better than nested.
  Sparse is better than dense.

两种模式中字符串将被解析为

python 复制代码
{'preserve': 'Beautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\n',
 'fold': 'Complex is better than complicated. Flat is better than nested. Sparse is better than dense.'
}

变量引用

如果要引用前面定义的变量,可以使用 & 先对元素进行锚定,在需要引用的地方使用 * 来引用之前锚定的内容,可以在锚定时指定相应的名称。例如

yaml 复制代码
Sam: 
 name: Sam
 age: &a 20
 hobby: 
 - &s1 football
 - sing song
  
Ben:
  name: Ben
  age: *a
  hobby: 
  - *s1

对于键值对映射的元素需要在冒号之后添加锚定,对于列表元素,则在值之前添加锚定。

锚定与合并标签(<<)搭配使用,可以将任意数据进行合并,例如

yaml 复制代码
person: &base
    name: null
    age: 0
sam:
    name: Sam
    <<: *base     # 引用锚点,实例化时会自动展开
    hobby:        # 添加额外的属性
      - football
tom:
    <<: *base     # 引用锚点,实例化时会自动展开
    name: Tom     # 覆盖属性,相同属性是允许的但只会取最后一次的值
    age: 10

上面的文件将被解析为

json 复制代码
{
	'person': {'name': None, 'age': 0},
	'sam': {'name': 'Sam', 'age': 0, 'hobby': ['football']},
	'tom': {'name': 'Tom', 'age': 10}
}

数据类型

一般我们读取文件时,会自动解析数据的类型,我们也可以在文件中使用双感叹号加类型名称的方式(如 !!str)强制转换数据的类型,主要包含如下类型

标记 类型 标记 类型
!!int 整数类型 !!float 浮点类型
!!bool 布尔类型 !!str 字符串类型
!!binary 二进制字符串 !!timestamp 日期时间类型
!!null 空值 !!set 集合类型
!!omp,!!pairs 配对列表 !!map 键值对
!!seq 列表

例如,对于下面的文件内容

yaml 复制代码
Stu: !!map
  name: !!str Sam
  age: !!float 20
  boy: !!bool yes
  
Info: !!seq
- name
- age
- boy

将被解析为

python 复制代码
{'Stu': {'name': 'Sam', 'age': 20.0, 'boy': True},
 'Info': ['name', 'age', 'boy']}

R 解析文件

R 中操作 yaml 文件,也是使用 configr 包的 read.config 函数。对于如下文件 demo.yaml

yaml 复制代码
- Sam: 
    age: 19
    hobby: 
     - football
     - sing song
   
- Tom:
    age: 19
    hobby: 
     - football
     - TV

文件将会被解析为一个 list,如果在读取时抛出了 readLines 函数的警告信息,可以忽略,或者在文件最后一行添加一个空行即可。

r 复制代码
y <- read.config("demo.yaml", file.type = "yaml")
str(y)
# List of 2
# $ :List of 1
# ..$ Sam:List of 2
# .. ..$ age  : int 19
# .. ..$ hobby: chr [1:2] "football" "sing song"
# $ :List of 1
# ..$ Tom:List of 2
# .. ..$ age  : int 19
# .. ..$ hobby: chr [1:2] "football" "TV"

与前面类似,也可以将一个数据框写出为 yaml 文件

r 复制代码
write.config(d, "dump.yaml", write.type = "yaml")

dump.yaml 文件中的内容为

yaml 复制代码
name:
- Tom
- Jim
age:
- 18.0
- 20.0

Python 解析文件

Python 中我们可以使用第三方包 PyYAML 来解析 yaml 文件,可直接使用 pip 进行安装

bash 复制代码
pip install pyyaml  # version 6.0

如果文件中只有一个文档,可以使用 load 函数或 safe_load

python 复制代码
import yaml
from yaml import CLoader

yaml.load(open('demo.yaml'), CLoader)
# yaml.safe_load(open('demo.yaml'))
# [{'Sam': {'age': 19, 'hobby': ['football', 'sing song']}},
#  {'Tom': {'age': 19, 'hobby': ['football', 'TV']}}]

如果一个文件里面有多个文档,则需要使用 load_all,该函数将返回一个生成器

python 复制代码
for data in yaml.load_all(open('demo.yaml'), CLoader):
    print(data)

如果要将一个对象序列化为 yaml 文件,则可以使用 dump 函数

python 复制代码
data = {
    'name': 'Tom',
    'age': 30,
    'hobby': ['football', 'TV']
}
yaml.dump(data, open('dump.yaml', 'w'))

或者写入多个文档

python 复制代码
yaml.dump_all([data, 1, 2, 3], open('dump.yaml', 'w'))

TOML

TOMLTom's Obvious, Minimal Language) 的设计宗旨在于创建一种语义明确且易于阅读的最精简配置文件格式,其语法有点类似于 INI 文件格式,但它加了更多的规范来进行限定,这些规范包括一系列的数据类型:字符串、整数、浮点数、布尔值、日期时间、数组和表。其文件后缀为 .toml

基本语法

yaml 类似,toml 也是大小写敏感的,使用 # 来添加注释,也是以键值对的形式来定义属性,但是其不依赖缩进,且不属于属性名或属性值的空格都会被忽略掉。

键值对使用的是 = 来进行声明,例如

toml 复制代码
key = "value"  # this is first comment

键名可以是裸露的或使用引号包裹的字符串,即可分为裸键和引号键。有效的裸键必须为字符集 A-Za-z0-9_- 中的字符组成,引号键可以定义更加广泛的名称。注意,裸键加引号与不加引号本质是一样的,会被当做同一个属性,且同一个键不能被重复赋值。

toml 复制代码
a-b = "123"
123 = "abc"

键名可以是空字符串,但是不推荐使用,也没人会在实际项目中这么用。

键名中如果存在 . 号,表示嵌套层级,例如

toml 复制代码
person.name = 'Tom'
person.age = 20
person.'major' = 'chinese'

与下面的 json 表示同样的内容,会将 . 之后的名字作为子键名,与后面的表结构所表示的内容相似

json 复制代码
{'person': {'name': 'Tom', 'age': 20, 'major': 'chinese'}}
字符串类型

字符串可分为 4 种类型,分别用不同的符号来表示:

  • 基本字符串使用双引号("
  • 多行字符串使用三个双引号("""
  • 单行字面量使用单引号('
  • 多行字面量使用三个单引号('''

字符串的值只能是有效的 UTF-8 字符,例如

toml 复制代码
str1 = "abc\tbcd\n"
str2 = """
    Beautiful is better than ugly. \
    Explicit is better than implicit.
    Simple is better than complex.
"""

打印字符串之后,转移字符将会发挥作用,同时在多行字符串中,行尾的 \ 表示将两行连接起来,即忽略后面的换行符。

abc	bcd

    Beautiful is better than ugly. Explicit is better than implicit.
    Simple is better than complex.

对于字符串字面量

toml 复制代码
str3 = 'abc\tbcd\n'
str4 = '''
    a\tb\ncd \
    efg\nh
'''

所有字符不会被转义,而是原样输出

abc\tbcd\n
    a\tb\ncd \
    efg\nh
整数类型

整数类型的数据,可以使用 +- 来标识正负数,如果是较大的数值,可以使用 _ 来分隔数字提高可读性,也可以使用不同进制形式的数值。例如

toml 复制代码
a = 3
b = 1_000       # 千分位分隔
c = 0xDEADBEEF  # 十六进制
d = 0o12345    # 八进制
e = 0b101001    # 二进制

文件将被解析为

python 复制代码
{'a': 3, 'b': 1000, 'c': 3735928559, 'd': 5349, 'e': 41}
浮点类型

浮点类型的数值与整数类似,只是分为整数部分和小数部分,注意,两部分都不能省略。也支持科学计数法,下划线分隔等,例如

toml 复制代码
a = 3.1415
b = 2.1e12
c = inf
d = nan
e = 3.141_592_653

将被解析为

python 复制代码
{'a': 3.1415, 'b': 2100000000000.0, 'c': inf, 'd': nan, 'e': 3.141592653}
布尔与时间日期

布尔类型使用小写

toml 复制代码
a = true
b = false

时间日期格式

toml 复制代码
a = 09:32:03
b = 1997-07-01
c = 1941-08-14T08:23:10

Python 中会被解析为相应的时间日期格式

python 复制代码
{'a': datetime.time(9, 32, 3),
 'b': datetime.date(1997, 7, 1),
 'c': datetime.datetime(1941, 8, 14, 8, 23, 10)}
数组

这里的数据基本相当于 Python 中的 list 类型,可以混合任意类型的数据,元素之间多余的空格和换行都会被忽略。例如

toml 复制代码
ints = [1, 2, 3]
strs = ["a", "b"]
nest_arr = [
    [1, 2], 
    [3, 4, 5]
]
mix = [0.1, 1, 'a', [2, "b"]]

解析为

python 复制代码
{'ints': [1, 2, 3],
 'strs': ['a', 'b'],
 'nest_arr': [[1, 2], [3, 4, 5]],
 'mix': [0.1, 1, 'a', [2, 'b']]}
哈希表

哈希表或字典类型,使用方括号里面加表名的方式作为表头来声明,与 ini 中的节类似,其作用范围从该表头至下一个表头或文件结束。例如

toml 复制代码
[table]
name = "Tom"
age = 10

Python 中将被解析为嵌套字典

python 复制代码
{'table': {'name': 'Tom', 'age': 10}}

表名可以像键名一样,使用 . 来创建嵌套哈希表,例如

toml 复制代码
[table.person]
name = "Tom"
age = 10

解析为 table 表下面的 person 子表

python 复制代码
{'table': {'person': {'name': 'Tom', 'age': 10}}}

表中的内容通常放在一起定义,不能定义两个同名的表。其实本质上带 . 的键名也是创建嵌套的表,会将最后一个点之前的名称作为表名,并创建对应的表,最后一个键作为属性值。例如

toml 复制代码
table.person.chinese.fujian = '123'

会创建三个表,table 表里面包含 person 表,person 表里面包含 chinese

表的内容也可以写在同一行中,即内联表,将所有内容以一种紧凑的形式展现,类似于 json

toml 复制代码
[table]
person = {name='Tom', age=19}
python 复制代码
{'table': {'person': {'name': 'Tom', 'age': 19}}}

内联表也可以作为数组中的元素

toml 复制代码
human = [{name='Tom', age=11}, {name='Sam', age=20}]
python 复制代码
{'human': [{'name': 'Tom', 'age': 11}, {'name': 'Sam', 'age': 20}]}
表数组

顾名思义,即由表组合起来的数组,使用双方括号里面加表名的方式声明下面的表内容将作为数组中的一个元素,其中表名将作为数组的键,例如

toml 复制代码
[[table]]
name = 'T1'

[[table.subtable]]
name = 'ST1'

[[table.subtable]]
name = 'ST2'

[[table]]
name = 'T2'

被解析为

python 复制代码
{'table': [
    {'name': 'T1', 'subtable': [
        {'name': 'ST1'}, {'name': 'ST2'}
    ]},
    {'name': 'T2'}]
}

R 解析文件

我们前面使用的 configr 包只能读取 toml 文件,而无法写入,所以我们使用另一个包 blogdown,首先对包进行安装

r 复制代码
install.packages('blogdown')  # 1.13
blogdown::install_hugo()

安装成功后,读取 demo.toml 文件

toml 复制代码
[table.person]
name = "Tom"
age = 10

直接使用包名加两个冒号来访问包内的函数,可以不用导入包,

r 复制代码
blogdown::read_toml("demo.toml")
# $table
# $table$person
# $table$person$age
# [1] 10
# 
# $table$person$name
# [1] "Tom"

将数据写入到文件中

r 复制代码
data <- list(
  name = 'Tom', 
  age = 30, 
  hobby = c('football', 'TV')
)
blogdown::write_toml(data, "dump.toml")

Python 解析文件

Python 中用于解析 toml 文件的包比较多,如 tomlrtomlqtomltomlitomlkit 等,由于 Python 3.11 标准库中加入的 tomllib 包是从 tomli 包而来,所以我们使用 tomli 包来解析 toml 文件,保持兼容

安装包

bash 复制代码
pip install tomli  # version 2.0.1

解析文件,需要以二进制的方式打开文件

python 复制代码
import tomli

tomli.load(open('demo.toml', 'rb'))
# {'table': {'person': {'name': 'Tom', 'age': 10}}}

tomli 是一个只读取并解析文件的包,若要将数据写入到 toml 文件中,需要用到其对应的写入数据的版本 tomli_w

bash 复制代码
pip install tomli-w  # version 1.0.0

写入文件中

python 复制代码
import tomli_w

data = {
    'name': 'Tom', 
    'age': 30, 
    'hobby': ['football', 'TV']
}
tomli_w.dump(data, open('dump.toml', 'wb'))
相关推荐
数据小爬虫@7 小时前
利用Python爬虫快速获取商品历史价格信息
开发语言·爬虫·python
小白学大数据7 小时前
如何使用Selenium处理JavaScript动态加载的内容?
大数据·javascript·爬虫·selenium·测试工具
qq_375872699 小时前
15爬虫:下载器中间件
爬虫
数据小小爬虫12 小时前
如何利用Python爬虫获取商品历史价格信息
开发语言·爬虫·python
黑色叉腰丶大魔王12 小时前
《基于 Python 的网页爬虫详细教程》
开发语言·爬虫·python
laity1712 小时前
爬取小说案例-BeautifulSoup教学篇
爬虫·python
lovelin+v1750304096614 小时前
智能电商:API接口如何驱动自动化与智能化转型
大数据·人工智能·爬虫·python
FBI780980459417 小时前
API接口在电商行业中的创新应用与趋势
运维·网络·人工智能·爬虫·python
数据小爬虫@17 小时前
Python爬虫抓取数据,有哪些常见的问题?
开发语言·爬虫·python
漫无目的行走的月亮18 小时前
基于Python Scrapy的豆瓣Top250电影爬虫程序
爬虫·python·scrapy