首页 > 编程语言 >使用C#爬取快手作者主页,并下载视频/图集

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

时间:2024-08-25 09:48:42浏览次数:6  
标签:快手 List request AddHeader 爬取 C# Text new model

最近发现一些快手的作者,作品还不错,出于学习研究的目的,决定看一下怎么爬取数据。现在网上有一些爬虫工具,不过大部分都失效了,或者不开源。于是自己就写了一个小工具。先看一下成果:
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:FluentWindow
  x: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:Button
      x: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:SnackbarPresenter
      x:Name="snackbarPresenter"
      Grid.Row="1"
      VerticalAlignment="Bottom" />
    <StackPanel
      Grid.Row="1"
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
      <Border
        Width="200"
        Height="200"
        HorizontalAlignment="Center"
        CornerRadius="100">
        <ui:Image
          x:Name="imgHeader"
          Width="200"
          Height="200"
          CornerRadius="100" />
      </Border>
      <ui:TextBlock
        x:Name="tbNickName"
        Margin="0,12,0,0"
        HorizontalAlignment="Center" />
      <StackPanel Margin="0,12,0,0" Orientation="Horizontal">
        <ui:TextBlock
          Width="60"
          Margin="0,12,0,0"
          VerticalAlignment="Center"
          Text="uid" />
        <ui:TextBox
          x: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:TextBlock
          Width="60"
          VerticalAlignment="Center"
          Text="cookie" />
        <ui:TextBox
          x:Name="tbCookie"
          Width="660"
          Height="36"
          VerticalContentAlignment="Center"
          ToolTip="利用浏览器开发者工具,从网络-请求标头中获取" />
      </StackPanel>
      <StackPanel
        Margin="0,12,0,0"
        HorizontalAlignment="Center"
        Orientation="Horizontal">
        <ui:Button
          x:Name="btnDownload"
          Height="32"
          Appearance="Primary"
          Click="Download_Click"
          Content="开始下载"
          CornerRadius="4 0 0 4"
          ToolTip="默认下载到程序根目录下,文件日期为作品发布日期" />
        <ui:Button
          x:Name="btnParseJson"
          Height="32"
          Appearance="Primary"
          Click="ParseJson_Click"
          Content="..."
          CornerRadius="0 4 4 0"
          ToolTip="解析从web或者postman保存的json数据" />
      </StackPanel>
      <TextBlock
        Width="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">
          <CheckBox
            x:Name="cbAddDate"
            Margin="12,0,0,0"
            VerticalAlignment="Center"
            Content="文件名前加上日期"
            IsChecked="True"
            ToolTip="文件名前面加上类似2024-01-02 13-00-00的标识,方便排序" />
          <CheckBox
            x:Name="cbLongInterval"
            Margin="12,0,0,0"
            VerticalAlignment="Center"
            Content="增加作品下载延时"
            IsChecked="True"
            ToolTip="默认勾选,作品间下载延时5~10秒。取消勾选1~5秒随机,可能被风控" />
        </StackPanel>
      </Expander>
    </StackPanel>
    <StackPanel
      Grid.Row="1"
      Margin="0,0,0,-2"
      VerticalAlignment="Bottom">
      <TextBlock x:Name="tbProgress" HorizontalAlignment="Center" />
      <ProgressBar x:Name="progress" Height="8" />
    </StackPanel>
    <ui:Button
      x: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:Flyout
      x: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));
                else
                    await 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

标签:快手,List,request,AddHeader,爬取,C#,Text,new,model
From: https://www.cnblogs.com/hupo376787/p/18378511

相关文章

  • SP10502 VIDEO - Video game combos 题解
    题目传送门前置知识AC自动机解法多模式串匹配考虑AC自动机。令\(f_{i,j}\)表示前\(i\)个字符,当前运行到AC自动机的状态\(j\)时的最大得分。状态转移方程为\(f_{i,k}=\max\limits_{k\inSon(j)}\{f_{i-1,j}+sum_{k}\}\),其中\(sum_{k}\)表示fail树上以\(k......
  • Linux 内核源码分析---NIC 数据包接收与发送
    网络接口控制器(networkinterfacecontroller,NIC),又称网络接口控制器,网络适配器(networkadapter),网卡(networkinterfacecard),或局域网接收器(LANadapter),是一块被设计用来允许计算机在计算机网络上进行通信的计算机硬件。由于其拥有MAC地址,因此属于OSI模型的第2层。它使......
  • Citrix ADC Release 14.1 Build 29.63 (nCore, VPX, SDX, CPX, BLX) - 混合多云应用交
    CitrixADCRelease14.1Build29.63(nCore,VPX,SDX,CPX,BLX)-混合多云应用交付控制器CitrixADC-混合多云应用交付控制器请访问原文链接:https://sysin.org/blog/citrix-adc-14/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org大规模应用程序交付可能很复......
  • 记一次Win10磁盘空间清理 robocopy mklink
    #流程说明通过【robocopy】命令和【mklink】命令联动的方式进行【用户目录】分区迁移;几个注意事项:1.迁移【loki】用户的时候,需要再PE或者非loki用户自身进行操作,比如启用【administrator】2.进行删除用户目录为了保证数据内容已经完全同步,磁盘空间足够的情况下可以先将其重......
  • VulNyx - Ceres 靶机
    有80端口访问看看他这个挺奇葩的看了wp才知道file.php的参数是file他会自动给你加上php也就是说file=secret.php读不到数据要file=secret才能读到数据伪协议读取文件<?phpinclude($_GET["file"].".php");?><?phpsystem("id");///......
  • 看图学 - Swift actor
     本文首发于FicowShen'sBlog,原文地址:看图学-Swiftactor。 想第一时间获取对于自己有帮助的新内容?欢迎关注Ficow的公众号:  看图学Swiftactor 如需获取PDF版本思维导图、示例代码,请查阅公众号内容:《看图学-Swiftactor》......
  • 勇夺三项SOTA!北航&爱诗科技联合发布灵活高效可控视频生成方法TrackGo!
    论文链接:https://arxiv.org/pdf/2408.11475项目链接:https://zhtjtcz.github.io/TrackGo-Page/★亮点直击本文引入了一种新颖的运动可控视频生成方法,称为TrackGo。该方法为用户提供了一种灵活的运动控制机制,通过结合masks和箭头,实现了在复杂场景中的精确操控,包......
  • 历年CSP-J初赛真题解析 | 2015年CSP-J初赛阅读程序(23-26)
    学习C++从娃娃抓起!记录下CSP-J备考学习过程中的题目,记录每一个瞬间。附上汇总贴:历年CSP-J初赛真题解析|汇总_热爱编程的通信人的博客-CSDN博客#include<iostream>usingnamespacestd;intmain(){inta,b,c;a=1;b=2;c=3;if(a>b){......
  • CENTOS7部署http应用配置yum网络源-1
    通过采用部署http的方式实现共享yum源,供其它服务器实现rpm的快捷安装。该部署方式也适用于RHEL服务器。RHEL7服务器说明服务器信息说明192.168.10.131yum源服务主节点192.168.10.243从节点(从192.168.10.131上获取yum源)前提条件:1.两台服务器需关闭......
  • iTransformer时序模型改进——基于SENet和TCN的倒置Transformer,性能暴涨
    1数据集介绍ETT(电变压器温度):由两个小时级数据集(ETTh)和两个15分钟级数据集(ETTm)组成。它们中的每一个都包含2016年7月至2018年7月的七种石油和电力变压器的负载特征。 数据集链接:https://drive.google.com/drive/folders/1ZOYpTUa82_jCcxIdTmyr0LXQfvaM9vIy......