kuaishou_follow.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. # -*- coding: utf-8 -*-
  2. # @Author: wangkun
  3. # @Time: 2023/2/24
  4. import os
  5. # import random
  6. import shutil
  7. # import string
  8. import sys
  9. import time
  10. from hashlib import md5
  11. import requests
  12. import json
  13. import urllib3
  14. from requests.adapters import HTTPAdapter
  15. sys.path.append(os.getcwd())
  16. from common.common import Common
  17. from common.feishu import Feishu
  18. from common.getuser import getUser
  19. from common.db import MysqlHelper
  20. from common.publish import Publish
  21. from common.public import random_title, get_config_from_mysql
  22. from common.public import get_user_from_mysql
  23. class KuaiShouFollow:
  24. platform = "快手"
  25. tag = "快手爬虫,定向爬虫策略"
  26. @classmethod
  27. def get_rule(cls, log_type, crawler, index):
  28. try:
  29. rule_sheet = Feishu.get_values_batch(log_type, crawler, "3iqG4z")
  30. if index == 1:
  31. rule_dict = {
  32. "play_cnt": f"{rule_sheet[1][1]}{rule_sheet[1][2]}",
  33. "video_width": f"{rule_sheet[2][1]}{rule_sheet[2][2]}",
  34. "video_height": f"{rule_sheet[3][1]}{rule_sheet[3][2]}",
  35. "like_cnt": f"{rule_sheet[4][1]}{rule_sheet[4][2]}",
  36. "duration": f"{rule_sheet[5][1]}{rule_sheet[5][2]}",
  37. "download_cnt": f"{rule_sheet[6][1]}{rule_sheet[6][2]}",
  38. "publish_time": f"{rule_sheet[7][1]}{rule_sheet[7][2]}",
  39. }
  40. # for k, v in rule_dict.items():
  41. # Common.logger(log_type, crawler).info(f"{k}:{v}")
  42. return rule_dict
  43. elif index == 2:
  44. rule_dict = {
  45. "play_cnt": f"{rule_sheet[9][1]}{rule_sheet[9][2]}",
  46. "video_width": f"{rule_sheet[10][1]}{rule_sheet[10][2]}",
  47. "video_height": f"{rule_sheet[11][1]}{rule_sheet[11][2]}",
  48. "like_cnt": f"{rule_sheet[12][1]}{rule_sheet[12][2]}",
  49. "duration": f"{rule_sheet[13][1]}{rule_sheet[13][2]}",
  50. "download_cnt": f"{rule_sheet[14][1]}{rule_sheet[14][2]}",
  51. "publish_time": f"{rule_sheet[15][1]}{rule_sheet[15][2]}",
  52. }
  53. # for k, v in rule_dict.items():
  54. # Common.logger(log_type, crawler).info(f"{k}:{v}")
  55. return rule_dict
  56. except Exception as e:
  57. Common.logger(log_type, crawler).error(f"get_rule:{e}\n")
  58. @classmethod
  59. def download_rule(cls, video_dict, rule_dict):
  60. if eval(f"{video_dict['play_cnt']}{rule_dict['play_cnt']}") is True \
  61. and eval(f"{video_dict['video_width']}{rule_dict['video_width']}") is True \
  62. and eval(f"{video_dict['video_height']}{rule_dict['video_height']}") is True \
  63. and eval(f"{video_dict['like_cnt']}{rule_dict['like_cnt']}") is True \
  64. and eval(f"{video_dict['duration']}{rule_dict['duration']}") is True \
  65. and eval(f"{video_dict['publish_time']}{rule_dict['publish_time']}") is True:
  66. return True
  67. else:
  68. return False
  69. # 过滤词库
  70. @classmethod
  71. def filter_words(cls, log_type, crawler):
  72. try:
  73. while True:
  74. filter_words_sheet = Feishu.get_values_batch(log_type, crawler, 'HIKVvs')
  75. if filter_words_sheet is None:
  76. Common.logger(log_type, crawler).warning(f"filter_words_sheet:{filter_words_sheet} 10秒钟后重试")
  77. continue
  78. filter_words_list = []
  79. for x in filter_words_sheet:
  80. for y in x:
  81. if y is None:
  82. pass
  83. else:
  84. filter_words_list.append(y)
  85. return filter_words_list
  86. except Exception as e:
  87. Common.logger(log_type, crawler).error(f'filter_words异常:{e}\n')
  88. # 获取站外用户信息
  89. @classmethod
  90. def get_out_user_info(cls, log_type, crawler, out_uid):
  91. try:
  92. url = "https://www.kuaishou.com/graphql"
  93. payload = json.dumps({
  94. "operationName": "visionProfile",
  95. "variables": {
  96. "userId": out_uid
  97. },
  98. "query": "query visionProfile($userId: String) {\n visionProfile(userId: $userId) {\n result\n hostName\n userProfile {\n ownerCount {\n fan\n photo\n follow\n photo_public\n __typename\n }\n profile {\n gender\n user_name\n user_id\n headurl\n user_text\n user_profile_bg_url\n __typename\n }\n isFollowing\n __typename\n }\n __typename\n }\n}\n"
  99. })
  100. # s = string.ascii_lowercase
  101. # r = random.choice(s)
  102. headers = {
  103. 'Accept': '*/*',
  104. 'Content-Type': 'application/json',
  105. 'Origin': 'https://www.kuaishou.com',
  106. 'Cookie': f'kpf=PC_WEB; clientid=3; did={cls.get_did(log_type, crawler)}; kpn=KUAISHOU_VISION',
  107. 'Content-Length': '552',
  108. 'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
  109. 'Host': 'www.kuaishou.com',
  110. 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15',
  111. 'Referer': 'https://www.kuaishou.com/profile/{}'.format(out_uid),
  112. 'Accept-Encoding': 'gzip, deflate, br',
  113. 'Connection': 'keep-alive'
  114. }
  115. urllib3.disable_warnings()
  116. s = requests.session()
  117. # max_retries=3 重试3次
  118. s.mount('http://', HTTPAdapter(max_retries=3))
  119. s.mount('https://', HTTPAdapter(max_retries=3))
  120. response = s.post(url=url, headers=headers, data=payload, proxies=Common.tunnel_proxies(), verify=False,
  121. timeout=5)
  122. response.close()
  123. # Common.logger(log_type, crawler).info(f"get_out_user_info_response:{response.text}")
  124. if response.status_code != 200:
  125. Common.logger(log_type, crawler).warning(f"get_out_user_info_response:{response.text}\n")
  126. return
  127. elif 'data' not in response.json():
  128. Common.logger(log_type, crawler).warning(f"get_out_user_info_response:{response.json()}\n")
  129. return
  130. elif 'visionProfile' not in response.json()['data']:
  131. Common.logger(log_type, crawler).warning(f"get_out_user_info_response:{response.json()['data']}\n")
  132. return
  133. elif 'userProfile' not in response.json()['data']['visionProfile']:
  134. Common.logger(log_type, crawler).warning(
  135. f"get_out_user_info_response:{response.json()['data']['visionProfile']['userProfile']}\n")
  136. return
  137. else:
  138. userProfile = response.json()['data']['visionProfile']['userProfile']
  139. # Common.logger(log_type, crawler).info(f"userProfile:{userProfile}")
  140. try:
  141. out_fans_str = str(userProfile['ownerCount']['fan'])
  142. except Exception:
  143. out_fans_str = "0"
  144. try:
  145. out_follow_str = str(userProfile['ownerCount']['follow'])
  146. except Exception:
  147. out_follow_str = "0"
  148. try:
  149. out_avatar_url = userProfile['profile']['headurl']
  150. except Exception:
  151. out_avatar_url = ""
  152. Common.logger(log_type, crawler).info(f"out_fans_str:{out_fans_str}")
  153. Common.logger(log_type, crawler).info(f"out_follow_str:{out_follow_str}")
  154. Common.logger(log_type, crawler).info(f"out_avatar_url:{out_avatar_url}")
  155. if "万" in out_fans_str:
  156. out_fans = int(float(out_fans_str.split("万")[0]) * 10000)
  157. else:
  158. out_fans = int(out_fans_str.replace(",", ""))
  159. if "万" in out_follow_str:
  160. out_follow = int(float(out_follow_str.split("万")[0]) * 10000)
  161. else:
  162. out_follow = int(out_follow_str.replace(",", ""))
  163. out_user_dict = {
  164. "out_fans": out_fans,
  165. "out_follow": out_follow,
  166. "out_avatar_url": out_avatar_url
  167. }
  168. Common.logger(log_type, crawler).info(f"out_user_dict:{out_user_dict}")
  169. return out_user_dict
  170. except Exception as e:
  171. Common.logger(log_type, crawler).error(f"get_out_user_info:{e}\n")
  172. # 获取用户信息列表
  173. @classmethod
  174. def get_user_list(cls, log_type, crawler, sheetid, env, machine):
  175. try:
  176. while True:
  177. user_sheet = Feishu.get_values_batch(log_type, crawler, sheetid)
  178. if user_sheet is None:
  179. Common.logger(log_type, crawler).warning(f"user_sheet:{user_sheet} 10秒钟后重试")
  180. continue
  181. our_user_list = []
  182. for i in range(1, len(user_sheet)):
  183. # for i in range(1, 2):
  184. out_uid = user_sheet[i][2]
  185. user_name = user_sheet[i][3]
  186. our_uid = user_sheet[i][6]
  187. our_user_link = user_sheet[i][7]
  188. if out_uid is None or user_name is None:
  189. Common.logger(log_type, crawler).info("空行\n")
  190. else:
  191. Common.logger(log_type, crawler).info(f"正在更新 {user_name} 用户信息\n")
  192. if our_uid is None:
  193. out_user_info = cls.get_out_user_info(log_type, crawler, out_uid)
  194. out_user_dict = {
  195. "out_uid": out_uid,
  196. "user_name": user_name,
  197. "out_avatar_url": out_user_info["out_avatar_url"],
  198. "out_create_time": '',
  199. "out_tag": '',
  200. "out_play_cnt": 0,
  201. "out_fans": out_user_info["out_fans"],
  202. "out_follow": out_user_info["out_follow"],
  203. "out_friend": 0,
  204. "out_like": 0,
  205. "platform": cls.platform,
  206. "tag": cls.tag,
  207. }
  208. our_user_dict = getUser.create_user(log_type=log_type, crawler=crawler,
  209. out_user_dict=out_user_dict, env=env, machine=machine)
  210. our_uid = our_user_dict['our_uid']
  211. our_user_link = our_user_dict['our_user_link']
  212. Feishu.update_values(log_type, crawler, sheetid, f'G{i + 1}:H{i + 1}',
  213. [[our_uid, our_user_link]])
  214. Common.logger(log_type, crawler).info(f'站内用户信息写入飞书成功!\n')
  215. our_user_list.append(our_user_dict)
  216. else:
  217. our_user_dict = {
  218. 'out_uid': out_uid,
  219. 'user_name': user_name,
  220. 'our_uid': our_uid,
  221. 'our_user_link': our_user_link,
  222. }
  223. our_user_list.append(our_user_dict)
  224. return our_user_list
  225. except Exception as e:
  226. Common.logger(log_type, crawler).error(f'get_user_list:{e}\n')
  227. # 处理视频标题
  228. @classmethod
  229. def video_title(cls, log_type, crawler, env, title):
  230. title_split1 = title.split(" #")
  231. if title_split1[0] != "":
  232. title1 = title_split1[0]
  233. else:
  234. title1 = title_split1[-1]
  235. title_split2 = title1.split(" #")
  236. if title_split2[0] != "":
  237. title2 = title_split2[0]
  238. else:
  239. title2 = title_split2[-1]
  240. title_split3 = title2.split("@")
  241. if title_split3[0] != "":
  242. title3 = title_split3[0]
  243. else:
  244. title3 = title_split3[-1]
  245. video_title = title3.strip().replace("\n", "") \
  246. .replace("/", "").replace("快手", "").replace(" ", "") \
  247. .replace(" ", "").replace("&NBSP", "").replace("\r", "") \
  248. .replace("#", "").replace(".", "。").replace("\\", "") \
  249. .replace(":", "").replace("*", "").replace("?", "") \
  250. .replace("?", "").replace('"', "").replace("<", "") \
  251. .replace(">", "").replace("|", "").replace("@", "").replace('"', '').replace("'", '')[:40]
  252. if video_title.replace(" ", "") == "" or video_title == "。。。" or video_title == "...":
  253. return random_title(log_type, crawler, env, text='title')
  254. else:
  255. return video_title
  256. @classmethod
  257. def get_did(cls, log_type, crawler):
  258. while True:
  259. did_sheet = Feishu.get_values_batch(log_type, crawler, "G7acT6")
  260. if did_sheet is None:
  261. Common.logger(log_type, crawler).warning(f"did_sheet:{did_sheet}")
  262. time.sleep(2)
  263. continue
  264. return did_sheet[0][1]
  265. @classmethod
  266. def get_videoList(cls, log_type, crawler, strategy, our_uid, out_uid, oss_endpoint, env, machine, pcursor=""):
  267. download_cnt_1, download_cnt_2 = 0, 0
  268. rule_dict_1 = cls.get_rule(log_type, crawler, 1)
  269. rule_dict_2 = cls.get_rule(log_type, crawler, 2)
  270. if rule_dict_1 is None or rule_dict_2 is None:
  271. Common.logger(log_type, crawler).warning(f"rule_dict is None")
  272. return
  273. url = "https://www.kuaishou.com/graphql"
  274. payload = json.dumps({
  275. "operationName": "visionProfilePhotoList",
  276. "variables": {
  277. "userId": out_uid,
  278. "pcursor": "",
  279. "page": "profile"
  280. },
  281. "query": "fragment photoContent on PhotoEntity {\n id\n duration\n caption\n originCaption\n likeCount\n viewCount\n commentCount\n realLikeCount\n coverUrl\n photoUrl\n photoH265Url\n manifest\n manifestH265\n videoResource\n coverUrls {\n url\n __typename\n }\n timestamp\n expTag\n animatedCoverUrl\n distance\n videoRatio\n liked\n stereoType\n profileUserTopPhoto\n musicBlocked\n __typename\n}\n\nfragment feedContent on Feed {\n type\n author {\n id\n name\n headerUrl\n following\n headerUrls {\n url\n __typename\n }\n __typename\n }\n photo {\n ...photoContent\n __typename\n }\n canAddComment\n llsid\n status\n currentPcursor\n tags {\n type\n name\n __typename\n }\n __typename\n}\n\nquery visionProfilePhotoList($pcursor: String, $userId: String, $page: String, $webPageArea: String) {\n visionProfilePhotoList(pcursor: $pcursor, userId: $userId, page: $page, webPageArea: $webPageArea) {\n result\n llsid\n webPageArea\n feeds {\n ...feedContent\n __typename\n }\n hostName\n pcursor\n __typename\n }\n}\n"
  282. })
  283. headers = {
  284. 'Accept': '*/*',
  285. 'Content-Type': 'application/json',
  286. 'Origin': 'https://www.kuaishou.com',
  287. 'Cookie': f'kpf=PC_WEB; clientid=3; did={cls.get_did(log_type, crawler)}; kpn=KUAISHOU_VISION',
  288. 'Content-Length': '1260',
  289. 'Accept-Language': 'zh-CN,zh-Hans;q=0.9',
  290. 'Host': 'www.kuaishou.com',
  291. 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15',
  292. 'Referer': 'https://www.kuaishou.com/profile/{}'.format(out_uid),
  293. 'Accept-Encoding': 'gzip, deflate, br',
  294. 'Connection': 'keep-alive'
  295. }
  296. response = requests.post(url=url, headers=headers, data=payload, proxies=Common.tunnel_proxies(),
  297. verify=False, timeout=10)
  298. try:
  299. feeds = response.json()['data']['visionProfilePhotoList']['feeds']
  300. except Exception as e:
  301. Common.logger(log_type, crawler).error(f"get_videoList:{e},response:{response.text}")
  302. return
  303. if not feeds:
  304. Common.logger(log_type, crawler).info("没有更多视频啦 ~\n")
  305. return
  306. pcursor = response.json()['data']['visionProfilePhotoList']['pcursor']
  307. # Common.logger(log_type, crawler).info(f"feeds0: {feeds}\n")
  308. for i in range(len(feeds)):
  309. try:
  310. # video_title
  311. if 'caption' not in feeds[i]['photo']:
  312. video_title = random_title(log_type, crawler, env, text='title')
  313. elif feeds[i]['photo']['caption'].strip() == "":
  314. video_title = random_title(log_type, crawler, env, text='title')
  315. else:
  316. video_title = cls.video_title(log_type, crawler, env, feeds[i]['photo']['caption'])
  317. if 'videoResource' not in feeds[i]['photo'] \
  318. and 'manifest' not in feeds[i]['photo'] \
  319. and 'manifestH265' not in feeds[i]['photo']:
  320. Common.logger(log_type, crawler).warning(f"get_videoList:{feeds[i]['photo']}\n")
  321. break
  322. videoResource = feeds[i]['photo']['videoResource']
  323. if 'h264' not in videoResource and 'hevc' not in videoResource:
  324. Common.logger(log_type, crawler).warning(f"get_videoList:{videoResource}\n")
  325. break
  326. # video_id
  327. if 'h264' in videoResource and 'videoId' in videoResource['h264']:
  328. video_id = videoResource['h264']['videoId']
  329. elif 'hevc' in videoResource and 'videoId' in videoResource['hevc']:
  330. video_id = videoResource['hevc']['videoId']
  331. else:
  332. video_id = ""
  333. # play_cnt
  334. if 'viewCount' not in feeds[i]['photo']:
  335. play_cnt = 0
  336. else:
  337. play_cnt = int(feeds[i]['photo']['viewCount'])
  338. # like_cnt
  339. if 'realLikeCount' not in feeds[i]['photo']:
  340. like_cnt = 0
  341. else:
  342. like_cnt = feeds[i]['photo']['realLikeCount']
  343. # publish_time
  344. if 'timestamp' not in feeds[i]['photo']:
  345. publish_time_stamp = 0
  346. publish_time_str = ''
  347. publish_time = 0
  348. else:
  349. publish_time_stamp = int(int(feeds[i]['photo']['timestamp']) / 1000)
  350. publish_time_str = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(publish_time_stamp))
  351. publish_time = int((int(time.time()) - publish_time_stamp) / (3600 * 24))
  352. # duration
  353. if 'duration' not in feeds[i]['photo']:
  354. duration = 0
  355. else:
  356. duration = int(int(feeds[i]['photo']['duration']) / 1000)
  357. # video_width / video_height / video_url
  358. mapping = {}
  359. for item in ['width', 'height']:
  360. try:
  361. val = str(videoResource['h264']['adaptationSet'][0]['representation'][0][item])
  362. except:
  363. val = str(videoResource['hevc']['adaptationSet'][0]['representation'][0][item])
  364. mapping[item] = val
  365. video_width = int(mapping['width']) if mapping['width'] else 0
  366. video_height = int(mapping['height']) if mapping['height'] else 0
  367. # cover_url
  368. if 'coverUrl' not in feeds[i]['photo']:
  369. cover_url = ""
  370. else:
  371. cover_url = feeds[i]['photo']['coverUrl']
  372. # user_name / avatar_url
  373. user_name = feeds[i]['author']['name']
  374. avatar_url = feeds[i]['author']['headerUrl']
  375. video_url = feeds[i]['photo']['photoUrl']
  376. video_dict = {'video_title': video_title,
  377. 'video_id': video_id,
  378. 'play_cnt': play_cnt,
  379. 'comment_cnt': 0,
  380. 'like_cnt': like_cnt,
  381. 'share_cnt': 0,
  382. 'video_width': video_width,
  383. 'video_height': video_height,
  384. 'duration': duration,
  385. 'publish_time': publish_time,
  386. 'publish_time_stamp': publish_time_stamp,
  387. 'publish_time_str': publish_time_str,
  388. 'user_name': user_name,
  389. 'user_id': out_uid,
  390. 'avatar_url': avatar_url,
  391. 'cover_url': cover_url,
  392. 'video_url': video_url,
  393. 'session': f"kuaishou{int(time.time())}"}
  394. rule_1 = cls.download_rule(video_dict, rule_dict_1)
  395. Common.logger(log_type, crawler).info(f"video_title:{video_title}")
  396. Common.logger(log_type, crawler).info(f"video_id:{video_id}\n")
  397. Common.logger(log_type, crawler).info(
  398. f"play_cnt:{video_dict['play_cnt']}{rule_dict_1['play_cnt']}, {eval(str(video_dict['play_cnt']) + str(rule_dict_1['play_cnt']))}")
  399. Common.logger(log_type, crawler).info(
  400. f"like_cnt:{video_dict['like_cnt']}{rule_dict_1['like_cnt']}, {eval(str(video_dict['like_cnt']) + str(rule_dict_1['like_cnt']))}")
  401. Common.logger(log_type, crawler).info(
  402. f"video_width:{video_dict['video_width']}{rule_dict_1['video_width']}, {eval(str(video_dict['video_width']) + str(rule_dict_1['video_width']))}")
  403. Common.logger(log_type, crawler).info(
  404. f"video_height:{video_dict['video_height']}{rule_dict_1['video_height']}, {eval(str(video_dict['video_height']) + str(rule_dict_1['video_height']))}")
  405. Common.logger(log_type, crawler).info(
  406. f"duration:{video_dict['duration']}{rule_dict_1['duration']}, {eval(str(video_dict['duration']) + str(rule_dict_1['duration']))}")
  407. Common.logger(log_type, crawler).info(
  408. f"publish_time:{video_dict['publish_time']}{rule_dict_1['publish_time']}, {eval(str(video_dict['publish_time']) + str(rule_dict_1['publish_time']))}")
  409. Common.logger(log_type, crawler).info(f"rule_1:{rule_1}\n")
  410. rule_2 = cls.download_rule(video_dict, rule_dict_2)
  411. Common.logger(log_type, crawler).info(
  412. f"play_cnt:{video_dict['play_cnt']}{rule_dict_2['play_cnt']}, {eval(str(video_dict['play_cnt']) + str(rule_dict_2['play_cnt']))}")
  413. Common.logger(log_type, crawler).info(
  414. f"like_cnt:{video_dict['like_cnt']}{rule_dict_2['like_cnt']}, {eval(str(video_dict['like_cnt']) + str(rule_dict_2['like_cnt']))}")
  415. Common.logger(log_type, crawler).info(
  416. f"video_width:{video_dict['video_width']}{rule_dict_2['video_width']}, {eval(str(video_dict['video_width']) + str(rule_dict_2['video_width']))}")
  417. Common.logger(log_type, crawler).info(
  418. f"video_height:{video_dict['video_height']}{rule_dict_2['video_height']}, {eval(str(video_dict['video_height']) + str(rule_dict_2['video_height']))}")
  419. Common.logger(log_type, crawler).info(
  420. f"duration:{video_dict['duration']}{rule_dict_2['duration']}, {eval(str(video_dict['duration']) + str(rule_dict_2['duration']))}")
  421. Common.logger(log_type, crawler).info(
  422. f"publish_time:{video_dict['publish_time']}{rule_dict_2['publish_time']}, {eval(str(video_dict['publish_time']) + str(rule_dict_2['publish_time']))}")
  423. Common.logger(log_type, crawler).info(f"rule_2:{rule_2}\n")
  424. if video_title == "" or video_url == "":
  425. Common.logger(log_type, crawler).info("无效视频\n")
  426. continue
  427. elif rule_1 is True:
  428. if download_cnt_1 < int(
  429. rule_dict_1['download_cnt'].replace("=", "")[-1].replace("<", "")[-1].replace(">",
  430. "")[
  431. -1]):
  432. cls.download_publish(log_type=log_type,
  433. crawler=crawler,
  434. strategy=strategy,
  435. video_dict=video_dict,
  436. rule_dict=rule_dict_1,
  437. our_uid=our_uid,
  438. oss_endpoint=oss_endpoint,
  439. env=env,
  440. machine=machine)
  441. # if download_finished is True:
  442. # download_cnt_1 += 1
  443. elif rule_2 is True:
  444. if download_cnt_2 < int(
  445. rule_dict_2['download_cnt'].replace("=", "")[-1].replace("<", "")[-1].replace(">",
  446. "")[
  447. -1]):
  448. cls.download_publish(log_type=log_type,
  449. crawler=crawler,
  450. strategy=strategy,
  451. video_dict=video_dict,
  452. rule_dict=rule_dict_2,
  453. our_uid=our_uid,
  454. oss_endpoint=oss_endpoint,
  455. env=env,
  456. machine=machine)
  457. # if download_finished is True:
  458. # download_cnt_2 += 1
  459. else:
  460. Common.logger(log_type, crawler).info("不满足下载规则\n")
  461. # Common.logger(log_type, crawler).info(f"feeds: {feeds}\n")
  462. except Exception as e:
  463. Common.logger(log_type, crawler).warning(f"抓取单条视频异常:{e}\n")
  464. # if pcursor == "no_more":
  465. # Common.logger(log_type, crawler).info(f"作者,{out_uid},已经到底了,没有更多内容了\n")
  466. # return
  467. # cls.get_videoList(log_type, crawler, strategy, our_uid, out_uid, oss_endpoint, env, machine,
  468. # pcursor=pcursor)
  469. # time.sleep(random.randint(1, 3))
  470. @classmethod
  471. def repeat_video(cls, log_type, crawler, video_id, video_title, publish_time, env, machine):
  472. sql = f""" select * from crawler_video where platform="{cls.platform}" and out_video_id="{video_id}" or (platform="{cls.platform}" and video_title="{video_title}" and publish_time="{publish_time}") """
  473. repeat_video = MysqlHelper.get_values(log_type, crawler, sql, env, machine)
  474. return len(repeat_video)
  475. @classmethod
  476. def download_publish(cls, log_type, crawler, strategy, video_dict, rule_dict, our_uid, oss_endpoint, env, machine):
  477. filter_words = get_config_from_mysql(log_type, crawler, env, text='filter')
  478. for filter_word in filter_words:
  479. if filter_word in video_dict['video_title']:
  480. Common.logger(log_type, crawler).info('标题已中过滤词:{}\n', video_dict['video_title'])
  481. return
  482. download_finished = False
  483. if cls.repeat_video(log_type, crawler, video_dict['video_id'], video_dict['video_title'],
  484. video_dict['publish_time_str'], env, machine) != 0:
  485. Common.logger(log_type, crawler).info('视频已下载\n')
  486. else:
  487. # 下载视频
  488. Common.download_method(log_type=log_type, crawler=crawler, text='video',
  489. title=video_dict['video_title'], url=video_dict['video_url'])
  490. md_title = md5(video_dict['video_title'].encode('utf8')).hexdigest()
  491. try:
  492. if os.path.getsize(f"./{crawler}/videos/{md_title}/video.mp4") == 0:
  493. # 删除视频文件夹
  494. shutil.rmtree(f"./{crawler}/videos/{md_title}")
  495. Common.logger(log_type, crawler).info("视频size=0,删除成功\n")
  496. return
  497. except FileNotFoundError:
  498. # 删除视频文件夹
  499. shutil.rmtree(f"./{crawler}/videos/{md_title}")
  500. Common.logger(log_type, crawler).info("未发现视频文件,删除成功\n")
  501. return
  502. # 下载封面
  503. Common.download_method(log_type=log_type, crawler=crawler, text='cover',
  504. title=video_dict['video_title'], url=video_dict['cover_url'])
  505. # 保存视频信息至txt
  506. Common.save_video_info(log_type=log_type, crawler=crawler, video_dict=video_dict)
  507. # 上传视频
  508. Common.logger(log_type, crawler).info("开始上传视频...")
  509. our_video_id = Publish.upload_and_publish(log_type=log_type,
  510. crawler=crawler,
  511. strategy=strategy,
  512. our_uid=our_uid,
  513. env=env,
  514. oss_endpoint=oss_endpoint)
  515. if env == 'dev':
  516. our_video_link = f"https://testadmin.piaoquantv.com/cms/post-detail/{our_video_id}/info"
  517. else:
  518. our_video_link = f"https://admin.piaoquantv.com/cms/post-detail/{our_video_id}/info"
  519. Common.logger(log_type, crawler).info("视频上传完成")
  520. if our_video_id is None:
  521. try:
  522. Common.logger(log_type, crawler).warning(f"our_video_id:{our_video_id} 删除该视频文件夹")
  523. # 删除视频文件夹
  524. shutil.rmtree(f"./{crawler}/videos/{md_title}")
  525. return download_finished
  526. except FileNotFoundError:
  527. return download_finished
  528. # 视频信息保存数据库
  529. insert_sql = f""" insert into crawler_video(video_id,
  530. user_id,
  531. out_user_id,
  532. platform,
  533. strategy,
  534. out_video_id,
  535. video_title,
  536. cover_url,
  537. video_url,
  538. duration,
  539. publish_time,
  540. play_cnt,
  541. crawler_rule,
  542. width,
  543. height)
  544. values({our_video_id},
  545. {our_uid},
  546. "{video_dict['user_id']}",
  547. "{cls.platform}",
  548. "定向爬虫策略",
  549. "{video_dict['video_id']}",
  550. "{video_dict['video_title']}",
  551. "{video_dict['cover_url']}",
  552. "{video_dict['video_url']}",
  553. {int(video_dict['duration'])},
  554. "{video_dict['publish_time_str']}",
  555. {int(video_dict['play_cnt'])},
  556. '{json.dumps(rule_dict)}',
  557. {int(video_dict['video_width'])},
  558. {int(video_dict['video_height'])}) """
  559. Common.logger(log_type, crawler).info(f"insert_sql:{insert_sql}")
  560. MysqlHelper.update_values(log_type, crawler, insert_sql, env, machine)
  561. Common.logger(log_type, crawler).info('视频信息插入数据库成功!\n')
  562. # 视频写入飞书
  563. Feishu.insert_columns(log_type, 'kuaishou', "fYdA8F", "ROWS", 1, 2)
  564. upload_time = int(time.time())
  565. values = [[our_video_id,
  566. time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(upload_time)),
  567. "定向榜",
  568. str(video_dict['video_id']),
  569. video_dict['video_title'],
  570. our_video_link,
  571. video_dict['play_cnt'],
  572. video_dict['comment_cnt'],
  573. video_dict['like_cnt'],
  574. video_dict['share_cnt'],
  575. video_dict['duration'],
  576. f"{video_dict['video_width']}*{video_dict['video_height']}",
  577. video_dict['publish_time_str'],
  578. video_dict['user_name'],
  579. video_dict['user_id'],
  580. video_dict['avatar_url'],
  581. video_dict['cover_url'],
  582. video_dict['video_url']]]
  583. time.sleep(1)
  584. Feishu.update_values(log_type, 'kuaishou', "fYdA8F", "E2:Z2", values)
  585. Common.logger(log_type, crawler).info(f"视频已保存至云文档\n")
  586. download_finished = True
  587. return download_finished
  588. @classmethod
  589. def get_follow_videos(cls, log_type, crawler, strategy, oss_endpoint, env, machine):
  590. user_list = get_user_from_mysql(log_type, crawler, crawler, env)
  591. for user in user_list:
  592. try:
  593. spider_link = user["link"]
  594. out_uid = spider_link.split('/')[-1]
  595. user_name = user["nick_name"]
  596. our_uid = user["uid"]
  597. Common.logger(log_type, crawler).info(f"开始抓取 {user_name} 用户主页视频\n")
  598. cls.get_videoList(log_type=log_type,
  599. crawler=crawler,
  600. strategy=strategy,
  601. our_uid=our_uid,
  602. out_uid=out_uid,
  603. oss_endpoint=oss_endpoint,
  604. env=env,
  605. machine=machine)
  606. except Exception as e:
  607. Common.logger(log_type, crawler).warning(f"抓取用户{user}时异常:{e}\n")
  608. if __name__ == "__main__":
  609. print(KuaiShouFollow.get_did("follow", "kuaishou"))
  610. pass