经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C# » 查看文章
使用C#爬取快手作者主页,并下载视频/图集(附源码及软件下载链接)
来源:cnblogs  作者:猫叔Vincent  时间:2024/8/26 9:18:17  对本文有异议

最近发现一些快手的作者,作品还不错,出于学习研究的目的,决定看一下怎么爬取数据。现在网上有一些爬虫工具,不过大部分都失效了,或者不开源。于是自己就写了一个小工具。先看一下成果:
image
image
软件只需要填写作者uid以及网页版的请求Cookie,即可实现自动下载,下载目录在程序根目录下的Download文件夹。
由于快手的风控比较厉害,软件也做了应对措施。不过需要用户点击软件中的提示文字,复制粘贴到浏览器,把请求的json保存到本地文件。使用软件提供的解析本地json按钮解析下载即可。如果返回的json文件很短或者没有数据,需要在快手的任意一个页面刷新一下,也就是告诉快手风控,现在是正常浏览,没有机器人的行为。

下面说一下构建整个App的思路。

1. 快手网页端准备

  1. 打开https://live.kuaishou.com/ ,在顶部搜索你要爬取的作者昵称,进入作者主页。也可以从App端分享作者的主页链接,粘贴进来。作者主页加载完成后,地址栏的地址一定要是类似:https://live.kuaishou.com/profile/xxxxxx。 后面的xxxxxx就是作者的user id。这个记住,复制出来,后面会用到。

  2. 按F12打开浏览器的开发者工具(我之前就说过开发者工具是好东西,研究爬虫必备,一定要好好学习)。

  3. 选择开发者工具顶部的“网络”,“全部”,如图所示。在请求列表中找到user id,点击它,右面就会出来请求的标头。里面有个Cookie,需要记住,复制出来。如果没有的话,记得刷新页面。
    image

  4. 在列表里面可以看到很多请求,我们需要从中找到网页端展示作品列表的那条请求,即public开头的,或者直接在左上角搜索public,即可过滤绝大部分无关请求。这个请求的响应数据里面有作者作品的完整json响应。
    image

你可以右击它,在新标签页面打开,打开后地址栏会显示完成的浏览器请求地址。这个网址需要记住,后续会用到。那个count默认是12或者20,我们用到时候,直接拉满,9999即可。
image
image

2. Postman拦截请求,模拟请求,并生成C#请求代码

  1. 安装postman interceptor拦截器,安装地址https://chromewebstore.google.com/detail/postman-interceptor/aicmkgpgakddgnaphhhpliifpcfhicfo 不得不说,这又是一个神器,搭配开发者工具,理论上可以搞定几乎所有的爬虫需求了。

  2. 打开Postman,点击右下角的Start Proxy,
    image
    开启拦截后,重新回到网页版作者主页,刷新一下页面,等页面加载完成后,点击停止拦截。否则列表会一直增多,因为他会拦截电脑的所有网络请求。这时Postman拦截器就会拦截到一大堆请求,同理,找到public请求,或者在左上角输入public,即可过滤出来我们需要的。
    image
    点击这个请求链接
    image
    这是Postman会打开一个新的窗口,包含了请求这个链接的所有参数以及标头信息。
    image
    点击Postman最右面的代码工具即可生成我们需要的代码。你可以选择C#、python、js、curl等等。
    image

3. 使用WPF写界面以及下载逻辑

  1. 新建WPF工程,为了界面好看,这次我用了开源的WPF UI,之前用过HandyControl、MicaWPF,这些都是不错的UI控件库。
    下载使用了开源的Downloader,请求使用了RestSharp,解析Json使用NewtonsoftJson,另外推荐一个免费的图标库FlatIcon。
    界面如下:
点击查看代码
  1. <ui:FluentWindow
  2. x:Class="KuaishouDownloader.MainWindow"
  3. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  4. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  5. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  6. xmlns:local="clr-namespace:KuaishouDownloader"
  7. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  8. xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
  9. Title="MainWindow"
  10. Width="900"
  11. Height="760"
  12. ExtendsContentIntoTitleBar="True"
  13. WindowBackdropType="Mica"
  14. WindowCornerPreference="Default"
  15. WindowStartupLocation="CenterScreen"
  16. mc:Ignorable="d">
  17. <Grid>
  18. <Grid.RowDefinitions>
  19. <RowDefinition Height="Auto" />
  20. <RowDefinition Height="*" />
  21. </Grid.RowDefinitions>
  22. <ui:TitleBar Title="快手作者主页作品爬取" Height="32" />
  23. <ui:Button
  24. x:Name="themeButton"
  25. Grid.Row="1"
  26. Width="32"
  27. Height="32"
  28. Margin="0,0,8,0"
  29. Padding="0"
  30. HorizontalAlignment="Right"
  31. VerticalAlignment="Top"
  32. Click="Theme_Click"
  33. CornerRadius="16"
  34. FontSize="24"
  35. Icon="{ui:SymbolIcon WeatherMoon48}"
  36. ToolTip="切换主题" />
  37. <ui:SnackbarPresenter
  38. x:Name="snackbarPresenter"
  39. Grid.Row="1"
  40. VerticalAlignment="Bottom" />
  41. <StackPanel
  42. Grid.Row="1"
  43. HorizontalAlignment="Center"
  44. VerticalAlignment="Center">
  45. <Border
  46. Width="200"
  47. Height="200"
  48. HorizontalAlignment="Center"
  49. CornerRadius="100">
  50. <ui:Image
  51. x:Name="imgHeader"
  52. Width="200"
  53. Height="200"
  54. CornerRadius="100" />
  55. </Border>
  56. <ui:TextBlock
  57. x:Name="tbNickName"
  58. Margin="0,12,0,0"
  59. HorizontalAlignment="Center" />
  60. <StackPanel Margin="0,12,0,0" Orientation="Horizontal">
  61. <ui:TextBlock
  62. Width="60"
  63. Margin="0,12,0,0"
  64. VerticalAlignment="Center"
  65. Text="uid" />
  66. <ui:TextBox
  67. x:Name="tbUid"
  68. Width="660"
  69. Height="36"
  70. VerticalContentAlignment="Center"
  71. ToolTip="App进入作者主页,分享主页-复制链接,用浏览器打开链接,地址栏一般变为https://www.kuaishou.com/profile/xxxxxx/开头的,复制xxxxxx过来" />
  72. </StackPanel>
  73. <StackPanel Margin="0,12,0,0" Orientation="Horizontal">
  74. <ui:TextBlock
  75. Width="60"
  76. VerticalAlignment="Center"
  77. Text="cookie" />
  78. <ui:TextBox
  79. x:Name="tbCookie"
  80. Width="660"
  81. Height="36"
  82. VerticalContentAlignment="Center"
  83. ToolTip="利用浏览器开发者工具,从网络-请求标头中获取" />
  84. </StackPanel>
  85. <StackPanel
  86. Margin="0,12,0,0"
  87. HorizontalAlignment="Center"
  88. Orientation="Horizontal">
  89. <ui:Button
  90. x:Name="btnDownload"
  91. Height="32"
  92. Appearance="Primary"
  93. Click="Download_Click"
  94. Content="开始下载"
  95. CornerRadius="4 0 0 4"
  96. ToolTip="默认下载到程序根目录下,文件日期为作品发布日期" />
  97. <ui:Button
  98. x:Name="btnParseJson"
  99. Height="32"
  100. Appearance="Primary"
  101. Click="ParseJson_Click"
  102. Content="..."
  103. CornerRadius="0 4 4 0"
  104. ToolTip="解析从web或者postman保存的json数据" />
  105. </StackPanel>
  106. <TextBlock
  107. Width="700"
  108. Margin="0,12,0,0"
  109. Foreground="Gray"
  110. MouseDown="CopyUrl"
  111. Text="被快手风控不要慌,浏览器打开快手网页版,扫码登陆,点击我复制网址,粘贴到浏览器打开。打开后如果有很长很长的json数据返回,就对了。复制json保存到本地json文件,然后用第二个按钮解析json数据即可下载。"
  112. TextWrapping="Wrap" />
  113. <Expander Margin="0,12,0,0" Header="更多选项">
  114. <StackPanel Orientation="Horizontal">
  115. <CheckBox
  116. x:Name="cbAddDate"
  117. Margin="12,0,0,0"
  118. VerticalAlignment="Center"
  119. Content="文件名前加上日期"
  120. IsChecked="True"
  121. ToolTip="文件名前面加上类似2024-01-02 13-00-00的标识,方便排序" />
  122. <CheckBox
  123. x:Name="cbLongInterval"
  124. Margin="12,0,0,0"
  125. VerticalAlignment="Center"
  126. Content="增加作品下载延时"
  127. IsChecked="True"
  128. ToolTip="默认勾选,作品间下载延时5~10秒。取消勾选1~5秒随机,可能被风控" />
  129. </StackPanel>
  130. </Expander>
  131. </StackPanel>
  132. <StackPanel
  133. Grid.Row="1"
  134. Margin="0,0,0,-2"
  135. VerticalAlignment="Bottom">
  136. <TextBlock x:Name="tbProgress" HorizontalAlignment="Center" />
  137. <ProgressBar x:Name="progress" Height="8" />
  138. </StackPanel>
  139. <ui:Button
  140. x:Name="infoButton"
  141. Grid.Row="1"
  142. Width="32"
  143. Height="32"
  144. Margin="0,0,8,8"
  145. Padding="0"
  146. HorizontalAlignment="Right"
  147. VerticalAlignment="Bottom"
  148. Click="Info_Click"
  149. CornerRadius="16"
  150. FontSize="24"
  151. Icon="{ui:SymbolIcon Info28}"
  152. ToolTip="鸣谢" />
  153. <ui:Flyout
  154. x:Name="flyout"
  155. Grid.Row="1"
  156. HorizontalAlignment="Right">
  157. <ui:TextBlock Text="鸣谢: &#xA;1. Microsoft Presentation Foundation&#xA;2. WPF-UI&#xA;3. RestSharp&#xA;4. Newtonsoft.Json&#xA;5. Downloader&#xA;6. Icon from FlatIcon" />
  158. </ui:Flyout>
  159. </Grid>
  160. </ui:FluentWindow>
  1. 后台逻辑没有使用MVVM,就是图方便。
点击查看代码
  1. using KuaishouDownloader.Models;
  2. using Newtonsoft.Json;
  3. using RestSharp;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Text.RegularExpressions;
  7. using System.Windows;
  8. using Wpf.Ui;
  9. using Wpf.Ui.Controls;
  10. namespace KuaishouDownloader
  11. {
  12. /// <summary>
  13. /// Interaction logic for MainWindow.xaml
  14. /// </summary>
  15. public partial class MainWindow
  16. {
  17. string downloadFolder = AppContext.BaseDirectory;
  18. SnackbarService? snackbarService = null;
  19. public MainWindow()
  20. {
  21. InitializeComponent();
  22. this.Loaded += MainWindow_Loaded;
  23. }
  24. private void MainWindow_Loaded(object sender, RoutedEventArgs e)
  25. {
  26. snackbarService = new SnackbarService();
  27. snackbarService.SetSnackbarPresenter(snackbarPresenter);
  28. if (File.Exists("AppConfig.json"))
  29. {
  30. var model = JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText("AppConfig.json"));
  31. if (model != null)
  32. {
  33. tbUid.Text = model.Uid;
  34. tbCookie.Text = model.Cookie;
  35. }
  36. }
  37. }
  38. private void Theme_Click(object sender, RoutedEventArgs e)
  39. {
  40. if (Wpf.Ui.Appearance.ApplicationThemeManager.GetAppTheme() == Wpf.Ui.Appearance.ApplicationTheme.Light)
  41. {
  42. themeButton.Icon = new SymbolIcon(SymbolRegular.WeatherSunny48);
  43. Wpf.Ui.Appearance.ApplicationThemeManager.Apply(Wpf.Ui.Appearance.ApplicationTheme.Dark);
  44. }
  45. else
  46. {
  47. themeButton.Icon = new SymbolIcon(SymbolRegular.WeatherMoon48);
  48. Wpf.Ui.Appearance.ApplicationThemeManager.Apply(Wpf.Ui.Appearance.ApplicationTheme.Light);
  49. }
  50. }
  51. private async void Download_Click(object sender, RoutedEventArgs e)
  52. {
  53. try
  54. {
  55. btnDownload.IsEnabled = false;
  56. btnParseJson.IsEnabled = false;
  57. if (string.IsNullOrEmpty(tbUid.Text) || string.IsNullOrEmpty(tbCookie.Text))
  58. {
  59. snackbarService?.Show("提示", $"请输入uid以及cookie", ControlAppearance.Caution, null, TimeSpan.FromSeconds(3));
  60. return;
  61. }
  62. var json = JsonConvert.SerializeObject(new AppConfig() { Uid = tbUid.Text, Cookie = tbCookie.Text }, Formatting.Indented);
  63. File.WriteAllText("AppConfig.json", json);
  64. var options = new RestClientOptions("https://live.kuaishou.com")
  65. {
  66. Timeout = TimeSpan.FromSeconds(15),
  67. UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",
  68. };
  69. var client = new RestClient(options);
  70. var request = new RestRequest($"/live_api/profile/public?count=9999&pcursor=&principalId={tbUid.Text}&hasMore=true", Method.Get);
  71. request.AddHeader("host", "live.kuaishou.com");
  72. request.AddHeader("connection", "keep-alive");
  73. request.AddHeader("cache-control", "max-age=0");
  74. request.AddHeader("sec-ch-ua", "\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\"");
  75. request.AddHeader("sec-ch-ua-mobile", "?0");
  76. request.AddHeader("sec-ch-ua-platform", "\"Windows\"");
  77. request.AddHeader("upgrade-insecure-requests", "1");
  78. request.AddHeader("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
  79. request.AddHeader("sec-fetch-site", "none");
  80. request.AddHeader("sec-fetch-mode", "navigate");
  81. request.AddHeader("sec-fetch-user", "?1");
  82. request.AddHeader("sec-fetch-dest", "document");
  83. request.AddHeader("accept-encoding", "gzip, deflate, br, zstd");
  84. request.AddHeader("accept-language", "zh,en;q=0.9,zh-CN;q=0.8");
  85. request.AddHeader("cookie", tbCookie.Text);
  86. request.AddHeader("x-postman-captr", "9467712");
  87. RestResponse response = await client.ExecuteAsync(request);
  88. Debug.WriteLine(response.Content);
  89. var model = JsonConvert.DeserializeObject<KuaishouModel>(response.Content!);
  90. if (model == null || model?.Data?.List == null || model?.Data?.List?.Count == 0)
  91. {
  92. snackbarService?.Show("提示", $"获取失败,可能触发了快手的风控机制,请等一段时间再试。", ControlAppearance.Danger, null, TimeSpan.FromSeconds(3));
  93. return;
  94. }
  95. await Download(model!);
  96. }
  97. finally
  98. {
  99. btnDownload.IsEnabled = true;
  100. btnParseJson.IsEnabled = true;
  101. }
  102. }
  103. private async void ParseJson_Click(object sender, RoutedEventArgs e)
  104. {
  105. try
  106. {
  107. btnDownload.IsEnabled = false;
  108. btnParseJson.IsEnabled = false;
  109. var dialog = new Microsoft.Win32.OpenFileDialog();
  110. dialog.Filter = "Json文件(.Json)|*.json";
  111. bool? result = dialog.ShowDialog();
  112. if (result == false)
  113. {
  114. return;
  115. }
  116. var model = JsonConvert.DeserializeObject<KuaishouModel>(File.ReadAllText(dialog.FileName)!);
  117. if (model == null || model?.Data?.List == null || model?.Data?.List?.Count == 0)
  118. {
  119. snackbarService?.Show("提示", $"不是正确的json", ControlAppearance.Caution, null, TimeSpan.FromSeconds(3));
  120. return;
  121. }
  122. await Download(model!);
  123. }
  124. finally
  125. {
  126. btnDownload.IsEnabled = true;
  127. btnParseJson.IsEnabled = true;
  128. }
  129. }
  130. private async Task Download(KuaishouModel model)
  131. {
  132. progress.Value = 0;
  133. progress.Minimum = 0;
  134. progress.Maximum = (double)model?.Data?.List?.Count!;
  135. snackbarService?.Show("提示", $"解析到{model?.Data?.List?.Count!}个作品,开始下载", ControlAppearance.Success, null, TimeSpan.FromSeconds(5));
  136. imgHeader.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(model?.Data?.List?[0]?.Author?.Avatar!));
  137. tbNickName.Text = model?.Data?.List?[0]?.Author?.Name;
  138. string pattern = @"\d{4}/\d{2}/\d{2}/\d{2}";
  139. for (int i = 0; i < model?.Data?.List!.Count; i++)
  140. {
  141. DateTime dateTime = DateTime.Now;
  142. string fileNamePrefix = "";
  143. var item = model?.Data?.List[i]!;
  144. Match match = Regex.Match(item.Poster!, pattern);
  145. if (match.Success)
  146. {
  147. dateTime = new DateTime(int.Parse(match.Value.Split("/")[0]), int.Parse(match.Value.Split("/")[1]),
  148. int.Parse(match.Value.Split("/")[2]), int.Parse(match.Value.Split("/")[3]), 0, 0);
  149. if (cbAddDate.IsChecked == true)
  150. fileNamePrefix = match.Value.Split("/")[0] + "-" + match.Value.Split("/")[1] + "-" + match.Value.Split("/")[2]
  151. + " " + match.Value.Split("/")[3] + "-00-00 ";
  152. }
  153. downloadFolder = Path.Combine(AppContext.BaseDirectory, "Download", item?.Author?.Name! + "(" + item?.Author?.Id! + ")");
  154. Directory.CreateDirectory(downloadFolder);
  155. switch (item?.WorkType)
  156. {
  157. case "single":
  158. case "vertical":
  159. case "multiple":
  160. {
  161. await DownLoadHelper.Download(item?.ImgUrls!, dateTime, downloadFolder, fileNamePrefix);
  162. }
  163. break;
  164. case "video":
  165. {
  166. await DownLoadHelper.Download(new List<string>() { item?.PlayUrl! }, dateTime, downloadFolder, fileNamePrefix);
  167. }
  168. break;
  169. }
  170. progress.Value = i + 1;
  171. tbProgress.Text = $"{i + 1} / {model?.Data?.List!.Count}";
  172. Random random = new Random();
  173. if (cbLongInterval.IsChecked == true)
  174. await Task.Delay(random.Next(5000, 10000));
  175. else
  176. await Task.Delay(random.Next(1000, 5000));
  177. }
  178. snackbarService?.Show("提示", $"下载完成,共下载{model?.Data?.List!.Count}个作品", ControlAppearance.Success, null, TimeSpan.FromDays(1));
  179. }
  180. private void CopyUrl(object sender, System.Windows.Input.MouseButtonEventArgs e)
  181. {
  182. if (string.IsNullOrEmpty(tbUid.Text))
  183. {
  184. snackbarService?.Show("提示", "请输入uid以及cookie", ControlAppearance.Caution, null, TimeSpan.FromSeconds(3));
  185. return;
  186. }
  187. Clipboard.SetText($"https://live.kuaishou.com/live_api/profile/public?count=9999&pcursor=&principalId={tbUid.Text}&hasMore=true");
  188. snackbarService?.Show("提示", "复制完成,请粘贴到浏览器打开", ControlAppearance.Success, null, TimeSpan.FromSeconds(3));
  189. }
  190. private void Info_Click(object sender, RoutedEventArgs e)
  191. {
  192. flyout.IsOpen = true;
  193. }
  194. }
  195. }
  1. 下载类,下载完文件后,将文件的日志修改为发表日志,方便排序以及数据分析。
点击查看代码
  1. public static async Task Download(List<string> urls, DateTime dateTime, string downloadFolder, string fileNamePrefix)
  2. {
  3. string file = string.Empty;
  4. try
  5. {
  6. var downloader = new DownloadService();
  7. foreach (var url in urls)
  8. {
  9. Uri uri = new Uri(url);
  10. file = downloadFolder + "\\" + fileNamePrefix + Path.GetFileName(uri.LocalPath);
  11. if (!File.Exists(file))
  12. await downloader.DownloadFileTaskAsync(url, file);
  13. //修改文件日期时间为发博的时间
  14. File.SetCreationTime(file, dateTime);
  15. File.SetLastWriteTime(file, dateTime);
  16. File.SetLastAccessTime(file, dateTime);
  17. }
  18. }
  19. catch
  20. {
  21. Debug.WriteLine(file);
  22. Trace.Listeners.Add(new TextWriterTraceListener(downloadFolder + "\\_FailedFiles.txt", "myListener"));
  23. Trace.TraceInformation(file);
  24. Trace.Flush();
  25. }
  26. }
  1. 源码分享
    完整版代码已上传到Github https://github.com/hupo376787/KuaishouDownloader ,喜欢的点一下Star谢谢。

4. 下载使用

打开https://github.com/hupo376787/KuaishouDownloader/releases/tag/1.0,点击下载zip文件,解压缩后,就可以像开头那样使用了。
image
image

原文链接:https://www.cnblogs.com/hupo376787/p/18378511

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号