先把结论摆出来。如果你如今的数据管道, 是由一堆使用 .get 方法来获取数据以及靠手动拼接字段来凑成的脚本构成。并且你遇到的状况是, 每当添加一个数据源, 就需要把原有的代码复制粘贴一遍, 然后再去修改相应的字段。
那么在这种情况下, dlt 这个工具大概率可以帮助你把需要编写的代码量减少一半。同时它还能顺便把那些处理分页逻辑、实现增量更新以及适应架构演进等繁杂且脏累的工作全部接手去做。
我先说一下自己为什么会把眼光盯在这个东西上面。原因是我们在之前抽取一个第三方 API 的数据进到系统里的时候, 虽然写那个脚本本身的代码也就没有几行, 但是围绕着它生长出来的那些额外内容却特别多。
具体来说就是翻页要由人去自己维护游标, 接口偶尔返回的字段如果多了一个或者少了一个就会报错, 跑了一半挂掉了也不知道从什么地方开始进行续传操作, 每次表结构发生了改变还得让人去手动执行 ALTER TABLE 这个操作。
这样一个本来只需要几十行代码就能搞定的活儿, 最后竟然膨胀成了两百多行代码, 而且整里面还到处是 try 这样的异常处理块。
dlt这个词就是data load tool这个纯库的意思, 用户在使用的时候只需要执行pip指令去安装一个叫作dlt的东西就可以了, 整个过程完全不需要启动服务, 也不需要配置调度器, 更没有必要去安装一大堆其他的相关平台软件产品, 这个工具的具体定位是非常明确的, 也就是让操作人员来专门负责解决如何把各种所需的数据信息顺利输出出来的问题, 而这款名叫作dlt的产品本身则是要专心负责处理那些数据到底应该以什么方式才能更干净更方便地保存到指定数据库里的所有技术问题。
关于三十行抽取那个问题的事情, 被弄进去了。
直接提供一个能够运行的案例, 这个案例的具体内容是去抽取某个代码仓库里面的信息, 然后把抽到的这些内容放到一个指定的目标位置或者存储起来。
dlt
从的。
使用合并操作, 并将合并的键分别设置为id。
def (repo="dlt-hub/dlt"):
这个变量的值被设置为一个拼接后的字符串, 该字符串由变量repo的值以及固定后缀“?=100&state=all”组成。
while url:
将网址传入函数并获取返回的结果, 这里把它赋值给变量 resp, 这个过程里面自动地包含了重试机制以及限流退避的策略。
resp.()
yield resp.json()
# 的下一页地址放在 Link 头里,dlt 的 帮你解析
url = resp.links.get("next", {}).get("url")
= dlt.(
="",
="",
="raw",
执行.run操作, 传入一个空元组。
print()
等你把这个步骤执行完之后, 系统会自动去那个指定的空间里面, 把相应的数据表给创建好, 这些字段所对应的数据类型, 它是直接从那份 JSON 格式的数据里面推出来的, 如果遇到那些层层嵌套的对象结构, 它会被强行拍平成看起来像是列名那样的一种形式, 如果是数组的话, 那就会被拆分成单独的子表, 并且还会自动地把它和外键之间的关联关系也给带上, 我当初第一次跑完这个流程, 然后跑过去看那个表结构的时候, 心里真的是感到十分的惊讶, 哪怕是有那种非常复杂的嵌套字段在里面, 它都给我按照规矩一点点地拆分好了。
=“merge”加上 =“id”是非常关键的步骤, 它的意思就是按照主键来处理。这意味着如果是同一个 issue, 第二次被抽取到的时候, 就会去更新已有的记录, 而不是插入一个重复的行。
如果想要实现增量处理也非常容易, 只要加上 dlt…… 游标, 然后只拉取发生过变化的那部分数据就可以了, 这样即使任务中断后重新运行, 也不会发生重复加载的情况, 因为状态信息本身就会进行保存和管理。
大家会去问, 它到底连接了哪些非常不好的工作任务。
关于我把以前手写脚本的时候需要操心的事情, 以及现在被这个东西给我代替来管理的事情的情况, 我现在把它列一下:
脏活
手写脚本
dlt