找回密码
 会员注册
查看: 189|回复: 0

飞书API(3):Python自动读取多维表所有分页数据的三种方法

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64021
发表于 2024-9-13 16:34:44 | 显示全部楼层 |阅读模式
上一小节介绍了怎么使用Python读取多维表的数据,看似可以成功获取到了所有的数据,但是在实际生产使用过程中,我们会发现,上一小节的代码并不能获取到所有的多维表数据,它只能获取一页,默认是第一页。因为我使用的测试表数据量小,第一页便可读取完,所以有一种获取到所有数据的错觉。本文介绍如何进行分页读取飞书多维表的所有数据,主要内容介绍如下:在线测试:使用飞书API调试台测试本地测试:使用Python代码测试,介绍通过三种不同的方法自动读取所有分页数据:通过while循环读取、通过for循环读取和通过内函数递归读取。1、如何读取所有分页数据那么怎么获取到所有的数据呢?查看官方文档-查询记录,在“查询参数”中(如下图),可以看到有2个和翻页相关的参数:“page_token”和“page_size”。先说下“page_size”,和分页记录数相关,默认值为20,最大值上图没介绍,不过在API开头有说明(如下图),单次最大查询500行记录。而“page_token”是一个翻页参数,首页没有该参数,从第2页开始就必须要传递相关的参数,而相关的“page_token”参数的值需要通过上一页返回的数据(即响应体)进行获取,也就是说从第2页开始都依赖上一页的返回。分页标记,第一次请求不填,表示从头开始遍历;分页查询结果还有更多项时会同时返回新的page_token,下次遍历可采用该page_token获取查询结果。查看响应体说明,可以看到有两个参数和翻页相关:“has_more”和“page_token”。“has_more”用于判断有没有更多的页,有则返回True。“page_token”用于传递给查询数据接口,请求下一页的数据。1.1调试:读取第二页数据了解了取数逻辑之后,可以先调试一下。调试逻辑:先获取第1页,然后获取到第2页的“page_token”再发起请求。1.1.1读取第1页打开API调试台,准备多维表,多维表的参数说明等,参考第2小节:《API(2):通过Python读取多维表数据节》的【1.1获取demo】。请求头和路径参数参考第2小节填写即可查询参数:第2小节只填写了必填参数,所以查询查数没有传递值(即使用默认参数值),由于我测试的数据仅有10条记录,所以“page_size”改为5,以便观察翻页的参数值。改完直接点击【开始调试】即可,结果如下,可以看到“has_more”的值为true,“page_token”返回一个字符串。注:数据在响应体的data>items中,可以展开和多维表的记录比对查看下。1.1.2读取第2页将首次请求返回的“page_token”的值填写到路径参数中“page_token”的输入框里,再次点击【开始调试】发起请求。结果如下,可以看到“has_more”为false,没有“page_token”参数。10条记录分两次取完了,所以这个结果符合预期。1.2调试:本地测试读取第二页数据续上,第二次请求结果,点击示例代码,点击“Python-requests”,可以看到API连接多了3个参数,复制代码到本地测试运行。测试结果如下,正常获取到第二页的数据。2、使用Python自动读取所有分页数据上面通过手动输入“page_token”的方式读取到了测试文档的所有数据,在实际生产过程中,不可能手动一页页处理,所以需要通过代码自动将第一次获取的“page_token”传递给第二次请求。根据飞书提供的信息,至少我们可以有三种方法来穷尽读取任意多维表的所有数据。方法一:通过while循环读取方法二:通过for循环读取方法二:通过内函数递归读取上一小节最后有一个获取tenant_access_token并调接口读取数据的合并代码(代码如下)。本次测试基于该代码进行迭代。importrequestsimportjsondefget_tenant_access_token(app_id,app_secret):url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"payload=json.dumps({"app_id":app_id,"app_secret":app_secret})headers={'Content-Type':'application/json'}response=requests.request("POST",url,headers=headers,data=payload)#print(response.text)returnresponse.json()['tenant_access_token']defget_bitable_datas(tenant_access_token,app_token,table_id):url=f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/search"payload=json.dumps({})headers={'Content-Type':'application/json','Authorization':f'Bearer{tenant_access_token}'}response=requests.request("POST",url,headers=headers,data=payload)print(response.text)app_id='your_app_id'app_secret='your_app_secret'tenant_access_token=get_tenant_access_token(app_id,app_secret)app_token='your_app_token'table_id='your_table_id'get_bitable_datas(tenant_access_token,app_token,table_id)1234567891011121314151617181920212223242526272829302.1while循环读取分页数据思路:主代码使用while循环调用get_bitable_datas()函数,并处理提取page_token,在下一次循环传递给get_bitable_datas()函数。get_bitable_datas()函数需要进行修改,以便适配任意页的读取,并返回调用接口的数据。url新增三个参数:page_size、page_token、user_id_type;第一页page_token=''可以正常请求数据;返回调用接口的数据:加上returnresponse.json()。修改完参考如下:defget_bitable_datas(tenant_access_token,app_token,table_id,page_token='',page_size=20):url=f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/search?page_size={page_size}&page_token={page_token}&user_id_type=user_id"payload=json.dumps({})headers={'Content-Type':'application/json','Authorization':f'Bearer{tenant_access_token}'}response=requests.request("POST",url,headers=headers,data=payload)#print(response.text)returnresponse.json()1234567891011'运行运行主代码加上while循环,把调用get_bitable_datas()函数放到循环体中,并提取返回数据中的page_token和has_more,打印记录。参考如下:app_id='your_app_id'app_secret='your_app_secret'tenant_access_token=get_tenant_access_token(app_id,app_secret)app_token='your_app_token'table_id='your_table_id'page_token=''page_size=5has_more=Truewhilehas_more:response=get_bitable_datas(tenant_access_token,app_token,table_id,page_token,page_size)ifresponse['code']==0:page_token=response['data'].get('page_token')has_more=response['data'].get('has_more')print(response['data'].get('items'))12345678910111213141516运行代码,结果如下:成功读取并打印分页的数据。将主代码封装到main()函数中,并将所有返回的结果整到一个列表中,最终参考代码如下:importrequestsimportjsondefget_tenant_access_token(app_id,app_secret):url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"payload=json.dumps({"app_id":app_id,"app_secret":app_secret})headers={'Content-Type':'application/json'}response=requests.request("POST",url,headers=headers,data=payload)#print(response.text)returnresponse.json()['tenant_access_token']defget_bitable_datas(tenant_access_token,app_token,table_id,page_token='',page_size=20):url=f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/search?page_size={page_size}&page_token={page_token}&user_id_type=user_id"payload=json.dumps({})headers={'Content-Type':'application/json','Authorization':f'Bearer{tenant_access_token}'}response=requests.request("POST",url,headers=headers,data=payload)#print(response.text)returnresponse.json()defmain():app_id='your_app_id'app_secret='your_app_secret'tenant_access_token=get_tenant_access_token(app_id,app_secret)app_token='your_app_token'table_id='your_table_id'page_token=''page_size=5has_more=Truefeishu_datas=[]whilehas_more:response=get_bitable_datas(tenant_access_token,app_token,table_id,page_token,page_size)ifresponse['code']==0:page_token=response['data'].get('page_token')has_more=response['data'].get('has_more')#print(response['data'].get('items'))#print('\n--------------------------------------------------------------------\n')feishu_datas.extend(response['data'].get('items'))else:raiseException(response['msg'])returnfeishu_datasif__name__=='__main__':feishu_datas=main()print(feishu_datas)1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253执行结果如下:2.2for循环读取分页数据注:在2.1基础上修改。使用for循环,不如while循环便捷。因为for循环有一个难点,就是需要先确认循环的次数。而确认次数,需要知道总数据量。而确认总数据量,需要通过调接口获取,每次查询请求都会返回一个记录数据量的参数:total(如下图)。读取该值后结合每页的记录数page_size,使用公式total/size-1,然后向上取整来确定循环的次数。关联核心代码如下:先调一次接口获取到total的参数值,然后根据公式计算需要循环多少次(需要使用math.ceil()方法向上取整);顺带将首次请求的page_token和feishu_datas处理然后根据上面得到的次数进行遍历,读取后面页数的数据,并拼接到feishu_datas中。#首次请求单独处理,获取total、page_token、has_more、itemsresponse=get_bitable_datas(tenant_access_token,app_token,table_id,page_token,page_size)ifresponse['code']==0:total=math.ceil(response['data'].get('total')/page_size-1)page_token=response['data'].get('page_token')feishu_datas=response['data'].get('items')#非首次请求,统一处理foriinrange(total):response=get_bitable_datas(tenant_access_token,app_token,table_id,page_token,page_size)ifresponse['code']==0:page_token=response['data'].get('page_token')feishu_datas.extend(response['data'].get('items'))123456789101112最终的完整参考代码如下:首次请求和非首次请求的数据分开处理;使用total计算循环次数,不需要has_more参数。importrequestsimportjson,mathdefget_tenant_access_token(app_id,app_secret):url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"payload=json.dumps({"app_id":app_id,"app_secret":app_secret})headers={'Content-Type':'application/json'}response=requests.request("POST",url,headers=headers,data=payload)#print(response.text)returnresponse.json()['tenant_access_token']defget_bitable_datas(tenant_access_token,app_token,table_id,page_token='',page_size=20):url=f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/search?page_size={page_size}&page_token={page_token}&user_id_type=user_id"payload=json.dumps({})headers={'Content-Type':'application/json','Authorization':f'Bearer{tenant_access_token}'}response=requests.request("POST",url,headers=headers,data=payload)#print(response.text)returnresponse.json()defmain():app_id='your_app_id'app_secret='your_app_secret'tenant_access_token=get_tenant_access_token(app_id,app_secret)app_token='your_app_token'table_id='your_table_id'page_token=''page_size=5#首次请求单独处理,获取total、page_token、has_more、itemsresponse=get_bitable_datas(tenant_access_token,app_token,table_id,page_token,page_size)ifresponse['code']==0:total=math.ceil(response['data'].get('total')/page_size-1)page_token=response['data'].get('page_token')feishu_datas=response['data'].get('items')#非首次请求,统一处理foriinrange(total):response=get_bitable_datas(tenant_access_token,app_token,table_id,page_token,page_size)ifresponse['code']==0:page_token=response['data'].get('page_token')feishu_datas.extend(response['data'].get('items'))else:raiseException(response['msg'])else:raiseException(response['msg'])returnfeishu_datasif__name__=='__main__':feishu_datas=main()print(feishu_datas)12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758执行结果如下:2.3内函数递归读取分页数据注:在2.1基础上修改。本小节介绍使用内函数进行递归处理,相对会比较复杂。目标是传入相关的参数,便可获取到多维表的所有数据。实现该目标主要是在get_bitable_datas()函数中进行迭代:递归逻辑:加上内函数inner(),在内函数中调用外函数get_bitable_datas();什么时候调用内函数?has_more为True的时候。数据的存储问题:将存储数据的列表feishu_datas放到函数外作为全局变量,函数get_bitable_datas()的功能是给列表feishu_datas添加元素。直接看看最终完整的参考代码:将feishu_datas变量放到函数之前,使得全局可调用get_bitable_datas()中新增内函数inner(),该函数功能只有一个就是调用外函数get_bitable_datas()。当has_more值为True的时候,便不断调用内函数inner(),也不断调用外函数get_bitable_datas(),直到最后一页has_more值为False时,一层层返回,最终得到记录所有数据的列表feishu_datas。在main()函数中直接调用get_bitable_datas()函数便可得到记录所有数据的列表feishu_datas。importrequestsimportjsonfeishu_datas=[]defget_tenant_access_token(app_id,app_secret):url="https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"payload=json.dumps({"app_id":app_id,"app_secret":app_secret})headers={'Content-Type':'application/json'}response=requests.request("POST",url,headers=headers,data=payload)#print(response.text)returnresponse.json()['tenant_access_token']defget_bitable_datas(tenant_access_token,app_token,table_id,page_token='',page_size=20):url=f"https://open.feishu.cn/open-apis/bitable/v1/apps/{app_token}/tables/{table_id}/records/search?page_size={page_size}&page_token={page_token}&user_id_type=user_id"payload=json.dumps({})headers={'Content-Type':'application/json','Authorization':f'Bearer{tenant_access_token}'}response=requests.request("POST",url,headers=headers,data=payload).json()definner(tenant_access_token,app_token,table_id,page_token):#调用外函数,递归调用get_bitable_datas(tenant_access_token,app_token,table_id,page_token)ifresponse['code']==0:page_token=response['data'].get('page_token')has_more=response['data'].get('has_more')feishu_datas.extend(response['data'].get('items'))ifhas_more:#调用内函数inner(tenant_access_token,app_token,table_id,page_token)else:raiseException(response['msg'])defmain():app_id='your_app_id'app_secret='your_app_secret'tenant_access_token=get_tenant_access_token(app_id,app_secret)app_token='your_app_token'table_id='your_table_id'page_token=''page_size=5get_bitable_datas(tenant_access_token,app_token,table_id,page_token,page_size)if__name__=='__main__':main()print(feishu_datas)123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354执行结果如下:3、小结使用Python读取多维表分页,需要传递“page_token”参数。第一页“page_token”参数为空字符串,第二页开始从上一页的响应体提取“page_token”的值。使用Python实现,可以通过三种方式读取所有分页的数据:while循环读取、for循环读取和内函数递归读取。while循环:使用has_more参数,直到has_more=False跳出循环;for循环:使用total参数,结合page_size计算循环次数;内函数递归:使用has_more参数递归调用内函数,直到has_more=False时逐层返回结果。while循环相对比较好理解,而且很简洁。for循环通过数量进行计算,或有一定风险,就是在读取的过程中,如果新增数据,可能会出现bug,如果新增数据涉及新的页,将读取不到新增的数据。内函数递归方法,理解难度相对较高,代码相对简洁。个人推荐顺序:while循环>内函数递归>for循环。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 01:14 , Processed in 1.081084 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表