Skip to content

🌳 进阶用法

📰 设置表头

用表格文件做数据采集的时候,要设置表头,我们可以把表头作为第一条数据通过add_data()方法添加到文件。但我们通常会使用同一段代码对一个文件进行多批次写入,如果每次都执行到这个语句,就会写入多个表头行。

因此设计了一个设置表头的方法set.head()Recorder还有一个自动设置表头的功能。

📜 自动设置

自动设置只有Recorder支持,同时满足两个条件就能自动激活:

  • 指定不存在的 csv 或 xlsx 文件

  • 数据格式为dict

写入数据时,程序会自动创建文件,同时会把第一条数据的key值作为表头添加到文件。

from DataRecorder import Recorder

r = Recorder('data.csv')
data = {'姓名':'张三', '性别':'男'}
r.add_data(data)

文件内容:

姓名 性别
张三

📜 手动设置

set.head()方法可以为 xlsx 或 csv 文件设置表头,无论文件是否已创建。任何时候都可以使用。

RecorderFiller都支持此方法。

from DataRecorder import Recorder

r = Recorder('data.csv')
r.set.head(('姓名', '性别'))
r.add_data(('张三', '男'))

注意

set.head()会覆盖第一行内容,使用时要注意。


🧩 自动匹配表头

当操作 xlsx 或 csv 文件,且接收dict格式数据时,Recorder可根据表头自动匹配数据列。

该功能默认关闭,须主动打开:

r = Recorder()
r.set.fit_head(True)

示例:

假设有如下表格文件:

姓名 性别 年龄
张三 20

执行以下代码:

from DataRecorder import Recorder

r = Recorder('demo.xlsx')
r.set.fit_head(True)

d = {'姓名': '李四', '年龄': 25, '性别': '女', '来自': '陕西'}
r.add_data(d)
r.record()

运行结果:

姓名 性别 年龄
张三 20
李四 25

可见,插入数据时会自动按照表头排序,且忽略表头不存在的列。


🔖 为每条数据添加固定前后缀

有些时候,数据采集的时候要为该批次每条数据都录入同一个内容,比如日期。我们当然可以直接在数据里面加上这些列,但有一个更方便的方法,可以减少这些内容对采集代码的干扰。就是beforeafter属性。

顾名思义,before是在每条数据记录的时候在前面添加一些固定列,after则是在后面。

RecorderFiller支持这两个属性,ByteRecorder不支持。

下面这个示例,采集 gitee 开源项目前 5 页。此示例使用 DrissionPage 进行页面访问和解析。

from DrissionPage import SessionPage
from DataRecorder import Recorder

def get_list(page, recorder):
    """获取一页信息并添加到记录器"""
    p = SessionPage()  # 创建页面对象
    url = f'https://gitee.com/explore/all?page={page}'
    p.get(url)  # 访问页面
    rows = p('.ui relaxed divided items explore-repo__list').eles('.item')
    for row in rows:  # 遍历所有行
        data = {  # 产生一行数据
            'page': page,
            'title': row('.title project-namespace-path').text,
            'content': row('.project-desc mb-1').text,
            'stars': row('.stars-count').text
        }
        recorder.add_data(data)  # 把一条数据放入记录器

def main():
    r = Recorder('data.xlsx')  # 创建记录器
    for i in range(1, 6):  # 遍历5页
        get_list(i, r)

if __name__ == '__main__':
    main()

文件内容:

page title content stars
1 RTduino/RTduino RT-Thread的Arduino生态兼容层 3
1 zu1k/nali An offline tool for querying IP geographic information and CDN provider.一个查询IP地理信息和CDN服务提供商的离线终端工具. 2
1 徐小夕/dooringx 开箱即用的H5可视化搭建框架,轻松搭建自己的可视化搭建平台 31
以下省略。。。

我们希望在前面增加一列用于记录采集日期,在后面添加一列记录采集人,这些列每一行都一样,因此可以统一写在beforeafter属性中。

r = Recorder('data.xlsx')
r.set.before({'date': '2022-06-10'})
r.set.after({'staff': 'g1879'})

现在删掉刚才的文件重新执行采集,得到以下文件内容:

date page title content stars staff
2022-06-10 1 RTduino/RTduino RT-Thread的Arduino生态兼容层 3 g1879
2022-06-10 1 zu1k/nali An offline tool for querying IP geographic information and CDN provider.一个查询IP地理信息和CDN服务提供商的离线终端工具. 2 g1879
2022-06-10 1 徐小夕/dooringx 开箱即用的H5可视化搭建框架,轻松搭建自己的可视化搭建平台 31 g1879
以下省略。。。

也许您已经注意到,代码没有执行set.head(),但前后两列的key值也加到表头里去了。

而且这两个属性还支持多列,可以接收listtupledict等,添加前后列。

注意

DBRecorder设置的afterbefore如果不是dict格式,而add_data()传入的数据是dict格式,afterbefore设置会被忽略。


🧵 多线程写入文件

做数据采集的时候经常用到多线程,多线程同时记录到文件容易产生冲突。本库为多线程数据采集提供了支持,只要在主线程中创建记录器对象,分发到各个采集线程中使用即可。用法和单线程一致,可避免写入冲突。

现在继续用上面的示例,创建 5 个线程分别爬取 gitee 开源项目前 5 页,每个线程接收同一个记录器对象,往里面添加数据。只要一个小改动,把对get_list()的调用放入线程里即可。

# 单线程:
def main():
    r = Recorder('data.xlsx')  # 创建记录器
    for i in range(1, 6):  # 编历5页
        get_list(i, r)

# 多线程:
def main():
    r = Recorder('data.xlsx')  # 创建记录器
    for i in range(1, 6):  # 创建5个线程分别获取1-5页
        Thread(target=get_list, args=(i, r)).start()

文件内容:

page title content stars
4 RichardoMu/yolov5-mask-detect A C++ implementation of Yolov5 to detect mask running in Jetson Xavier nx and Jetson nano. 17
4 RichardoMu/yolov5-smoke-detection-python A Python implementation of Yolov5 to detect whether peaple smoking in Jetson Xavier nx and Jetson nano 4
4 RichardoMu/yolov5-smoking-detect A C++ implementation of Yolov5 to detect peaple smoking running in Jetson Xavier nx and Jetson nano. 6
以下省略。。。

注意

因为是多线程采集,文件内容并不是按顺序添加。