recommend_list.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  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. wechat_sheet = Feishu.get_values_batch("recommend", "kuaishou", "WFF4jw")
  21. Referer = wechat_sheet[2][2]
  22. NS_sig3 = wechat_sheet[3][2]
  23. NS_sig3_origin = wechat_sheet[4][2]
  24. did = wechat_sheet[5][2]
  25. session_key = wechat_sheet[6][2]
  26. unionid = wechat_sheet[7][2]
  27. eUserStableOpenId = wechat_sheet[8][2]
  28. openId = wechat_sheet[9][2]
  29. eOpenUserId = wechat_sheet[10][2]
  30. kuaishou_wechat_app_st = wechat_sheet[11][2]
  31. passToken = wechat_sheet[12][2]
  32. userId = wechat_sheet[13][2]
  33. @classmethod
  34. def sensitive_words(cls):
  35. # 敏感词库列表
  36. word_list = []
  37. # 从云文档读取所有敏感词,添加到词库列表
  38. lists = Feishu.get_values_batch("recommend", "kuaishou", "HIKVvs")
  39. for i in lists:
  40. for j in i:
  41. # 过滤空的单元格内容
  42. if j is None:
  43. pass
  44. else:
  45. word_list.append(j)
  46. return word_list
  47. @staticmethod
  48. def kuaishou_download_rule(d_duration, d_width, d_height, d_play_cnt, d_like_cnt, d_share_cnt):
  49. """
  50. 下载视频的基本规则
  51. :param d_duration: 时长
  52. :param d_width: 宽
  53. :param d_height: 高
  54. :param d_play_cnt: 播放量
  55. :param d_like_cnt: 点赞量
  56. :param d_share_cnt: 分享量
  57. :return: 满足规则,返回 True;反之,返回 False
  58. """
  59. if 600 >= int(float(d_duration)) >= 60:
  60. if int(d_width) >= 720 or int(d_height) >= 720:
  61. if int(d_play_cnt) >= 50000:
  62. if int(d_like_cnt) >= 50000:
  63. if int(d_share_cnt) >= 2000:
  64. return True
  65. else:
  66. return False
  67. else:
  68. return False
  69. else:
  70. return False
  71. else:
  72. return False
  73. else:
  74. return False
  75. @classmethod
  76. def get_feeds(cls, log_type):
  77. """
  78. 1.从快手小程序首页推荐,获取视频列表
  79. 2.先在 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=3cd128 中去重
  80. 3.再从 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=JK6npf 中去重
  81. 4.添加视频信息至 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=JK6npf
  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(" ", "").replace(" ", "") \
  154. .replace("&NBSP", "").replace("\r", "").replace("#", "").replace(".", "。") \
  155. .replace("\\", "").replace(":", "").replace("*", "").replace("?", "") \
  156. .replace("?", "").replace('"', "").replace("<", "").replace(">", "").replace("|", "")[
  157. :40]
  158. if "photoId" not in feeds[i]:
  159. video_id = "0"
  160. else:
  161. video_id = feeds[i]["photoId"]
  162. if "viewCount" not in feeds[i]:
  163. video_play_cnt = "0"
  164. else:
  165. video_play_cnt = feeds[i]["viewCount"]
  166. if "likeCount" not in feeds[i]:
  167. video_like_cnt = "0"
  168. else:
  169. video_like_cnt = feeds[i]["likeCount"]
  170. if "shareCount" not in feeds[i]:
  171. video_share_cnt = "0"
  172. else:
  173. video_share_cnt = feeds[i]["shareCount"]
  174. if "commentCount" not in feeds[i]:
  175. video_comment_cnt = "0"
  176. else:
  177. video_comment_cnt = feeds[i]["commentCount"]
  178. if "duration" not in feeds[i]:
  179. video_duration = "0"
  180. else:
  181. video_duration = int(int(feeds[i]["duration"]) / 1000)
  182. if "width" not in feeds[i] or "height" not in feeds[i]:
  183. video_width = "0"
  184. video_height = "0"
  185. video_resolution = str(video_width) + "*" + str(video_height)
  186. else:
  187. video_width = feeds[i]["width"]
  188. video_height = feeds[i]["height"]
  189. video_resolution = str(video_width) + "*" + str(video_height)
  190. if "timestamp" not in feeds[i]:
  191. video_send_time = "0"
  192. else:
  193. video_send_time = feeds[i]["timestamp"]
  194. user_name = feeds[i]["userName"].strip().replace("\n", "") \
  195. .replace("/", "").replace("快手", "").replace(" ", "") \
  196. .replace(" ", "").replace("&NBSP", "").replace("\r", "")
  197. user_id = feeds[i]["userId"]
  198. if "headUrl" not in feeds[i]:
  199. head_url = "0"
  200. else:
  201. head_url = feeds[i]["headUrl"]
  202. if len(feeds[i]["coverUrls"]) == 0:
  203. cover_url = "0"
  204. else:
  205. cover_url = feeds[i]["coverUrls"][0]["url"]
  206. if len(feeds[i]["mainMvUrls"]) == 0:
  207. video_url = "0"
  208. else:
  209. video_url = feeds[i]["mainMvUrls"][0]["url"]
  210. Common.logger(log_type).info("video_title:{}".format(video_title))
  211. Common.logger(log_type).info("user_name:{}".format(user_name))
  212. Common.logger(log_type).info("video_id:{}".format(video_id))
  213. Common.logger(log_type).info("video_play_cnt:{}".format(video_play_cnt))
  214. Common.logger(log_type).info("video_like_cnt:{}".format(video_like_cnt))
  215. Common.logger(log_type).info("video_share_cnt:{}".format(video_share_cnt))
  216. # Common.logger(log_type).info("video_comment_cnt:{}".format(video_comment_cnt))
  217. Common.logger(log_type).info("video_duration:{}秒".format(video_duration))
  218. # Common.logger(log_type).info("video_resolution:{}".format(video_resolution))
  219. Common.logger(log_type).info("video_send_time:{}".format(
  220. time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(int(video_send_time) / 1000))))
  221. # Common.logger(log_type).info("user_id:{}".format(user_id))
  222. # Common.logger(log_type).info("head_url:{}".format(head_url))
  223. # Common.logger(log_type).info("cover_url:{}".format(cover_url))
  224. Common.logger(log_type).info("video_url:{}".format(video_url))
  225. # 过滤无效视频
  226. if video_id == "0" \
  227. or head_url == "0" \
  228. or cover_url == "0" \
  229. or video_url == "0" \
  230. or video_duration == "0" \
  231. or video_send_time == "0" \
  232. or user_name == "" \
  233. or video_title == "":
  234. Common.logger(log_type).info("无效视频\n")
  235. # 判断敏感词
  236. elif any(word if word in kuaishou_title else False for word in cls.sensitive_words()) is True:
  237. Common.logger(log_type).info("视频已中敏感词:{}\n".format(kuaishou_title))
  238. # 从 云文档 去重:https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=3cd128
  239. elif str(video_id) in [j for m in Feishu.get_values_batch(log_type, "kuaishou", "3cd128") for j in m]:
  240. Common.logger(log_type).info("该视频已下载:{}\n", video_title)
  241. # 从 云文档 去重:https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=JK6npf
  242. elif str(video_id) in [j for n in Feishu.get_values_batch(log_type, "kuaishou", "JK6npf") for j in n]:
  243. Common.logger(log_type).info("该视频已在feeds中:{}\n", video_title)
  244. else:
  245. # feeds工作表,插入首行
  246. time.sleep(1)
  247. Feishu.insert_columns(log_type, "kuaishou", "JK6npf", "ROWS", 1, 2)
  248. # 获取当前时间
  249. get_feeds_time = int(time.time())
  250. # 看一看云文档,工作表 kanyikan_feeds_1 中写入数据
  251. values = [[str(time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(get_feeds_time))),
  252. "推荐榜",
  253. str(video_id),
  254. video_title,
  255. video_play_cnt,
  256. video_comment_cnt,
  257. video_like_cnt,
  258. video_share_cnt,
  259. video_duration,
  260. video_resolution,
  261. time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(int(video_send_time) / 1000)),
  262. user_name,
  263. user_id,
  264. head_url,
  265. cover_url,
  266. video_url]]
  267. # 等待 1s,防止操作云文档太频繁,导致报错
  268. time.sleep(1)
  269. Feishu.update_values(log_type, "kuaishou", "JK6npf", "A2:P2", values)
  270. Common.logger(log_type).info("视频:{}添加至feeds成功\n".format(video_title))
  271. except Exception as e:
  272. # Feishu.bot(log_type, "recommend:get_feeds异常"+format(e))
  273. Common.logger(log_type).error("获取视频 list 异常:{}\n".format(e))
  274. @classmethod
  275. def download_publish(cls, log_type, env):
  276. """
  277. 1.从 https://w42nne6hzg.feishu.cn/sheets/shtcnp4SaJt37q6OOOrYzPMjQkg?sheet=JK6npf 中读取视频信息
  278. 2.下载并上传符合规则的视频
  279. 测试环境:env == dev
  280. 正式环境:env == prod
  281. """
  282. try:
  283. recommend_feeds_sheet = Feishu.get_values_batch(log_type, "kuaishou", "JK6npf")
  284. for i in range(1, len(recommend_feeds_sheet) + 1):
  285. download_video_id = recommend_feeds_sheet[i][2]
  286. download_video_title = recommend_feeds_sheet[i][3]
  287. download_video_play_cnt = recommend_feeds_sheet[i][4]
  288. download_video_comment_cnt = recommend_feeds_sheet[i][5]
  289. download_video_like_cnt = recommend_feeds_sheet[i][6]
  290. download_video_share_cnt = recommend_feeds_sheet[i][7]
  291. download_video_duration = recommend_feeds_sheet[i][8]
  292. download_video_resolution = recommend_feeds_sheet[i][9]
  293. download_video_send_time = recommend_feeds_sheet[i][10]
  294. download_user_name = recommend_feeds_sheet[i][11]
  295. download_user_id = recommend_feeds_sheet[i][12]
  296. download_head_url = recommend_feeds_sheet[i][13]
  297. download_cover_url = recommend_feeds_sheet[i][14]
  298. download_video_url = recommend_feeds_sheet[i][15]
  299. Common.logger(log_type).info("正在判断第{}行,视频:{}", i + 1, download_video_title)
  300. # Common.logger(log_type).info("download_video_id:{}", download_video_id)
  301. # Common.logger(log_type).info("download_video_title:{}", download_video_title)
  302. # Common.logger(log_type).info("download_video_play_cnt:{}", download_video_play_cnt)
  303. # Common.logger(log_type).info("download_video_comment_cnt:{}", download_video_comment_cnt)
  304. # Common.logger(log_type).info("download_video_like_cnt:{}", download_video_like_cnt)
  305. # Common.logger(log_type).info("download_video_share_cnt:{}", download_video_share_cnt)
  306. # Common.logger(log_type).info("download_video_duration:{}", download_video_duration)
  307. # Common.logger(log_type).info("download_video_resolution:{}", download_video_resolution)
  308. # Common.logger(log_type).info("download_video_send_time:{}", download_video_send_time)
  309. # Common.logger(log_type).info("download_user_name:{}", download_user_name)
  310. # Common.logger(log_type).info("download_user_id:{}", download_user_id)
  311. # Common.logger(log_type).info("download_head_url:{}", download_head_url)
  312. # Common.logger(log_type).info("download_cover_url:{}", download_cover_url)
  313. # Common.logger(log_type).info("download_video_url:{}", download_video_url)
  314. # 过滤空行
  315. if download_video_id is None or download_video_title is None or download_video_play_cnt is None:
  316. # 删除行或列,可选 ROWS、COLUMNS
  317. Feishu.dimension_range(log_type, "kuaishou", "JK6npf", "ROWS", i + 1, i + 1)
  318. Common.logger(log_type).info("空行,删除成功\n")
  319. return
  320. # 去重
  321. elif str(download_video_id) in [j for m in Feishu.get_values_batch(log_type, "kuaishou", "3cd128") for j
  322. in m]:
  323. # 删除行或列,可选 ROWS、COLUMNS
  324. Feishu.dimension_range(log_type, "kuaishou", "JK6npf", "ROWS", i + 1, i + 1)
  325. Common.logger(log_type).info("该视频已下载:{},删除成功\n", download_video_title)
  326. return
  327. # 下载规则
  328. elif cls.kuaishou_download_rule(
  329. download_video_duration, download_video_resolution.split("*")[0],
  330. download_video_resolution.split("*")[-1],
  331. download_video_play_cnt, download_video_like_cnt, download_video_share_cnt) is True:
  332. # 下载封面
  333. Common.download_method(log_type=log_type, text="cover",
  334. d_name=str(download_video_title), d_url=str(download_cover_url))
  335. # 下载视频
  336. Common.download_method(log_type=log_type, text="video",
  337. d_name=str(download_video_title), d_url=str(download_video_url))
  338. # 保存视频信息至 "./videos/{download_video_title}/info.txt"
  339. with open("./videos/" + download_video_title
  340. + "/" + "info.txt", "a", encoding="UTF-8") as f_a:
  341. f_a.write(str(download_video_id) + "\n" +
  342. str(download_video_title) + "\n" +
  343. str(download_video_duration) + "\n" +
  344. str(download_video_play_cnt) + "\n" +
  345. str(download_video_comment_cnt) + "\n" +
  346. str(download_video_like_cnt) + "\n" +
  347. str(download_video_share_cnt) + "\n" +
  348. str(download_video_resolution) + "\n" +
  349. str(int(time.mktime(
  350. time.strptime(download_video_send_time, "%Y/%m/%d %H:%M:%S")))) + "\n" +
  351. str(download_user_name) + "\n" +
  352. str(download_head_url) + "\n" +
  353. str(download_video_url) + "\n" +
  354. str(download_cover_url) + "\n" +
  355. str(cls.did))
  356. Common.logger(log_type).info("==========视频信息已保存至info.txt==========")
  357. # 上传视频
  358. Common.logger(log_type).info("开始上传视频:{}".format(download_video_title))
  359. our_video_id = Publish.upload_and_publish(log_type, env, "play")
  360. our_video_link = "https://admin.piaoquantv.com/cms/post-detail/" + str(our_video_id) + "/info"
  361. Common.logger(log_type).info("视频上传完成:{}", download_video_title)
  362. # 视频ID工作表,插入首行
  363. time.sleep(1)
  364. Feishu.insert_columns(log_type, "kuaishou", "3cd128", "ROWS", 1, 2)
  365. # 视频ID工作表,首行写入数据
  366. upload_time = int(time.time())
  367. values = [[our_video_id,
  368. time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(upload_time)),
  369. "推荐榜",
  370. str(download_video_id),
  371. str(download_video_title),
  372. our_video_link,
  373. download_video_play_cnt,
  374. download_video_comment_cnt,
  375. download_video_like_cnt,
  376. download_video_share_cnt,
  377. download_video_duration,
  378. str(download_video_resolution),
  379. str(download_video_send_time),
  380. str(download_user_name),
  381. str(download_user_id),
  382. str(download_head_url),
  383. str(download_cover_url),
  384. str(download_video_url)]]
  385. time.sleep(1)
  386. Feishu.update_values(log_type, "kuaishou", "3cd128", "E2:Z2", values)
  387. Common.logger(log_type).info("视频保存至云文档成功:{}", download_video_title)
  388. # 删除行或列,可选 ROWS、COLUMNS
  389. time.sleep(1)
  390. Feishu.dimension_range(log_type, "kuaishou", "JK6npf", "ROWS", i + 1, i + 1)
  391. Common.logger(log_type).info("从云文档删除该视频信息成功:{}\n", download_video_title)
  392. return
  393. else:
  394. # 删除行或列,可选 ROWS、COLUMNS
  395. Feishu.dimension_range(log_type, "kuaishou", "JK6npf", "ROWS", i + 1, i + 1)
  396. Common.logger(log_type).info("该视频不满足下载规则,删除成功:{}\n", download_video_title)
  397. return
  398. except Exception as e:
  399. # Feishu.bot(log_type, "recommend:download_publish异常" + format(e))
  400. Common.logger(log_type).error("视频 info 异常,删除该视频信息\n", e)
  401. # 删除行或列,可选 ROWS、COLUMNS
  402. Feishu.dimension_range(log_type, "kuaishou", "JK6npf", "ROWS", 2, 2)
  403. return
  404. # 执行上传及下载
  405. @classmethod
  406. def run_download_publish(cls, log_type, env):
  407. try:
  408. while True:
  409. if len(Feishu.get_values_batch(log_type, "kuaishou", "JK6npf")) == 1:
  410. break
  411. else:
  412. cls.download_publish(log_type, env)
  413. except Exception as e:
  414. Common.logger(log_type).error("执行下载/上传异常:{}", e)
  415. if __name__ == "__main__":
  416. kuaishou = KuaiShou()
  417. print(kuaishou.sensitive_words())