使用C#爬取快手作者主页,并下载视频/图集

news/2024/11/15 17:29:19/文章来源:https://www.cnblogs.com/hupo376787/p/18378511

最近发现一些快手的作者,作品还不错,出于学习研究的目的,决定看一下怎么爬取数据。现在网上有一些爬虫工具,不过大部分都失效了,或者不开源。于是自己就写了一个小工具。先看一下成果:
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。
    界面如下:
点击查看代码
<ui:FluentWindowx:Class="KuaishouDownloader.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:KuaishouDownloader"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"Title="MainWindow"Width="900"Height="760"ExtendsContentIntoTitleBar="True"WindowBackdropType="Mica"WindowCornerPreference="Default"WindowStartupLocation="CenterScreen"mc:Ignorable="d"><Grid><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="*" /></Grid.RowDefinitions><ui:TitleBar Title="快手作者主页作品爬取" Height="32" /><ui:Buttonx:Name="themeButton"Grid.Row="1"Width="32"Height="32"Margin="0,0,8,0"Padding="0"HorizontalAlignment="Right"VerticalAlignment="Top"Click="Theme_Click"CornerRadius="16"FontSize="24"Icon="{ui:SymbolIcon WeatherMoon48}"ToolTip="切换主题" /><ui:SnackbarPresenterx:Name="snackbarPresenter"Grid.Row="1"VerticalAlignment="Bottom" /><StackPanelGrid.Row="1"HorizontalAlignment="Center"VerticalAlignment="Center"><BorderWidth="200"Height="200"HorizontalAlignment="Center"CornerRadius="100"><ui:Imagex:Name="imgHeader"Width="200"Height="200"CornerRadius="100" /></Border><ui:TextBlockx:Name="tbNickName"Margin="0,12,0,0"HorizontalAlignment="Center" /><StackPanel Margin="0,12,0,0" Orientation="Horizontal"><ui:TextBlockWidth="60"Margin="0,12,0,0"VerticalAlignment="Center"Text="uid" /><ui:TextBoxx:Name="tbUid"Width="660"Height="36"VerticalContentAlignment="Center"ToolTip="App进入作者主页,分享主页-复制链接,用浏览器打开链接,地址栏一般变为https://www.kuaishou.com/profile/xxxxxx/开头的,复制xxxxxx过来" /></StackPanel><StackPanel Margin="0,12,0,0" Orientation="Horizontal"><ui:TextBlockWidth="60"VerticalAlignment="Center"Text="cookie" /><ui:TextBoxx:Name="tbCookie"Width="660"Height="36"VerticalContentAlignment="Center"ToolTip="利用浏览器开发者工具,从网络-请求标头中获取" /></StackPanel><StackPanelMargin="0,12,0,0"HorizontalAlignment="Center"Orientation="Horizontal"><ui:Buttonx:Name="btnDownload"Height="32"Appearance="Primary"Click="Download_Click"Content="开始下载"CornerRadius="4 0 0 4"ToolTip="默认下载到程序根目录下,文件日期为作品发布日期" /><ui:Buttonx:Name="btnParseJson"Height="32"Appearance="Primary"Click="ParseJson_Click"Content="..."CornerRadius="0 4 4 0"ToolTip="解析从web或者postman保存的json数据" /></StackPanel><TextBlockWidth="700"Margin="0,12,0,0"Foreground="Gray"MouseDown="CopyUrl"Text="被快手风控不要慌,浏览器打开快手网页版,扫码登陆,点击我复制网址,粘贴到浏览器打开。打开后如果有很长很长的json数据返回,就对了。复制json保存到本地json文件,然后用第二个按钮解析json数据即可下载。"TextWrapping="Wrap" /><Expander Margin="0,12,0,0" Header="更多选项"><StackPanel Orientation="Horizontal"><CheckBoxx:Name="cbAddDate"Margin="12,0,0,0"VerticalAlignment="Center"Content="文件名前加上日期"IsChecked="True"ToolTip="文件名前面加上类似2024-01-02 13-00-00的标识,方便排序" /><CheckBoxx:Name="cbLongInterval"Margin="12,0,0,0"VerticalAlignment="Center"Content="增加作品下载延时"IsChecked="True"ToolTip="默认勾选,作品间下载延时5~10秒。取消勾选1~5秒随机,可能被风控" /></StackPanel></Expander></StackPanel><StackPanelGrid.Row="1"Margin="0,0,0,-2"VerticalAlignment="Bottom"><TextBlock x:Name="tbProgress" HorizontalAlignment="Center" /><ProgressBar x:Name="progress" Height="8" /></StackPanel><ui:Buttonx:Name="infoButton"Grid.Row="1"Width="32"Height="32"Margin="0,0,8,8"Padding="0"HorizontalAlignment="Right"VerticalAlignment="Bottom"Click="Info_Click"CornerRadius="16"FontSize="24"Icon="{ui:SymbolIcon Info28}"ToolTip="鸣谢" /><ui:Flyoutx:Name="flyout"Grid.Row="1"HorizontalAlignment="Right"><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" /></ui:Flyout></Grid>
</ui:FluentWindow>
  1. 后台逻辑没有使用MVVM,就是图方便。
点击查看代码
using KuaishouDownloader.Models;
using Newtonsoft.Json;
using RestSharp;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
using System.Windows;
using Wpf.Ui;
using Wpf.Ui.Controls;namespace KuaishouDownloader
{/// <summary>/// Interaction logic for MainWindow.xaml/// </summary>public partial class MainWindow{string downloadFolder = AppContext.BaseDirectory;SnackbarService? snackbarService = null;public MainWindow(){InitializeComponent();this.Loaded += MainWindow_Loaded;}private void MainWindow_Loaded(object sender, RoutedEventArgs e){snackbarService = new SnackbarService();snackbarService.SetSnackbarPresenter(snackbarPresenter);if (File.Exists("AppConfig.json")){var model = JsonConvert.DeserializeObject<AppConfig>(File.ReadAllText("AppConfig.json"));if (model != null){tbUid.Text = model.Uid;tbCookie.Text = model.Cookie;}}}private void Theme_Click(object sender, RoutedEventArgs e){if (Wpf.Ui.Appearance.ApplicationThemeManager.GetAppTheme() == Wpf.Ui.Appearance.ApplicationTheme.Light){themeButton.Icon = new SymbolIcon(SymbolRegular.WeatherSunny48);Wpf.Ui.Appearance.ApplicationThemeManager.Apply(Wpf.Ui.Appearance.ApplicationTheme.Dark);}else{themeButton.Icon = new SymbolIcon(SymbolRegular.WeatherMoon48);Wpf.Ui.Appearance.ApplicationThemeManager.Apply(Wpf.Ui.Appearance.ApplicationTheme.Light);}}private async void Download_Click(object sender, RoutedEventArgs e){try{btnDownload.IsEnabled = false;btnParseJson.IsEnabled = false;if (string.IsNullOrEmpty(tbUid.Text) || string.IsNullOrEmpty(tbCookie.Text)){snackbarService?.Show("提示", $"请输入uid以及cookie", ControlAppearance.Caution, null, TimeSpan.FromSeconds(3));return;}var json = JsonConvert.SerializeObject(new AppConfig() { Uid = tbUid.Text, Cookie = tbCookie.Text }, Formatting.Indented);File.WriteAllText("AppConfig.json", json);var options = new RestClientOptions("https://live.kuaishou.com"){Timeout = TimeSpan.FromSeconds(15),UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36",};var client = new RestClient(options);var request = new RestRequest($"/live_api/profile/public?count=9999&pcursor=&principalId={tbUid.Text}&hasMore=true", Method.Get);request.AddHeader("host", "live.kuaishou.com");request.AddHeader("connection", "keep-alive");request.AddHeader("cache-control", "max-age=0");request.AddHeader("sec-ch-ua", "\"Not)A;Brand\";v=\"99\", \"Google Chrome\";v=\"127\", \"Chromium\";v=\"127\"");request.AddHeader("sec-ch-ua-mobile", "?0");request.AddHeader("sec-ch-ua-platform", "\"Windows\"");request.AddHeader("upgrade-insecure-requests", "1");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");request.AddHeader("sec-fetch-site", "none");request.AddHeader("sec-fetch-mode", "navigate");request.AddHeader("sec-fetch-user", "?1");request.AddHeader("sec-fetch-dest", "document");request.AddHeader("accept-encoding", "gzip, deflate, br, zstd");request.AddHeader("accept-language", "zh,en;q=0.9,zh-CN;q=0.8");request.AddHeader("cookie", tbCookie.Text);request.AddHeader("x-postman-captr", "9467712");RestResponse response = await client.ExecuteAsync(request);Debug.WriteLine(response.Content);var model = JsonConvert.DeserializeObject<KuaishouModel>(response.Content!);if (model == null || model?.Data?.List == null || model?.Data?.List?.Count == 0){snackbarService?.Show("提示", $"获取失败,可能触发了快手的风控机制,请等一段时间再试。", ControlAppearance.Danger, null, TimeSpan.FromSeconds(3));return;}await Download(model!);}finally{btnDownload.IsEnabled = true;btnParseJson.IsEnabled = true;}}private async void ParseJson_Click(object sender, RoutedEventArgs e){try{btnDownload.IsEnabled = false;btnParseJson.IsEnabled = false;var dialog = new Microsoft.Win32.OpenFileDialog();dialog.Filter = "Json文件(.Json)|*.json";bool? result = dialog.ShowDialog();if (result == false){return;}var model = JsonConvert.DeserializeObject<KuaishouModel>(File.ReadAllText(dialog.FileName)!);if (model == null || model?.Data?.List == null || model?.Data?.List?.Count == 0){snackbarService?.Show("提示", $"不是正确的json", ControlAppearance.Caution, null, TimeSpan.FromSeconds(3));return;}await Download(model!);}finally{btnDownload.IsEnabled = true;btnParseJson.IsEnabled = true;}}private async Task Download(KuaishouModel model){progress.Value = 0;progress.Minimum = 0;progress.Maximum = (double)model?.Data?.List?.Count!;snackbarService?.Show("提示", $"解析到{model?.Data?.List?.Count!}个作品,开始下载", ControlAppearance.Success, null, TimeSpan.FromSeconds(5));imgHeader.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri(model?.Data?.List?[0]?.Author?.Avatar!));tbNickName.Text = model?.Data?.List?[0]?.Author?.Name;string pattern = @"\d{4}/\d{2}/\d{2}/\d{2}";for (int i = 0; i < model?.Data?.List!.Count; i++){DateTime dateTime = DateTime.Now;string fileNamePrefix = "";var item = model?.Data?.List[i]!;Match match = Regex.Match(item.Poster!, pattern);if (match.Success){dateTime = new DateTime(int.Parse(match.Value.Split("/")[0]), int.Parse(match.Value.Split("/")[1]),int.Parse(match.Value.Split("/")[2]), int.Parse(match.Value.Split("/")[3]), 0, 0);if (cbAddDate.IsChecked == true)fileNamePrefix = match.Value.Split("/")[0] + "-" + match.Value.Split("/")[1] + "-" + match.Value.Split("/")[2]+ " " + match.Value.Split("/")[3] + "-00-00 ";}downloadFolder = Path.Combine(AppContext.BaseDirectory, "Download", item?.Author?.Name! + "(" + item?.Author?.Id! + ")");Directory.CreateDirectory(downloadFolder);switch (item?.WorkType){case "single":case "vertical":case "multiple":{await DownLoadHelper.Download(item?.ImgUrls!, dateTime, downloadFolder, fileNamePrefix);}break;case "video":{await DownLoadHelper.Download(new List<string>() { item?.PlayUrl! }, dateTime, downloadFolder, fileNamePrefix);}break;}progress.Value = i + 1;tbProgress.Text = $"{i + 1} / {model?.Data?.List!.Count}";Random random = new Random();if (cbLongInterval.IsChecked == true)await Task.Delay(random.Next(5000, 10000));elseawait Task.Delay(random.Next(1000, 5000));}snackbarService?.Show("提示", $"下载完成,共下载{model?.Data?.List!.Count}个作品", ControlAppearance.Success, null, TimeSpan.FromDays(1));}private void CopyUrl(object sender, System.Windows.Input.MouseButtonEventArgs e){if (string.IsNullOrEmpty(tbUid.Text)){snackbarService?.Show("提示", "请输入uid以及cookie", ControlAppearance.Caution, null, TimeSpan.FromSeconds(3));return;}Clipboard.SetText($"https://live.kuaishou.com/live_api/profile/public?count=9999&pcursor=&principalId={tbUid.Text}&hasMore=true");snackbarService?.Show("提示", "复制完成,请粘贴到浏览器打开", ControlAppearance.Success, null, TimeSpan.FromSeconds(3));}private void Info_Click(object sender, RoutedEventArgs e){flyout.IsOpen = true;}}
}
  1. 下载类,下载完文件后,将文件的日志修改为发表日志,方便排序以及数据分析。
点击查看代码
public static async Task Download(List<string> urls, DateTime dateTime, string downloadFolder, string fileNamePrefix)
{string file = string.Empty;try{var downloader = new DownloadService();foreach (var url in urls){Uri uri = new Uri(url);file = downloadFolder + "\\" + fileNamePrefix + Path.GetFileName(uri.LocalPath);if (!File.Exists(file))await downloader.DownloadFileTaskAsync(url, file);//修改文件日期时间为发博的时间File.SetCreationTime(file, dateTime);File.SetLastWriteTime(file, dateTime);File.SetLastAccessTime(file, dateTime);}}catch{Debug.WriteLine(file);Trace.Listeners.Add(new TextWriterTraceListener(downloadFolder + "\\_FailedFiles.txt", "myListener"));Trace.TraceInformation(file);Trace.Flush();}
}
  1. 源码分享
    完整版代码已上传到Github https://github.com/hupo376787/KuaishouDownloader ,喜欢的点一下Star谢谢。

4. 下载使用

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/786956.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python安装教程

第一步:先去官网上下载python安装包 系统64位下载地址:系统32位下载地址:第二步:点击安装包 要点击Add python.exe to PATH 这个是配置系统变量的然后点install Now就开始跑安装进度了打开终端输入代码 python 再输入print(123),如果输入123那么安装完成,可以整活了

用空间清理调理风水4什么是杂乱5杂物如何影响你6为什么保留杂物

4 什么是杂乱? 《牛津英语词典》将杂乱定义为 “乱七八糟的东西的集合”。是的,这是其中的一部分,但它只是从纯粹的物理层面来描述杂乱。 在我的定义中,凌乱分为四类:你不使用或不喜欢的东西杂乱无章的东西太多东西放在太小的空间里任何未完成的东西让我们逐一来看看这些东…

go免杀学习记录

本文主要介绍了go语言的加载器基本框架与绕过360与火绒的通用方法。题记最近剑来动漫上线,虽然观感不如我的预期,感觉节奏过快。但是也是一种进步了,愿各位道友都能找到自己的宁姚。"我喜欢的姑娘啊,她眉如远山,浩然天下所有好看的山,好看的水,加起来都不如她。她睫…

看图学 - Swift actor

本文首发于 Ficow Shens Blog,原文地址: 看图学 - Swift actor。想第一时间获取对于自己有帮助的新内容? 欢迎关注 Ficow 的公众号: 看图学 Swift actor如需获取PDF版本思维导图、示例代码,请查阅公众号内容: 《看图学 - Swift actor》Stay hungry,stay foolish.

VulNyx - Ceres 靶机

有80端口访问看看他这个挺奇葩的看了wp才知道 file.php的参数是file 他会自动给你加上php 也就是说file=secret.php读不到数据要file=secret才能读到数据伪协议读取文件<?php include($_GET["file"].".php"); ?><?php system("id…

读软件开发安全之道:概念、设计与实施08密码学(下)

密码学1. 对称加密 1.1. symmetric encryption 1.2. 使用各方共享的密钥来隐藏数据1.2.1. 对称加密在本质上依赖共享密钥1.3. 所有加密都是通过对明文进行转换,把明文消息(或者原始消息)变成无法识别的形式(也称为密文)​,从而隐藏原始消息内容的 1.4. 可逆的转换称为对称…

Citrix ADC Release 14.1 Build 29.63 (nCore, VPX, SDX, CPX, BLX) - 混合多云应用交付控制器

Citrix ADC Release 14.1 Build 29.63 (nCore, VPX, SDX, CPX, BLX) - 混合多云应用交付控制器Citrix ADC Release 14.1 Build 29.63 (nCore, VPX, SDX, CPX, BLX) - 混合多云应用交付控制器 Citrix ADC - 混合多云应用交付控制器 请访问原文链接:https://sysin.org/blog/citr…

程序员:全栈的痛你不知道

我这里说的全栈,不只是IT技术栈,还有更多的是产品运营思维。任何时候全栈人都应该用解决问题、推动事情往前发展的思维去做事。上周一个同事直接对我开喷,骂我无能,说:“你怎么一个人就搞不定所有系统呢?”,我半支烟纵横IT江湖14余年,还是第一次被人这么嫌弃。 事情缘由…

dotnet C# 从控制台开始 关联 Win2D 和 WinUI 3 应用

本文将告诉大家如何从最简单的控制台开始搭建,让 Win2D 和 WinUI 3 关联起来,让 Win2D 可以将内容渲染到 WinUI 3 应用上本文适合想了解 WinUI 3 基础机制以及 Win2D 与 WinUI 3 协同的方式的伙伴。阅读本文将可以了解到一个简单的方式,简单到使用控制台项目即可进行搭建整个…

Zustand:状态持久化在项目中的应用

Zustand的持久化中间件允许你将状态存储在各种存储中,例如localStorage、AsyncSZustand的持久化中间件允许你将状态存储在各种存储中,例如localStorage、AsyncStorage或IndexedDB等。这使得应用的状态可以跨页面持久化。也就是说用户刷新页面或者关闭浏览器后重新打开,应用的…

060、Vue3+TypeScript基础,插槽的基础用法

01、main.js代码如下:// 引入createApp用于创建Vue实例 import {createApp} from vue // 引入App.vue根组件 import App from ./App.vue// 引入emitter用于全局事件总线 // import emitter from @/utils/emitterconst app = createApp(App);// App.vue的根元素id为app app.mou…

059、Vue3+TypeScript基础,页面通讯之父组件provide数据,子孙组件用inject直接使用

01、main.js代码如下:// 引入createApp用于创建Vue实例 import {createApp} from vue // 引入App.vue根组件 import App from ./App.vue// 引入emitter用于全局事件总线 // import emitter from @/utils/emitterconst app = createApp(App);// App.vue的根元素id为app app.mou…