[精品软件] 快手视频下载,指定作者批量下载无水印

  [复制链接]
查看2477 | 回复212 | 2022-7-28 21:55 | 显示全部楼层 |阅读模式
本帖最后由 65646766 于 2022-7-28 21:59 编辑

195938dhf4c05fc3l0mpoo.jpg
第二步,粘贴ID,到对话框,先获取,再下载
195940po3l9eq3etyq3eny.jpg
第三步,下载好的文件位于程序所在目录下,有个ksdownloads文件夹下,以作者名命名的文件夹内。

问题:
1 目前是单线程,较大文件下载会慢,等待时间略长并且程序无提示,期待改进
2 下载未提示下载的是第几个文件

源码如下

  1. # -*- coding:utf-8 -*-
  2. # @FileName  :mykuaishouUi.py
  3. # @AuTho[url=home.php?mod=space&uid=686208]r[/url]    :kololi@52pojie
  4. import os
  5. import re
  6. import subprocess
  7. import threading
  8. import time
  9. import tkinter as tk
  10. import tkinter.font as tkFont
  11. import warnings
  12. from datetime import datetime

  13. import requests

  14. LOG_LINE_NUM = 0


  15. class App:
  16.     def __init__(self, root):
  17.         self.initUi(root)
  18.         self.initData()

  19.     def initData(self):
  20.         self.urls = 'https://www.kuaishou.com/profile/3xqsuf66a4m3ujy'
  21.         self.pcursor = ''
  22.         self.nickname = ''
  23.         self.datas = []
  24.         self.status_download = True
  25.         self.tag = 'odd'
  26.         self.base_url = 'https://www.kuaishou.com/graphql'
  27.         self.session = requests.Session()
  28.         self.session.headers.update({
  29.                                         'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.60 Safari/537.36'})
  30.         self.starurl = 'https://www.kuaishou.com/brilliant'
  31.         self.postdata = {
  32.             "operationName span>": "visionProfilePhotoList",  # 对你没看错,就是有个”span>“
  33.             "variables": {"userId": "3xn6jcvx7j3g2d2", "pcursor": "", "page": "profile"},
  34.             "query": "fragment photoContent on PhotoEntity {\n  id\n  duration\n  caption\n  likeCount\n  viewCount\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  __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  __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"
  35.         }

  36.     def initUi(self, root):
  37.         # setting title
  38.         root.title("KSDownloader kololi@52pojie")
  39.         # setting window size
  40.         width = 897
  41.         height = 533
  42.         screenwidth = root.winfo_screenwidth()
  43.         screenheight = root.winfo_screenheight()
  44.         alignstr = '%dx%d %d %d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
  45.         root.geometry(alignstr)
  46.         root.resizable(width=False, height=False)

  47.         ft = tkFont.Font(family='宋体', size=10)
  48.         GLabel_515 = tk.Label(root)
  49.         GLabel_515["font"] = ft
  50.         GLabel_515["justify"] = "center"
  51.         GLabel_515["text"] = "作者ID"
  52.         GLabel_515.place(x=20, y=10, width=47, height=30)

  53.         self.GLineEdit_508 = tk.Entry(root)
  54.         self.GLineEdit_508["borderwidth"] = "1px"
  55.         self.GLineEdit_508["justify"] = "center"
  56.         self.GLineEdit_508["text"] = "path"
  57.         self.GLineEdit_508['state'] = 'readonly'
  58.         self.GLineEdit_508.place(x=90, y=50, width=610, height=30)

  59.         self.GLineEdit_332 = tk.Entry(root)
  60.         self.GLineEdit_332["borderwidth"] = "1px"
  61.         self.GLineEdit_332["justify"] = "center"
  62.         self.GLineEdit_332["text"] = "url"
  63.         self.GLineEdit_332.place(x=90, y=10, width=231, height=30)

  64.         self.GButton_333 = tk.Button(root)
  65.         self.GButton_333["justify"] = "center"
  66.         self.GButton_333["text"] = "开始下载"
  67.         self.GButton_333.place(x=710, y=50, width=90, height=30)
  68.         self.GButton_333['state'] = 'disable'
  69.         self.GButton_333["command"] = self.GButton_333_command

  70.         ft2 = tkFont.Font(family='宋体', size=12)
  71.         self.GLineEdit_428 = tk.Text(root)
  72.         self.GLineEdit_428["borderwidth"] = "1px"
  73.         self.GLineEdit_428["font"] = ft2
  74.         self.GLineEdit_428.place(x=10, y=90, width=881, height=427)

  75.         self.GButton_676 = tk.Button(root)
  76.         self.GButton_676["font"] = ft
  77.         self.GButton_676["justify"] = "center"
  78.         self.GButton_676["text"] = "停止下载"
  79.         self.GButton_676['state'] = 'disable'
  80.         self.GButton_676.place(x=810, y=50, width=74, height=30)
  81.         self.GButton_676["command"] = self.GButton_676_command

  82.         GButton_701 = tk.Button(root)
  83.         GButton_701["font"] = ft
  84.         GButton_701["justify"] = "center"
  85.         GButton_701["text"] = "获取信息"
  86.         GButton_701.place(x=330, y=10, width=70, height=30)
  87.         GButton_701["command"] = self.GButton_701_command

  88.         GLabel_100 = tk.Label(root)
  89.         GLabel_100["font"] = ft
  90.         GLabel_100["justify"] = "center"
  91.         GLabel_100["text"] = "昵称"
  92.         GLabel_100.place(x=410, y=10, width=43, height=30)

  93.         GLabel_1 = tk.Label(root)
  94.         GLabel_1["font"] = ft
  95.         GLabel_1["justify"] = "center"
  96.         GLabel_1["text"] = "条作品"
  97.         GLabel_1.place(x=790, y=10, width=76, height=30)

  98.         self.GLineEdit_690 = tk.Entry(root)
  99.         self.GLineEdit_690["borderwidth"] = "1px"
  100.         self.GLineEdit_690["font"] = ft
  101.         self.GLineEdit_690["justify"] = "center"
  102.         self.GLineEdit_690["text"] = "条作品"
  103.         self.GLineEdit_690['state'] = 'readonly'
  104.         self.GLineEdit_690.place(x=710, y=10, width=90, height=30)

  105.         self.GLineEdit_281 = tk.Entry(root)
  106.         self.GLineEdit_281["borderwidth"] = "1px"
  107.         self.GLineEdit_281["font"] = ft
  108.         self.GLineEdit_281["fg"] = "#333333"
  109.         self.GLineEdit_281["justify"] = "center"
  110.         self.GLineEdit_281["text"] = "昵称"
  111.         self.GLineEdit_281['state'] = 'readonly'
  112.         self.GLineEdit_281.place(x=460, y=10, width=240, height=31)

  113.         GButton_55 = tk.Button(root)
  114.         GButton_55["bg"] = "#efefef"
  115.         GButton_55["font"] = ft
  116.         GButton_55["fg"] = "#000000"
  117.         GButton_55["justify"] = "center"
  118.         GButton_55["text"] = "保存路径"
  119.         GButton_55["relief"] = "groove"
  120.         GButton_55.place(x=10, y=50, width=70, height=30)
  121.         GButton_55["command"] = self.GButton_55_command

  122.     def GButton_55_command(self):  # 打开文件夹
  123.         path = self.GLineEdit_508.get()
  124.         if path:
  125.             self.open_fp(path)

  126.     def GButton_701_command(self):  # 获取信息
  127.         authorId = self.GLineEdit_332.get()
  128.         self.status_download = True
  129.         # self._log(authorId)
  130.         if authorId:
  131.             self.pcursor = ''
  132.             self.postdata['variables']['userId'] = authorId
  133.             self._log(f'--------------开始查询,请稍等-----------')
  134.             obj1 = threading.Thread(target=self.analysis, args=({False}))
  135.             obj1.setDaemon(True)
  136.             obj1.start()
  137.         else:
  138.             self._log("请输入作者ID")

  139.     def GButton_676_command(self):  # 停止下载
  140.         self.status_download = False
  141.         self.GButton_676['state'] = 'disable'

  142.     def GButton_333_command(self):  # 开始下载
  143.         self.status_download = True
  144.         self.GButton_333['state'] = 'disable'
  145.         self.GButton_676['state'] = 'active'
  146.         self.pcursor = ''
  147.         obj1 = threading.Thread(target=self.analysis, args=({True}))
  148.         obj1.setDaemon(True)
  149.         obj1.start()

  150.     def analysis(self, flag):
  151.         print(flag)
  152.         page_num = 0
  153.         len_feeds = 0
  154.         nickname = '未找到该ID用户或者暂未发布作品'
  155.         self._requests('get', self.starurl, decode_level=3)
  156.         self.pcursor == ''
  157.         while self.status_download:
  158.             if self.pcursor == 'no_more':
  159.                 if flag:
  160.                     self._log(f'--------------已全部完成下载!-----------')
  161.                 else:
  162.                     self._log(f'--------------查询完成!-----------')
  163.                 break
  164.             elif not self.status_download:
  165.                 self._log(f'--------------已停止下载!-----while------')
  166.                 break
  167.             else:
  168.                 page_num = 1
  169.                 self.postdata['variables']['pcursor'] = self.pcursor
  170.                 json_data = self._requests('post', self.base_url, decode_level=2, json=self.postdata)
  171.                 if not json_data:
  172.                     self._log(f'获取视频列表失败')
  173.                     break
  174.                 feeds = json_data['data']['visionProfilePhotoList']['feeds']
  175.                 if feeds and len(feeds) > 0:
  176.                     # 下一页 链接pcursor导入data
  177.                     self.pcursor = json_data['data']['visionProfilePhotoList']['pcursor']
  178.                     if flag:
  179.                         self._log(f'……………………………………开始下载第{page_num}页数据……………………………………')
  180.                         for feed in feeds:
  181.                             if not self.status_download:
  182.                                 self._log(f'--------------已停止下载!--feed---------')
  183.                                 break
  184.                             self.download_photoUrl(feed)
  185.                     else:
  186.                         len_feeds = len(feeds)
  187.                         nickname = feeds[0]['author']['name'] if feeds[0]['author']['name'] else "未知"
  188.                         self.GButton_333['state'] = 'active'
  189.                 else:
  190.                     if page_num == 1:
  191.                         self._log("未找到该ID用户或该用户暂未发布作品")
  192.                     break
  193.         if not flag:
  194.             filepath = os.getcwd() '\\' 'ksdownloads' '\\' nickname
  195.             self.GLineEdit_690['state'] = 'normal'
  196.             self.GLineEdit_281['state'] = 'normal'
  197.             self.GLineEdit_508['state'] = 'normal'
  198.             self.GLineEdit_508.delete(0, 'end')
  199.             self.GLineEdit_281.delete(0, 'end')
  200.             self.GLineEdit_690.delete(0, 'end')
  201.             self.GLineEdit_690.insert(0, f'{len_feeds}')
  202.             self.GLineEdit_281.insert(0, f'{nickname}')
  203.             self.GLineEdit_508.insert(0, f'{filepath}')
  204.             self.GLineEdit_690['state'] = 'readonly'
  205.             self.GLineEdit_281['state'] = 'readonly'
  206.             self.GLineEdit_508['state'] = 'readonly'

  207.     def download_photoUrl(self, feed):
  208.         try:
  209.             filepath = os.getcwd() '/' 'ksdownloads' '/' feed['author']['name'] if feed['author'][
  210.                 'name'] else os.getcwd() '/' 'ksdownloads' '/' '未知用户'
  211.             self.nickname = feed['author']['name'] if feed['author']['name'] else "未知"
  212.             caption = feed['photo']['caption']  # title
  213.             photoUrl = feed['photo']['photoUrl']  # video link
  214.             caption = re.sub('[ \\/:*?"<>|\n\t]', '', caption)
  215.             likeCount = feed['photo']['likeCount']
  216.             viewCount = feed['photo']['viewCount']
  217.             self._log(f'{caption} {viewCount}次观看 {likeCount}人喜欢')
  218.             caption = caption[:28] if len(caption) > 28 else caption
  219.             if caption:
  220.                 video_data = self._requests('get', photoUrl, decode_level=3).content
  221.                 time_ns = time.time()
  222.                 self.save_video(os.path.normpath(filepath), caption '_' str(time_ns) '.mp4', video_data, photoUrl)
  223.         except Exception as e:
  224.             self._log(f'错误:{e},获取数据失败,请检查主播ID是否正确,也可能cookies已过期!')

  225.     def save_video(self, path, filename, video_data, url):
  226.         if not os.path.exists(path):
  227.             os.makedirs(path)
  228.         with open(os.path.normpath(os.path.join(path, filename)), 'wb') as f:
  229.             f.write(video_data)
  230.             now_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
  231.             self._log(f'  状态:[下载完成]')
  232.             self.datas.append([now_time, filename, path, url])

  233.     def open_fp(self, fp):
  234.         """
  235.         打开文件或文件夹
  236.         :param fp: 需要打开的文件或文件夹路径
  237.         """
  238.         import platform
  239.         systemType: str = platform.platform()  # 获取系统类型
  240.         if 'mac' in systemType:  # 判断以下当前系统类型
  241.             fp: str = fp.replace("", "/")  # mac系统下,遇到`\\`让路径打不开,不清楚为什么哈,觉得没必要的话自己可以删掉啦,18行那条也是
  242.             subprocess.call(["open", fp])
  243.         else:
  244.             fp: str = fp.replace("/", "")  # win系统下,有时`/`让路径打不开
  245.             try:
  246.                 os.startfile(fp)
  247.             except:
  248.                 self._log("文件还未下载")

  249.     def _requests(self, method, url, decode_level=1, retry=0, timeout=15, **kwargs):
  250.         if method in ["get", "post"]:
  251.             for _ in range(retry 1):
  252.                 try:
  253.                     warnings.filterwarnings('ignore')
  254.                     response = getattr(self.session, method)(url, timeout=timeout, verify=False, **kwargs)
  255.                     return response.text if decode_level == 1 else response.json() if decode_level == 2 else response
  256.                 except Exception as e:
  257.                     self._log(e)

  258.         return None

  259.     def _log(self, logmsg):
  260.         global LOG_LINE_NUM
  261.         current_time = self.get_current_time()
  262.         logmsg_in = str(current_time) " " str(logmsg) "\n"  # 换行
  263.         self.GLineEdit_428.tag_config("even", background='#e0e0e0')
  264.         self.GLineEdit_428.tag_config("odd", background='#ffffff')
  265.         self.tag = 'odd' if self.tag == 'even' else 'even'
  266.         if LOG_LINE_NUM <= 20:

  267.             self.GLineEdit_428.insert('end', logmsg_in, self.tag)
  268.             LOG_LINE_NUM = LOG_LINE_NUM 1
  269.         else:
  270.             self.GLineEdit_428.delete(1.0, 2.0)
  271.             self.GLineEdit_428.insert('end', logmsg_in, self.tag)

  272.     def get_current_time(self):
  273.         current_time = time.strftime('%H:%M:%S', time.localtime(time.time()))
  274.         return current_time


  275. if __name__ == "__main__":
  276.     root = tk.Tk()
  277.     app = App(root)
  278.     root.mainloop()
复制代码
打包好的exe(win7x64 python3.8)下载
游客,如果您要查看本帖隐藏内容请回复



幻彤8872 | 2022-7-29 07:56 | 显示全部楼层
淡定,淡定,淡定……
回复

使用道具 举报

fnwafkoqlu | 2022-7-29 15:11 | 显示全部楼层
我只是路过打酱油的。
回复

使用道具 举报

2fcd05ih01 | 2022-7-29 17:25 | 显示全部楼层
强烈支持楼主ing……
回复

使用道具 举报

42b8on1791 | 2022-7-29 17:43 | 显示全部楼层
太生气了,无法HOLD啦 >_<......
回复

使用道具 举报

pajkpnstna | 2022-7-29 19:11 | 显示全部楼层
66666666666666
回复

使用道具 举报

d2hf6c4qkf | 2022-7-29 22:34 | 显示全部楼层
太牛逼了
回复

使用道具 举报

s3u2766n05 | 2022-7-29 23:53 | 显示全部楼层
太牛逼了
回复

使用道具 举报

egcnmftlpw | 2022-7-30 14:15 | 显示全部楼层
66666666666666
回复

使用道具 举报

洎便汎 | 2022-7-30 18:03 | 显示全部楼层
太牛逼了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则