feishu_lib.py 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908
  1. # -*- coding: utf-8 -*-
  2. # @Author: wangkun
  3. # @Time: 2022/6/27
  4. import json
  5. import time
  6. import requests
  7. import urllib3
  8. from common import Common
  9. proxies = {"http": None, "https": None}
  10. class Feishu:
  11. """
  12. 编辑飞书云文档
  13. """
  14. # 看一看爬虫数据表
  15. kanyikan_url = "https://w42nne6hzg.feishu.cn/sheets/shtcngRPoDYAi24x52j2nDuHMih?"
  16. # 快手爬虫数据表
  17. kuaishou_url = "https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?"
  18. # 微视爬虫数据表
  19. weishi_url = "https://w42nne6hzg.feishu.cn/sheets/shtcn5YSWg91JfVGzj0SFZIRRPh?"
  20. # 小年糕爬虫数据表
  21. xiaoniangao_url = "https://w42nne6hzg.feishu.cn/sheets/shtcnYxiyQ1wLklo1W5Kdqc9cGh?"
  22. # twitter 爬虫表
  23. twitter_url = "https://whtlrai9ej.feishu.cn/sheets/shtcn6BYfYuqegIP13ORB6rI2dh?"
  24. # 爬虫视频监控表
  25. spreadsheettoken_monitor = "https://w42nne6hzg.feishu.cn/sheets/shtcnlZWYazInhf7Z60jkbLRJyd?"
  26. # 飞书路径token
  27. @classmethod
  28. def spreadsheettoken(cls, spreadsheettoken):
  29. """
  30. :param spreadsheettoken: 哪张云文档表
  31. """
  32. if spreadsheettoken == "kanyikan":
  33. return "shtcngRPoDYAi24x52j2nDuHMih"
  34. elif spreadsheettoken == "kuaishou":
  35. return "shtcnp4SaJt37q6OOOrYzPMjQkg"
  36. elif spreadsheettoken == "weishi":
  37. return "shtcn5YSWg91JfVGzj0SFZIRRPh"
  38. elif spreadsheettoken == "xiaoniangao":
  39. return "shtcnYxiyQ1wLklo1W5Kdqc9cGh"
  40. elif spreadsheettoken == "twitter":
  41. return "shtcn6BYfYuqegIP13ORB6rI2dh"
  42. elif spreadsheettoken == "monitor":
  43. return "shtcnlZWYazInhf7Z60jkbLRJyd"
  44. # 获取飞书api token
  45. @classmethod
  46. def get_token(cls, log_type):
  47. """
  48. 获取飞书api token
  49. :return:
  50. """
  51. time.sleep(1)
  52. url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/"
  53. post_data = {"app_id": "cli_a13ad2afa438d00b", # 这里账号密码是发布应用的后台账号及密码
  54. "app_secret": "4tK9LY9VbiQlY5umhE42dclBFo6t4p5O"}
  55. try:
  56. urllib3.disable_warnings()
  57. response = requests.post(url=url, data=post_data, proxies=proxies, verify=False)
  58. tenant_access_token = response.json()["tenant_access_token"]
  59. return tenant_access_token
  60. except Exception as e:
  61. Common.logger(log_type).error("获取飞书 api token 异常:{}", e)
  62. # 获取表格元数据
  63. @classmethod
  64. def get_metainfo(cls, log_type, spreadsheettoken):
  65. """
  66. 获取表格元数据
  67. :return:
  68. """
  69. get_metainfo_url = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" \
  70. + cls.spreadsheettoken(spreadsheettoken) + "/metainfo"
  71. headers = {
  72. "Authorization": "Bearer " + cls.get_token(log_type),
  73. "Content-Type": "application/json; charset=utf-8"
  74. }
  75. params = {
  76. "extFields": "protectedRange", # 额外返回的字段,extFields=protectedRange时返回保护行列信息
  77. "user_id_type": "open_id" # 返回的用户id类型,可选open_id,union_id
  78. }
  79. try:
  80. urllib3.disable_warnings()
  81. r = requests.get(url=get_metainfo_url, headers=headers, params=params, proxies=proxies, verify=False)
  82. response = json.loads(r.content.decode("utf8"))
  83. return response
  84. except Exception as e:
  85. Common.logger(log_type).error("获取表格元数据异常:{}", e)
  86. # 读取工作表中所有数据
  87. @classmethod
  88. def get_values_batch(cls, log_type, spreadsheettoken, sheetid):
  89. """
  90. 读取工作表中所有数据
  91. :param spreadsheettoken: 哪个爬虫
  92. :param log_type: 哪个日志
  93. :param sheetid: 哪张表
  94. :return: 所有数据
  95. """
  96. get_values_batch_url = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" \
  97. + cls.spreadsheettoken(spreadsheettoken) + "/values_batch_get"
  98. headers = {
  99. "Authorization": "Bearer " + cls.get_token(log_type),
  100. "Content-Type": "application/json; charset=utf-8"
  101. }
  102. params = {
  103. # 多个查询范围 如 url?ranges=range1,range2 ,其中 range 包含 sheetId 与单元格范围两部分
  104. "ranges": sheetid,
  105. # valueRenderOption=ToString 可返回纯文本的值(数值类型除外);
  106. # valueRenderOption=FormattedValue 计算并格式化单元格;
  107. # valueRenderOption=Formula单元格中含有公式时返回公式本身;
  108. # valueRenderOption=UnformattedValue计算但不对单元格进行格式化
  109. "valueRenderOption": "ToString",
  110. # dateTimeRenderOption=FormattedString 计算并将时间日期按照其格式进行格式化,但不会对数字进行格式化,返回格式化后的字符串。
  111. "dateTimeRenderOption": "",
  112. # 返回的用户id类型,可选open_id,union_id
  113. "user_id_type": "open_id"
  114. }
  115. try:
  116. urllib3.disable_warnings()
  117. r = requests.get(url=get_values_batch_url, headers=headers, params=params, proxies=proxies, verify=False)
  118. response = json.loads(r.content.decode("utf8"))
  119. values = response["data"]["valueRanges"][0]["values"]
  120. return values
  121. except Exception as e:
  122. Common.logger(log_type).error("读取工作表所有数据异常:{}", e)
  123. # 工作表,插入行或列
  124. @classmethod
  125. def insert_columns(cls, log_type, spreadsheettoken, sheetid, majordimension, startindex, endindex):
  126. """
  127. 工作表插入行或列
  128. :param log_type: 哪个日志
  129. :param spreadsheettoken: 哪个爬虫
  130. :param sheetid:哪张工作表
  131. :param majordimension:行或者列
  132. :param startindex:开始位置
  133. :param endindex:结束位置
  134. """
  135. insert_columns_url = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" \
  136. + cls.spreadsheettoken(spreadsheettoken) + "/insert_dimension_range"
  137. headers = {
  138. "Authorization": "Bearer " + cls.get_token(log_type),
  139. "Content-Type": "application/json; charset=utf-8"
  140. }
  141. body = {
  142. "dimension": {
  143. "sheetId": sheetid,
  144. "majorDimension": majordimension, # 默认 ROWS ,可选 ROWS、COLUMNS
  145. "startIndex": startindex, # 开始的位置
  146. "endIndex": endindex # 结束的位置
  147. },
  148. "inheritStyle": "AFTER" # BEFORE 或 AFTER,不填为不继承 style
  149. }
  150. try:
  151. urllib3.disable_warnings()
  152. r = requests.post(url=insert_columns_url, headers=headers, json=body, proxies=proxies, verify=False)
  153. Common.logger(log_type).info("插入行或列:{}", r.json()["msg"])
  154. except Exception as e:
  155. Common.logger(log_type).error("插入行或列异常:{}", e)
  156. # 写入数据
  157. @classmethod
  158. def update_values(cls, log_type, spreadsheettoken, sheetid, ranges, values):
  159. """
  160. 写入数据
  161. :param log_type: 哪个log
  162. :param spreadsheettoken: 哪个爬虫
  163. :param sheetid:哪张工作表
  164. :param ranges:单元格范围
  165. :param values:写入的具体数据,list
  166. """
  167. update_values_url = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" \
  168. + cls.spreadsheettoken(spreadsheettoken) + "/values_batch_update"
  169. headers = {
  170. "Authorization": "Bearer " + cls.get_token(log_type),
  171. "Content-Type": "application/json; charset=utf-8"
  172. }
  173. body = {
  174. "valueRanges": [
  175. {
  176. "range": sheetid + "!" + ranges,
  177. "values": values
  178. },
  179. ],
  180. }
  181. try:
  182. urllib3.disable_warnings()
  183. r = requests.post(url=update_values_url, headers=headers, json=body, proxies=proxies, verify=False)
  184. Common.logger(log_type).info("写入数据:{}", r.json()["msg"])
  185. except Exception as e:
  186. Common.logger(log_type).error("写入数据异常:{}", e)
  187. # 合并单元格
  188. @classmethod
  189. def merge_cells(cls, log_type, spreadsheettoken, sheetid, ranges):
  190. """
  191. 合并单元格
  192. :param spreadsheettoken: 哪个爬虫
  193. :param log_type: 哪个log
  194. :param sheetid:哪张工作表
  195. :param ranges:需要合并的单元格范围
  196. """
  197. merge_cells_url = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" \
  198. + cls.spreadsheettoken(spreadsheettoken) + "/merge_cells"
  199. headers = {
  200. "Authorization": "Bearer " + cls.get_token(log_type),
  201. "Content-Type": "application/json; charset=utf-8"
  202. }
  203. body = {
  204. "range": sheetid + "!" + ranges,
  205. "mergeType": "MERGE_ROWS"
  206. }
  207. try:
  208. urllib3.disable_warnings()
  209. r = requests.post(url=merge_cells_url, headers=headers, json=body, proxies=proxies, verify=False)
  210. Common.logger(log_type).info("合并单元格:{}", r.json()["msg"])
  211. except Exception as e:
  212. Common.logger(log_type).error("合并单元格异常:{}", e)
  213. # 读取单元格数据
  214. @classmethod
  215. def get_range_value(cls, log_type, spreadsheettoken, sheetid, cell):
  216. """
  217. 读取单元格内容
  218. :param spreadsheettoken: 哪个爬虫
  219. :param log_type: 哪个log
  220. :param sheetid: 哪张工作表
  221. :param cell: 哪个单元格
  222. :return: 单元格内容
  223. """
  224. get_range_value_url = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" \
  225. + cls.spreadsheettoken(spreadsheettoken) + "/values/" + sheetid + "!" + cell
  226. headers = {
  227. "Authorization": "Bearer " + cls.get_token(log_type),
  228. "Content-Type": "application/json; charset=utf-8"
  229. }
  230. params = {
  231. # valueRenderOption=ToString 可返回纯文本的值(数值类型除外);
  232. # valueRenderOption=FormattedValue 计算并格式化单元格;
  233. # valueRenderOption=Formula 单元格中含有公式时返回公式本身;
  234. # valueRenderOption=UnformattedValue 计算但不对单元格进行格式化。
  235. "valueRenderOption": "FormattedValue",
  236. # dateTimeRenderOption=FormattedString 计算并对时间日期按照其格式进行格式化,但不会对数字进行格式化,返回格式化后的字符串。
  237. "dateTimeRenderOption": "",
  238. # 返回的用户id类型,可选open_id,union_id
  239. "user_id_type": "open_id"
  240. }
  241. try:
  242. urllib3.disable_warnings()
  243. r = requests.get(url=get_range_value_url, headers=headers, params=params, proxies=proxies, verify=False)
  244. return r.json()["data"]["valueRange"]["values"][0]
  245. except Exception as e:
  246. Common.logger(log_type).error("读取单元格数据异常:{}", e)
  247. # 删除行或列,可选 ROWS、COLUMNS
  248. @classmethod
  249. def dimension_range(cls, log_type, spreadsheettoken, sheetid, major_dimension, startindex, endindex):
  250. """
  251. 删除行或列
  252. :param spreadsheettoken: 哪个爬虫
  253. :param log_type: 哪个log
  254. :param sheetid:工作表
  255. :param major_dimension:默认 ROWS ,可选 ROWS、COLUMNS
  256. :param startindex:开始的位置
  257. :param endindex:结束的位置
  258. :return:
  259. """
  260. dimension_range_url = "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" \
  261. + cls.spreadsheettoken(spreadsheettoken) + "/dimension_range"
  262. headers = {
  263. "Authorization": "Bearer " + cls.get_token(log_type),
  264. "Content-Type": "application/json; charset=utf-8"
  265. }
  266. body = {
  267. "dimension": {
  268. "sheetId": sheetid,
  269. "majorDimension": major_dimension,
  270. "startIndex": startindex,
  271. "endIndex": endindex
  272. }
  273. }
  274. try:
  275. urllib3.disable_warnings()
  276. r = requests.delete(url=dimension_range_url, headers=headers, json=body, proxies=proxies, verify=False)
  277. Common.logger(log_type).info("删除视频数据:{}", r.json()["msg"])
  278. except Exception as e:
  279. Common.logger(log_type).error("删除视频数据异常:{}", e)
  280. # 查找单元格
  281. @classmethod
  282. def find_cell(cls, log_type, spreadsheettoken, sheetid, find_text):
  283. """
  284. 查找单元格
  285. :param spreadsheettoken: 哪个爬虫
  286. :param log_type: 哪个log
  287. :param sheetid: 哪张表
  288. # :param ranges: 单元格范围
  289. :param find_text: 查找的字符
  290. :return: 返回单元格索引
  291. """
  292. find_cell_url = "https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/" \
  293. + cls.spreadsheettoken(spreadsheettoken) + "/sheets/" \
  294. + sheetid + "/find"
  295. headers = {
  296. "Authorization": "Bearer " + cls.get_token(log_type),
  297. "Content-Type": "application/json; charset=utf-8"
  298. }
  299. rows_count = len(cls.get_values_batch(log_type, spreadsheettoken, "db114c"))
  300. body = {
  301. "find_condition": {
  302. "range": sheetid + "!A1:A" + str(rows_count),
  303. "match_case": True, # 是否忽略大小写
  304. "match_entire_cell": False, # 是否匹配整个单元格
  305. "search_by_regex": False, # 是否为正则匹配
  306. "include_formulas": False # 是否搜索公式内容
  307. },
  308. "find": find_text # 搜索内容
  309. }
  310. try:
  311. urllib3.disable_warnings()
  312. r = requests.post(url=find_cell_url, headers=headers, json=body, proxies=proxies, verify=False)
  313. Common.logger(log_type).info("查找单元格:{}", r.json()["msg"])
  314. matched_cell = r.json()["data"]["find_result"]["matched_cells"][0].split("A")[-1]
  315. return matched_cell
  316. except Exception as e:
  317. Common.logger(log_type).error("查找单元格异常:{}", e)
  318. # 筛选:filter
  319. @classmethod
  320. def filter_created_at(cls, log_type):
  321. filter_created_at_url = "https://open.feishu.cn/open-apis/sheets/v3/spreadsheets/" \
  322. "shtcn8fFzDhCFHpB6vzf51s2xbf/sheets/48cfb0/filter"
  323. headers = {
  324. "Authorization": "Bearer " + cls.get_token(log_type),
  325. "Content-Type": "application/json; charset=utf-8"
  326. }
  327. body = {
  328. "col": "A",
  329. "condition": {
  330. "filter_type": "number",
  331. "compare_type": "less",
  332. "expected": [
  333. "6"
  334. ]
  335. }
  336. }
  337. try:
  338. urllib3.disable_warnings()
  339. r = requests.put(url=filter_created_at_url, headers=headers, json=body, proxies=proxies, verify=False)
  340. print(r.json())
  341. except Exception as e:
  342. Common.logger(log_type).error("查找单元格异常:{}", e)
  343. class Bitable:
  344. """
  345. 多维表格 API
  346. 文档地址:https://w42nne6hzg.feishu.cn/base/bascnpAYvIA0B1hBtNJlriZceUV?table=tblqMbXrpqFbDLNE&view=vewsMtek0O
  347. app_token:bascnpAYvIA0B1hBtNJlriZceUV
  348. """
  349. app_token = "bascnpAYvIA0B1hBtNJlriZceUV"
  350. table_id = "tblqMbXrpqFbDLNE"
  351. page_token = "" # 列出记录时,翻页参数
  352. # 获取飞书api token
  353. @classmethod
  354. def tenant_access_token(cls, log_type):
  355. """
  356. 获取飞书api token
  357. :return:
  358. """
  359. time.sleep(1)
  360. url = "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/"
  361. post_data = {"app_id": "cli_a13ad2afa438d00b", # 这里账号密码是发布应用的后台账号及密码
  362. "app_secret": "4tK9LY9VbiQlY5umhE42dclBFo6t4p5O"}
  363. try:
  364. urllib3.disable_warnings()
  365. response = requests.post(url=url, data=post_data, proxies=proxies, verify=False)
  366. tenant_access_token = response.json()["tenant_access_token"]
  367. return tenant_access_token
  368. except Exception as e:
  369. Common.logger(log_type).error("获取tenant_access_token异常:{}", e)
  370. # 获取多维表格元数据
  371. @classmethod
  372. def get_apps(cls, log_type):
  373. """
  374. 获取多维表格元数据
  375. 该接口支持调用频率上限为 20 QPS
  376. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app/get
  377. """
  378. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" + cls.app_token
  379. headers = {
  380. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  381. "Content-Type": "application/json; charset=utf-8"
  382. }
  383. try:
  384. urllib3.disable_warnings()
  385. r = requests.get(url=url, headers=headers, proxies=proxies, verify=False)
  386. Common.logger(log_type).info("获取多维表格元数据,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  387. except Exception as e:
  388. Common.logger(log_type).error("获取多维表格元数据异常:{}", e)
  389. # 列出数据表
  390. @classmethod
  391. def get_tables(cls, log_type):
  392. """
  393. 列出数据表
  394. 该接口支持调用频率上限为 20 QPS
  395. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table/list
  396. """
  397. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" + cls.app_token + "/tables"
  398. headers = {
  399. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  400. "Content-Type": "application/json; charset=utf-8"
  401. }
  402. params = {
  403. "page_token": "",
  404. "page_size": ""
  405. }
  406. try:
  407. urllib3.disable_warnings()
  408. r = requests.get(url=url, headers=headers, params=params, proxies=proxies, verify=False)
  409. Common.logger(log_type).info("列出数据表,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  410. except Exception as e:
  411. Common.logger(log_type).error("列出数据表异常:{}", e)
  412. # 列出记录
  413. @classmethod
  414. def list_records(cls, log_type, count):
  415. """
  416. 该接口用于列出数据表中的现有记录,单次最多列出 100 行记录,支持分页获取。
  417. 该接口支持调用频率上限为 1000 次/分钟
  418. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/list
  419. """
  420. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  421. + cls.app_token + "/tables/" + cls.table_id + "/records"
  422. headers = {
  423. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  424. "Content-Type": "application/json; charset=utf-8"
  425. }
  426. params = {
  427. "view_id": "", # 视图 id; 注意:
  428. # 如 filter 或 sort 有值,view_id 会被忽略。
  429. # 示例值: "vewqhz51lk"
  430. "filter": "", # 筛选参数; 注意:
  431. # 1.筛选记录的表达式不超过2000个字符。
  432. # 2.不支持对“人员”以及“关联字段”的属性进行过滤筛选,如人员的 OpenID。
  433. # 3.仅支持字段在页面展示字符值进行筛选。
  434. # 详细参考:https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/filter
  435. # 示例值:"示例表达式:AND(CurrentValue.[身高]>180, CurrentValue.[体重]>150)"
  436. "sort": "", # 排序参数。注意:
  437. # 1.表达式需要不超过1000字符。
  438. # 2.不支持对带“公式”和“关联字段”的表的使用。
  439. # 示例值:"["字段1 DESC","字段2 ASC"]
  440. # 注意:使用引号将字段名称和顺序逆序连接起来。"
  441. "field_names": "[]", # 字段名称。示例值:"["字段1"]"
  442. "text_field_as_array": True, # 控制多行文本字段数据的返回格式,true 表示以数组形式返回。注意:
  443. # 1.多行文本中如果有超链接部分,则会返回链接的 URL。
  444. # 2.目前可以返回多行文本中 URL 类型为多维表格链接、飞书 doc、飞书 sheet的URL类型以及@人员的数据结构。
  445. # 示例值:true
  446. # "user_id_type": "", # 用户 ID 类型
  447. # 示例值:"open_id"
  448. # 可选值有:
  449. # open_id:用户的 open id
  450. # union_id:用户的 union id
  451. # user_id:用户的 user id
  452. # 默认值:open_id
  453. "display_formula_ref": "", # 控制公式、查找引用是否显示完整的原样返回结果。示例值:true
  454. "automatic_fields": "", # 控制是否返回自动计算的字段
  455. # 例如 created_by/created_time/last_modified_by/last_modified_time,true 表示返回
  456. # 示例值:true
  457. "page_token": "", # 分页标记
  458. # 第一次请求不填,表示从头开始遍历;
  459. # 分页查询结果还有更多项时会同时返回新的 page_token
  460. # 下次遍历可采用该 page_token 获取查询结果
  461. # 示例值:"recn0hoyXL"
  462. "page_size": count # 分页大小。示例值:10。数据校验规则:最大值 100
  463. }
  464. try:
  465. urllib3.disable_warnings()
  466. r = requests.get(url=url, headers=headers, params=params, proxies=proxies, verify=False)
  467. cls.page_token = r.json()["data"]["page_token"]
  468. items = r.json()["data"]["items"]
  469. for item in items:
  470. print(item)
  471. Common.logger(log_type).info("列出记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  472. except Exception as e:
  473. Common.logger(log_type).error("列出记录异常:{}", e)
  474. # 检索记录
  475. @classmethod
  476. def search_records(cls, log_type, record_id):
  477. """
  478. 该接口用于根据 record_id 的值检索现有记录
  479. 该接口支持调用频率上限为 20 QPS
  480. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/get
  481. """
  482. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  483. + cls.app_token + "/tables/" + cls.table_id + "/records/" + record_id
  484. headers = {
  485. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  486. "Content-Type": "application/json; charset=utf-8"
  487. }
  488. params = {
  489. "text_field_as_array": True, # 控制多行文本字段数据的返回格式, true 表示以数组形式返回。示例值:true
  490. # "user_id_type": "", # 用户 ID 类型
  491. # 示例值:"open_id"
  492. # 可选值有:
  493. # open_id:用户的 open id
  494. # union_id:用户的 union id
  495. # user_id:用户的 user id
  496. # 默认值:open_id
  497. "display_formula_ref": True, # 控制公式、查找引用是否显示完整的原样返回结果。示例值:true
  498. "automatic_fields": True, # 控制是否返回自动计算的字段
  499. # 例如 created_by/created_time/last_modified_by/last_modified_time,true 表示返回。示例值:true
  500. }
  501. try:
  502. urllib3.disable_warnings()
  503. r = requests.get(url=url, headers=headers, params=params, proxies=proxies, verify=False)
  504. Common.logger(log_type).info("检索记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  505. except Exception as e:
  506. Common.logger(log_type).error("检索记录异常:{}", e)
  507. # 新增记录
  508. @classmethod
  509. def create_record(cls, log_type, fields):
  510. """
  511. 该接口用于在数据表中新增一条记录
  512. 该接口支持调用频率上限为 10 QPS
  513. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/create
  514. """
  515. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  516. + cls.app_token + "/tables/" + cls.table_id + "/records"
  517. headers = {
  518. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  519. "Content-Type": "application/json; charset=utf-8"
  520. }
  521. body = fields
  522. try:
  523. urllib3.disable_warnings()
  524. r = requests.post(url=url, headers=headers, json=body, proxies=proxies, verify=False)
  525. Common.logger(log_type).info("新增记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  526. except Exception as e:
  527. Common.logger(log_type).error("新增记录异常:{}", e)
  528. # 新增多条记录
  529. @classmethod
  530. def create_records(cls, log_type, records):
  531. """
  532. 该接口用于在数据表中新增多条记录
  533. 该接口支持调用频率上限为 10 QPS
  534. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/batch_create
  535. """
  536. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  537. + cls.app_token + "/tables/" + cls.table_id + "/records/batch_create"
  538. headers = {
  539. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  540. "Content-Type": "application/json; charset=utf-8"
  541. }
  542. body = {
  543. "records": records
  544. }
  545. try:
  546. urllib3.disable_warnings()
  547. r = requests.post(url=url, headers=headers, json=body, proxies=proxies, verify=False)
  548. Common.logger(log_type).info("新增多条记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  549. except Exception as e:
  550. Common.logger(log_type).error("新增多条记录异常:{}", e)
  551. # 更新记录
  552. @classmethod
  553. def update_record(cls, log_type, record_id, fields):
  554. """
  555. 该接口用于更新数据表中的一条记录
  556. 该接口支持调用频率上限为 10 QPS
  557. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/update
  558. """
  559. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  560. + cls.app_token + "/tables/" + cls.table_id + "/records/" + record_id
  561. headers = {
  562. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  563. "Content-Type": "application/json; charset=utf-8"
  564. }
  565. body = fields
  566. try:
  567. urllib3.disable_warnings()
  568. r = requests.put(url=url, headers=headers, json=body, proxies=proxies, verify=False)
  569. Common.logger(log_type).info("更新记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  570. except Exception as e:
  571. Common.logger(log_type).error("更新记录异常:{}", e)
  572. # 更新多条记录
  573. @classmethod
  574. def update_records(cls, log_type, records):
  575. """
  576. 该接口用于更新数据表中的多条记录
  577. 该接口支持调用频率上限为 10 QPS
  578. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/batch_update
  579. """
  580. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  581. + cls.app_token + "/tables/" + cls.table_id + "/records/batch_update"
  582. headers = {
  583. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  584. "Content-Type": "application/json; charset=utf-8"
  585. }
  586. body = records
  587. try:
  588. urllib3.disable_warnings()
  589. r = requests.post(url=url, headers=headers, json=body, proxies=proxies, verify=False)
  590. Common.logger(log_type).info("更新多条记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  591. except Exception as e:
  592. Common.logger(log_type).error("更新多条记录异常:{}", e)
  593. # 删除记录
  594. @classmethod
  595. def del_record(cls, log_type, record_id):
  596. """
  597. 该接口用于删除数据表中的一条记录
  598. 该接口支持调用频率上限为 10 QPS
  599. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/delete
  600. """
  601. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  602. + cls.app_token + "/tables/" + cls.table_id + "/records/" + record_id
  603. headers = {
  604. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  605. "Content-Type": "application/json; charset=utf-8"
  606. }
  607. try:
  608. urllib3.disable_warnings()
  609. r = requests.delete(url=url, headers=headers, proxies=proxies, verify=False)
  610. Common.logger(log_type).info("删除记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  611. except Exception as e:
  612. Common.logger(log_type).error("删除记录异常:{}", e)
  613. # 删除多条记录
  614. @classmethod
  615. def del_records(cls, log_type, record_ids):
  616. """
  617. 该接口用于删除数据表中现有的多条记录
  618. 该接口支持调用频率上限为 10 QPS
  619. https://open.feishu.cn/document/uAjLw4CM/ukTMukTMukTM/reference/bitable-v1/app-table-record/batch_delete
  620. """
  621. url = "https://open.feishu.cn/open-apis/bitable/v1/apps/" \
  622. + cls.app_token + "/tables/" + cls.table_id + "/records/batch_delete"
  623. headers = {
  624. "Authorization": "Bearer " + cls.tenant_access_token(log_type),
  625. "Content-Type": "application/json; charset=utf-8"
  626. }
  627. body = {
  628. "records": record_ids # 删除的多条记录id列表。示例值:["recIcJBbvC","recvmiCORa"]
  629. }
  630. try:
  631. urllib3.disable_warnings()
  632. r = requests.post(url=url, headers=headers, json=body, proxies=proxies, verify=False)
  633. Common.logger(log_type).info("删除多条记录,code:{},msg:{}", r.json()["code"], r.json()["msg"])
  634. except Exception as e:
  635. Common.logger(log_type).error("删除多条记录异常:{}", e)
  636. if __name__ == "__main__":
  637. # feishu = Feishu()
  638. # print(feishu.get_bitable_token())
  639. 'reck6nLiZV'
  640. 'recHcfJZnG'
  641. 'recxdSMhzE'
  642. # 实例化多维表格
  643. bitable = Bitable()
  644. # # 获取多维表格元数据
  645. # bitable.get_apps()
  646. #
  647. # # 列出数据表
  648. # bitable.get_tables()
  649. #
  650. # # 列出记录
  651. # bitable.list_records(3)
  652. #
  653. # # 检索记录
  654. # bitable.search_records("recHcfJZnG")
  655. # # 新增一条记录
  656. # create_value = {
  657. # "fields": {
  658. # "uid": "0000000000",
  659. # "key_words": "0000000000",
  660. # "name": "功能开发🥕",
  661. # "screen_name": "功能开发🥕",
  662. # "person_url": {
  663. # "link": "https://bytedance.feishu.cn/drive/home/", "text": "https://bytedance.feishu.cn/drive/home/"
  664. # },
  665. # "description": "功能开发🥕",
  666. # "location": "null",
  667. # "friends_count": 9999999999,
  668. # "followers_count": 9999999999,
  669. # "favourites_count": 9999999999,
  670. # "listed_count": 9999999999,
  671. # "statuses_count": 9999999999,
  672. # "media_count": 9999999999,
  673. # "display_url": {
  674. # "link": "https://bytedance.feishu.cn/drive/home/",
  675. # "text": "https://bytedance.feishu.cn/drive/home/"
  676. # },
  677. # "created_at": 1656053209000,
  678. # "profile_image_url": {
  679. # "link": "https://bytedance.feishu.cn/drive/home/",
  680. # "text": "https://bytedance.feishu.cn/drive/home/"
  681. # },
  682. # "profile_banner_url": {
  683. # "link": "null",
  684. # "text": "null"
  685. # },
  686. # "ext_has_nft_avatar": "False",
  687. # "verified": "False",
  688. # "记录创建时间": 1656053209000,
  689. # # "记录修改时间": ""
  690. # }
  691. # }
  692. # bitable.create_record(create_value)
  693. # 新增多条记录
  694. # create_values = {
  695. # "fields": {
  696. # "uid": "0000000000",
  697. # "key_words": "0000000000",
  698. # "name": "功能开发🥕",
  699. # "screen_name": "功能开发🥕",
  700. # "person_url": {
  701. # "link": "https://bytedance.feishu.cn/drive/home/", "text": "https://bytedance.feishu.cn/drive/home/"
  702. # },
  703. # "description": "功能开发🥕",
  704. # "location": "null",
  705. # "friends_count": 9999999999,
  706. # "followers_count": 9999999999,
  707. # "favourites_count": 9999999999,
  708. # "listed_count": 9999999999,
  709. # "statuses_count": 9999999999,
  710. # "media_count": 9999999999,
  711. # "display_url": {
  712. # "link": "https://bytedance.feishu.cn/drive/home/",
  713. # "text": "https://bytedance.feishu.cn/drive/home/"
  714. # },
  715. # "created_at": 1656053209000,
  716. # "profile_image_url": {
  717. # "link": "https://bytedance.feishu.cn/drive/home/",
  718. # "text": "https://bytedance.feishu.cn/drive/home/"
  719. # },
  720. # "profile_banner_url": {
  721. # "link": "null",
  722. # "text": "null"
  723. # },
  724. # "ext_has_nft_avatar": "False",
  725. # "verified": "False",
  726. # "记录创建时间": 1656053209000,
  727. # # "记录修改时间": ""
  728. # }
  729. # }
  730. # values_list = [create_values, create_values]
  731. # bitable.create_records(values_list)
  732. # # 更新一条记录
  733. # use_record_id = "recxdSMhzE"
  734. # use_fields = {
  735. # "fields": {
  736. # "uid": "1111111111",
  737. # "key_words": "1111111111",
  738. # "name": "功能开发🥕",
  739. # "screen_name": "功能开发🥕",
  740. # "person_url": {
  741. # "link": "https://bytedance.feishu.cn/drive/home/", "text": "https://bytedance.feishu.cn/drive/home/"
  742. # },
  743. # "description": "功能开发🥕",
  744. # "location": "null",
  745. # "friends_count": 9999999999,
  746. # "followers_count": 9999999999,
  747. # "favourites_count": 9999999999,
  748. # "listed_count": 9999999999,
  749. # "statuses_count": 9999999999,
  750. # "media_count": 9999999999,
  751. # "display_url": {
  752. # "link": "https://bytedance.feishu.cn/drive/home/",
  753. # "text": "https://bytedance.feishu.cn/drive/home/"
  754. # },
  755. # "created_at": 1656053209000,
  756. # "profile_image_url": {
  757. # "link": "https://bytedance.feishu.cn/drive/home/",
  758. # "text": "https://bytedance.feishu.cn/drive/home/"
  759. # },
  760. # "profile_banner_url": {
  761. # "link": "null",
  762. # "text": "null"
  763. # },
  764. # "ext_has_nft_avatar": "False",
  765. # "verified": "False",
  766. # "记录创建时间": 1656053209000,
  767. # # "记录修改时间": ""
  768. # }
  769. # }
  770. # bitable.update_record(use_record_id, use_fields)
  771. # # 更新多条记录
  772. # "recxdSMhzE"
  773. # "recHcfJZnG"
  774. # use_records = {
  775. # "records": [
  776. # {
  777. # "record_id": "recxdSMhzE",
  778. # "fields": {
  779. # "uid": "3333333333",
  780. # "key_words": "3333333333",
  781. # "name": "功能开发🥕",
  782. # "screen_name": "功能开发🥕",
  783. # "person_url": {
  784. # "link": "https://bytedance.feishu.cn/drive/home/",
  785. # "text": "https://bytedance.feishu.cn/drive/home/"
  786. # },
  787. # "description": "功能开发🥕",
  788. # "location": "null",
  789. # "friends_count": 9999999999,
  790. # "followers_count": 9999999999,
  791. # "favourites_count": 9999999999,
  792. # "listed_count": 9999999999,
  793. # "statuses_count": 9999999999,
  794. # "media_count": 9999999999,
  795. # "display_url": {
  796. # "link": "https://bytedance.feishu.cn/drive/home/",
  797. # "text": "https://bytedance.feishu.cn/drive/home/"
  798. # },
  799. # "created_at": 1656053209000,
  800. # "profile_image_url": {
  801. # "link": "https://bytedance.feishu.cn/drive/home/",
  802. # "text": "https://bytedance.feishu.cn/drive/home/"
  803. # },
  804. # "profile_banner_url": {
  805. # "link": "null",
  806. # "text": "null"
  807. # },
  808. # "ext_has_nft_avatar": "False",
  809. # "verified": "False",
  810. # "记录创建时间": 1656053209000,
  811. # # "记录修改时间": ""
  812. # }
  813. # },
  814. # {
  815. # "record_id": "recHcfJZnG",
  816. # "fields": {
  817. # "uid": "3333333333",
  818. # "key_words": "3333333333",
  819. # "name": "功能开发🥕",
  820. # "screen_name": "功能开发🥕",
  821. # "person_url": {
  822. # "link": "https://bytedance.feishu.cn/drive/home/",
  823. # "text": "https://bytedance.feishu.cn/drive/home/"
  824. # },
  825. # "description": "功能开发🥕",
  826. # "location": "null",
  827. # "friends_count": 9999999999,
  828. # "followers_count": 9999999999,
  829. # "favourites_count": 9999999999,
  830. # "listed_count": 9999999999,
  831. # "statuses_count": 9999999999,
  832. # "media_count": 9999999999,
  833. # "display_url": {
  834. # "link": "https://bytedance.feishu.cn/drive/home/",
  835. # "text": "https://bytedance.feishu.cn/drive/home/"
  836. # },
  837. # "created_at": 1656053209000,
  838. # "profile_image_url": {
  839. # "link": "https://bytedance.feishu.cn/drive/home/",
  840. # "text": "https://bytedance.feishu.cn/drive/home/"
  841. # },
  842. # "profile_banner_url": {
  843. # "link": "null",
  844. # "text": "null"
  845. # },
  846. # "ext_has_nft_avatar": "False",
  847. # "verified": "False",
  848. # "记录创建时间": 1656053209000,
  849. # # "记录修改时间": ""
  850. # }
  851. # }
  852. # ]
  853. # }
  854. # bitable.update_records(use_records)
  855. # # 删除一条记录
  856. # bitable.del_record("reck6nLiZV")
  857. # # 删除多条记录
  858. # bitable.del_records(['recHcfJZnG', 'recxdSMhzE'])