download_kuaishou.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. # -*- coding: utf-8 -*-
  2. # @Author: wangkun
  3. # @Time: 2022/3/29
  4. """
  5. 从 微信小程序-快手短视频 中,下载符合规则的视频
  6. """
  7. import json
  8. import os
  9. import sys
  10. import time
  11. import requests
  12. import urllib3
  13. sys.path.append(os.getcwd())
  14. from main.common import Common
  15. from main.feishu_lib import Feishu
  16. from main.publish import Publish
  17. proxies = {"http": None, "https": None}
  18. class KuaiShou:
  19. # 已下载视频列表
  20. download_video_list = []
  21. # 配置微信号
  22. Referer = Feishu.get_range_value("f1R7Mx", "C3:C3")[0][0]["link"]
  23. NS_sig3 = Feishu.get_range_value("f1R7Mx", "C4:C4")[0]
  24. NS_sig3_origin = Feishu.get_range_value("f1R7Mx", "C5:C5")[0]
  25. did = Feishu.get_range_value("f1R7Mx", "C6:C6")[0]
  26. session_key = Feishu.get_range_value("f1R7Mx", "C7:C7")[0]
  27. unionid = Feishu.get_range_value("f1R7Mx", "C8:C8")[0]
  28. eUserStableOpenId = Feishu.get_range_value("f1R7Mx", "C9:C9")[0]
  29. openId = Feishu.get_range_value("f1R7Mx", "C10:C10")[0]
  30. eOpenUserId = Feishu.get_range_value("f1R7Mx", "C11:C11")[0]
  31. kuaishou_wechat_app_st = Feishu.get_range_value("f1R7Mx", "C12:C12")[0]
  32. passToken = Feishu.get_range_value("f1R7Mx", "C13:C13")[0]
  33. userId = Feishu.get_range_value("f1R7Mx", "C14:C14")[0]
  34. @classmethod
  35. def sensitive_words(cls):
  36. # 敏感词库列表
  37. word_list = []
  38. # 从云文档读取所有敏感词,添加到词库列表
  39. lists = Feishu.get_values_batch("fn8IDi")
  40. for i in lists:
  41. for j in i:
  42. # 过滤空的单元格内容
  43. if j is None:
  44. pass
  45. else:
  46. word_list.append(j)
  47. return word_list
  48. @staticmethod
  49. def kuaishou_download_rule(d_duration, d_width, d_height,
  50. d_play_cnt, d_like_cnt, d_share_cnt):
  51. """
  52. 下载视频的基本规则
  53. :param d_duration: 时长
  54. :param d_width: 宽
  55. :param d_height: 高
  56. :param d_play_cnt: 播放量
  57. :param d_like_cnt: 点赞量
  58. :param d_share_cnt: 分享量
  59. :return: 满足规则,返回 True;反之,返回 False
  60. """
  61. if 600 >= int(float(d_duration)) >= 60:
  62. if int(d_width) >= 720 or int(d_height) >= 720:
  63. if int(d_play_cnt) >= 50000:
  64. if int(d_like_cnt) >= 50000:
  65. if int(d_share_cnt) >= 2000:
  66. return True
  67. else:
  68. return False
  69. else:
  70. return False
  71. else:
  72. return False
  73. return False
  74. return False
  75. @classmethod
  76. def get_feeds(cls):
  77. """
  78. 1.从快手小程序首页推荐,获取视频列表
  79. 2.先在 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=3b207c 中去重
  80. 3.再从 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=Zt2PGQ 中去重
  81. 4.添加视频信息至 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=Zt2PGQ
  82. """
  83. url = "https://wxmini-api.uyouqu.com/rest/wd/wechatApp/feed/recommend"
  84. headers = {
  85. "content-type": "application/json",
  86. "Accept-Encoding": "gzip,compress,br,deflate",
  87. "User-Agent": 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X)'
  88. ' AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148'
  89. ' MicroMessenger/8.0.20(0x18001442) NetType/WIFI Language/zh_CN',
  90. "Referer": str(cls.Referer),
  91. }
  92. params = {
  93. "__NS_sig3": str(cls.NS_sig3),
  94. "__NS_sig3_origin": str(cls.NS_sig3_origin)
  95. }
  96. cookies = {
  97. "did": str(cls.did),
  98. "preMinaVersion": "v3.109.0",
  99. "sid": "kuaishou.wechat.app",
  100. "appId": "ks_wechat_small_app_2",
  101. "clientid": "13",
  102. "client_key": "f60ac815",
  103. "kpn": "WECHAT_SMALL_APP",
  104. "kpf": "OUTSIDE_ANDROID_H5",
  105. "language": "zh_CN",
  106. "smallAppVersion": "v3.114.0",
  107. "session_key": str(cls.session_key),
  108. "unionid": str(cls.unionid),
  109. "eUserStableOpenId": str(cls.eUserStableOpenId),
  110. "openId": str(cls.openId),
  111. "eOpenUserId": str(cls.eOpenUserId),
  112. "kuaishou.wechat.app_st": str(cls.kuaishou_wechat_app_st),
  113. "passToken": str(cls.passToken),
  114. "userId": str(cls.userId)
  115. }
  116. json_data = {
  117. "count": 10,
  118. "portal": 1,
  119. "pageType": 2,
  120. "extraRequestInfo": "{\"scene\":1089,\"fid\":\"\",\"sharerUserId\":\"\",\"curPhotoIndex\":0,"
  121. "\"adShow\":true,\"weChatAd\":{},\"headurl\":\"https://js2.a.kwimgs.com/udata/pkg"
  122. "/fe/profiel_icon_photo_normal@3x.fb3ec1af.png\",\"page\":0}",
  123. "needLivestream": True,
  124. "pcursor": 0,
  125. "sourceFrom": 2,
  126. "thirdPartyUserId": int(cls.userId)
  127. }
  128. try:
  129. urllib3.disable_warnings()
  130. r = requests.post(url=url, headers=headers, params=params,
  131. cookies=cookies, json=json_data, proxies=proxies, verify=False)
  132. response = json.loads(r.content.decode("utf8"))
  133. feeds = response["feeds"]
  134. for i in range(len(feeds)):
  135. # 视频标题过滤话题及处理特殊字符
  136. kuaishou_title = feeds[i]["caption"]
  137. title_split1 = kuaishou_title.split(" #")
  138. if title_split1[0] != "":
  139. title1 = title_split1[0]
  140. else:
  141. title1 = title_split1[-1]
  142. title_split2 = title1.split(" #")
  143. if title_split2[0] != "":
  144. title2 = title_split2[0]
  145. else:
  146. title2 = title_split2[-1]
  147. title_split3 = title2.split("@")
  148. if title_split3[0] != "":
  149. title3 = title_split3[0]
  150. else:
  151. title3 = title_split3[-1]
  152. video_title = title3.strip().replace("\n", "") \
  153. .replace("/", "").replace("快手", "").replace(" ", "") \
  154. .replace(" ", "").replace("&NBSP", "").replace("\r", "") \
  155. .replace("#", "").replace(".", "。").replace("\\", "") \
  156. .replace(":", "").replace("*", "").replace("?", "") \
  157. .replace("?", "").replace('"', "").replace("<", "") \
  158. .replace(">", "").replace("|", "")
  159. Common.logger().info("video_title:{}".format(video_title))
  160. if "photoId" not in feeds[i]:
  161. photo_id = "0"
  162. Common.logger().info("photo_id:{}".format(photo_id))
  163. else:
  164. photo_id = feeds[i]["photoId"]
  165. Common.logger().info("photo_id:{}".format(photo_id))
  166. if "viewCount" not in feeds[i]:
  167. video_play_cnt = "0"
  168. Common.logger().info("video_play_cnt:0")
  169. else:
  170. video_play_cnt = feeds[i]["viewCount"]
  171. Common.logger().info("video_play_cnt:{}".format(video_play_cnt))
  172. if "likeCount" not in feeds[i]:
  173. video_like_cnt = "0"
  174. Common.logger().info("video_like_cnt:0")
  175. else:
  176. video_like_cnt = feeds[i]["likeCount"]
  177. Common.logger().info("video_like_cnt:{}".format(video_like_cnt))
  178. if "shareCount" not in feeds[i]:
  179. video_share_cnt = "0"
  180. Common.logger().info("video_share_cnt:0")
  181. else:
  182. video_share_cnt = feeds[i]["shareCount"]
  183. Common.logger().info("video_share_cnt:{}".format(video_share_cnt))
  184. if "commentCount" not in feeds[i]:
  185. video_comment_cnt = "0"
  186. Common.logger().info("video_comment_cnt:0")
  187. else:
  188. video_comment_cnt = feeds[i]["commentCount"]
  189. Common.logger().info("video_comment_cnt:{}".format(video_comment_cnt))
  190. if "duration" not in feeds[i]:
  191. video_duration = "0"
  192. Common.logger().info("video_duration:不存在")
  193. else:
  194. video_duration = int(int(feeds[i]["duration"]) / 1000)
  195. Common.logger().info("video_duration:{}秒".format(video_duration))
  196. if "width" not in feeds[i] or "height" not in feeds[i]:
  197. video_width = "0"
  198. video_height = "0"
  199. video_resolution = str(video_width) + "*" + str(video_height)
  200. Common.logger().info("无分辨率")
  201. else:
  202. video_width = feeds[i]["width"]
  203. video_height = feeds[i]["height"]
  204. video_resolution = str(video_width) + "*" + str(video_height)
  205. Common.logger().info("video_resolution:{}".format(video_resolution))
  206. if "timestamp" not in feeds[i]:
  207. video_send_time = "0"
  208. Common.logger().info("video_send_time:不存在")
  209. else:
  210. video_send_time = feeds[i]["timestamp"]
  211. Common.logger().info("video_send_time:{}".format(
  212. time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(int(video_send_time) / 1000))))
  213. user_name = feeds[i]["userName"].strip().replace("\n", "") \
  214. .replace("/", "").replace("快手", "").replace(" ", "") \
  215. .replace(" ", "").replace("&NBSP", "").replace("\r", "")
  216. Common.logger().info("user_name:{}".format(user_name))
  217. user_id = feeds[i]["userId"]
  218. Common.logger().info("user_id:{}".format(user_id))
  219. if "headUrl" not in feeds[i]:
  220. head_url = "0"
  221. Common.logger().info("head_url:不存在")
  222. else:
  223. head_url = feeds[i]["headUrl"]
  224. Common.logger().info("head_url:{}".format(head_url))
  225. if len(feeds[i]["coverUrls"]) == 0:
  226. cover_url = "0"
  227. Common.logger().info("cover_url:不存在")
  228. else:
  229. cover_url = feeds[i]["coverUrls"][0]["url"]
  230. Common.logger().info("cover_url:{}".format(cover_url))
  231. if len(feeds[i]["mainMvUrls"]) == 0:
  232. video_url = "0"
  233. Common.logger().info("video_url:不存在")
  234. else:
  235. video_url = feeds[i]["mainMvUrls"][0]["url"]
  236. Common.logger().info("video_url:{}".format(video_url))
  237. # 过滤无效视频
  238. if photo_id == "0" \
  239. or head_url == "0" \
  240. or cover_url == "0" \
  241. or video_url == "0" \
  242. or video_duration == "0" \
  243. or video_send_time == "0" \
  244. or user_name == "" \
  245. or video_title == "":
  246. Common.logger().info("无效视频")
  247. # 判断敏感词
  248. elif any(word if word in kuaishou_title else False for word in cls.sensitive_words()) is True:
  249. Common.logger().info("视频已中敏感词:{}".format(kuaishou_title))
  250. # 从 云文档 去重:https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=3b207c
  251. elif photo_id in [j for i in Feishu.get_values_batch("3b207c") for j in i]:
  252. Common.logger().info("该视频已下载:{}", video_title)
  253. # 从 云文档 去重:https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=Zt2PGQ
  254. elif photo_id in [j for i in Feishu.get_values_batch("Zt2PGQ") for j in i]:
  255. Common.logger().info("该视频已在feeds中:{}", video_title)
  256. else:
  257. Common.logger().info("该视频未下载,添加至feeds中:{}".format(video_title))
  258. # feeds工作表,插入首行
  259. time.sleep(1)
  260. Feishu.insert_columns("Zt2PGQ", "ROWS", 1, 2)
  261. # 获取当前时间
  262. get_feeds_time = int(time.time())
  263. # 看一看云文档,工作表 kanyikan_feeds_1 中写入数据
  264. values = [[str(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(get_feeds_time))),
  265. photo_id,
  266. video_title,
  267. video_play_cnt,
  268. video_comment_cnt,
  269. video_like_cnt,
  270. video_share_cnt,
  271. video_duration,
  272. video_resolution,
  273. time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(int(video_send_time) / 1000)),
  274. user_name,
  275. user_id,
  276. head_url,
  277. cover_url,
  278. video_url]]
  279. # 等待 1s,防止操作云文档太频繁,导致报错
  280. time.sleep(1)
  281. Feishu.update_values("Zt2PGQ", "A2:P2", values)
  282. except Exception as e:
  283. Common.logger().error("获取视频 list 异常:{}".format(e))
  284. @classmethod
  285. def download_publish(cls, env):
  286. """
  287. 1.从 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=Zt2PGQ 中读取视频信息
  288. 2.下载并上传符合规则的视频
  289. 测试环境:env == dev
  290. 正式环境:env == prod
  291. """
  292. try:
  293. if len(Feishu.get_values_batch("Zt2PGQ")) == 1:
  294. pass
  295. else:
  296. for i in range(1, len(Feishu.get_values_batch("Zt2PGQ")) + 1):
  297. time.sleep(1)
  298. try:
  299. download_photo_id = Feishu.get_values_batch("Zt2PGQ")[i][1]
  300. # Common.logger().info("download_photo_id:{}", download_photo_id)
  301. download_video_title = Feishu.get_values_batch("Zt2PGQ")[i][2]
  302. # Common.logger().info("download_video_title:{}", download_video_title)
  303. download_video_play_cnt = Feishu.get_values_batch("Zt2PGQ")[i][3]
  304. # Common.logger().info("download_video_play_cnt:{}", download_video_play_cnt)
  305. download_video_comment_cnt = Feishu.get_values_batch("Zt2PGQ")[i][4]
  306. # Common.logger().info("download_video_comment_cnt:{}", download_video_comment_cnt)
  307. download_video_like_cnt = Feishu.get_values_batch("Zt2PGQ")[i][5]
  308. # Common.logger().info("download_video_like_cnt:{}", download_video_like_cnt)
  309. download_video_share_cnt = Feishu.get_values_batch("Zt2PGQ")[i][6]
  310. # Common.logger().info("download_video_share_cnt:{}", download_video_share_cnt)
  311. download_video_duration = Feishu.get_values_batch("Zt2PGQ")[i][7]
  312. # Common.logger().info("download_video_duration:{}", download_video_duration)
  313. download_video_resolution = Feishu.get_values_batch("Zt2PGQ")[i][8]
  314. # Common.logger().info("download_video_resolution:{}", download_video_resolution)
  315. download_video_width = download_video_resolution.split("*")[0]
  316. download_video_height = download_video_resolution.split("*")[-1]
  317. download_video_send_time = Feishu.get_values_batch("Zt2PGQ")[i][9]
  318. # Common.logger().info("download_video_send_time:{}", download_video_send_time)
  319. download_user_name = Feishu.get_values_batch("Zt2PGQ")[i][10]
  320. # Common.logger().info("download_user_name:{}", download_user_name)
  321. download_user_id = Feishu.get_values_batch("Zt2PGQ")[i][11]
  322. # Common.logger().info("download_user_id:{}", download_user_id)
  323. download_head_url = Feishu.get_values_batch("Zt2PGQ")[i][12][0]["link"]
  324. # Common.logger().info("download_head_url:{}", download_head_url)
  325. download_cover_url = Feishu.get_values_batch("Zt2PGQ")[i][13][0]["link"]
  326. # Common.logger().info("download_cover_url:{}", download_cover_url)
  327. download_video_url = Feishu.get_values_batch("Zt2PGQ")[i][14][0]["link"]
  328. # Common.logger().info("download_video_url:{}", download_video_url)
  329. # 过滤空行
  330. if download_photo_id is None or download_video_title is None or download_video_play_cnt is None:
  331. Common.logger().warning("空行,略过")
  332. # 去重
  333. elif download_photo_id in [j for i in Feishu.get_values_batch("3b207c") for j in i]:
  334. Common.logger().info("该视频已下载:{}", download_video_title)
  335. # 下载规则
  336. elif cls.kuaishou_download_rule(
  337. download_video_duration, download_video_width, download_video_height,
  338. download_video_play_cnt, download_video_like_cnt, download_video_share_cnt) is True:
  339. Common.logger().info("开始下载快手视频:{}".format(download_video_title))
  340. # 下载封面
  341. Common.download_method(text="cover",
  342. d_name=str(download_video_title), d_url=str(download_cover_url))
  343. # 下载视频
  344. Common.download_method(text="video",
  345. d_name=str(download_video_title), d_url=str(download_video_url))
  346. # 保存视频信息至 "./videos/{download_video_title}/info.txt"
  347. with open("./videos/" + download_video_title
  348. + "/" + "info.txt", "a", encoding="UTF-8") as f_a:
  349. f_a.write(str(download_photo_id) + "\n" +
  350. str(download_video_title) + "\n" +
  351. str(download_video_duration) + "\n" +
  352. str(download_video_play_cnt) + "\n" +
  353. str(download_video_comment_cnt) + "\n" +
  354. str(download_video_like_cnt) + "\n" +
  355. str(download_video_share_cnt) + "\n" +
  356. str(download_video_resolution) + "\n" +
  357. str(int(time.mktime(
  358. time.strptime(download_video_send_time, "%Y/%m/%d %H:%M:%S")))) + "\n" +
  359. str(download_user_name) + "\n" +
  360. str(download_head_url) + "\n" +
  361. str(download_video_url) + "\n" +
  362. str(download_cover_url) + "\n" +
  363. str(cls.did))
  364. Common.logger().info("==========视频信息已保存至info.txt==========")
  365. # 添加视频 ID 到 list,用于统计当次下载总数
  366. cls.download_video_list.append(download_photo_id)
  367. # 上传视频
  368. Common.logger().info("开始上传视频:{}".format(download_video_title))
  369. Publish.upload_and_publish(env, "play")
  370. # 保存视频 ID 到云文档:https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=3b207c
  371. Common.logger().info("保存视频ID至云文档:{}", download_video_title)
  372. # 视频ID工作表,插入首行
  373. Feishu.insert_columns("3b207c", "ROWS", 1, 2)
  374. # 视频ID工作表,首行写入数据
  375. upload_time = int(time.time())
  376. values = [[str(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(upload_time))),
  377. str(download_photo_id),
  378. str(download_video_title),
  379. str(download_video_play_cnt),
  380. str(download_video_comment_cnt),
  381. str(download_video_like_cnt),
  382. str(download_video_share_cnt),
  383. str(download_video_duration),
  384. str(download_video_resolution),
  385. str(download_video_send_time),
  386. str(download_user_name),
  387. str(download_user_id),
  388. str(download_head_url),
  389. str(download_cover_url),
  390. str(download_video_url)]]
  391. time.sleep(1)
  392. Feishu.update_values("3b207c", "A2:Q2", values)
  393. # 从云文档删除该视频信息:https://w42nne6hzg.feishu.cn/sheets/shtcngRPoDYAi24x52j2nDuHMih?sheet=Zt2PGQ
  394. Common.logger().info("从云文档删除该视频信息:{}", download_video_title)
  395. # 删除行或列,可选 ROWS、COLUMNS
  396. time.sleep(1)
  397. Feishu.dimension_range("Zt2PGQ", "ROWS", i + 1, i + 1)
  398. else:
  399. # 从云文档删除该视频信息:https://w42nne6hzg.feishu.cn/sheets/shtcngRPoDYAi24x52j2nDuHMih?sheet=Zt2PGQ
  400. Common.logger().info("该视频不满足下载规则,删除在云文档中的信息:{}", download_video_title)
  401. # 删除行或列,可选 ROWS、COLUMNS
  402. Feishu.dimension_range("Zt2PGQ", "ROWS", i + 1, i + 1)
  403. except Exception as e:
  404. Common.logger().error("视频 info 异常,删除该视频信息", e)
  405. # 删除行或列,可选 ROWS、COLUMNS
  406. Feishu.dimension_range("Zt2PGQ", "ROWS", i + 1, i + 1)
  407. cls.download_publish("prod")
  408. except Exception as e:
  409. Common.logger().error(e)
  410. if __name__ == "__main__":
  411. kuaishou = KuaiShou()
  412. kuaishou.get_feeds()
  413. kuaishou.download_publish("dev")