You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#实现URL认证站点文件下载 追踪Meta重定向获取真实地址

.NET 6 实现类文件托管站点Meta重定向追踪与文件下载

核心问题定位

  • 原链接提取逻辑错误:Select(p => p.InnerText)获取的是按钮显示文本download,并非onclick属性内的跳转地址,需要从button的onclick属性中提取目标URL
  • 默认HttpClient仅能自动处理301/302/307/308这类HTTP状态码的重定向,无法识别返回200状态码、写在HTML<meta http-equiv="refresh">标签内的元刷新重定向,这是请求pt参数链接仅能拿到页面内容的核心原因
  • 站点会校验会话Cookie,首次访问分享页时站点下发的Cookie需要在后续所有请求中携带,否则pt参数会校验失败,无法进入后续跳转流程
  • 链路终止判断规则:当跳转目标地址为CDN域名、且query参数中携带download_token时,即为可直接下载的真实文件地址

实现步骤

1. 初始化HttpClient

全程复用单例HttpClient避免端口耗尽,开启自动Cookie管理,配置浏览器请求头绕过基础反爬校验:

// 全局单例HttpClient,WPF中可放在App.xaml.cs中初始化
private static readonly HttpClient _httpClient = new HttpClient(new HttpClientHandler()
{
    UseCookies = true,
    CookieContainer = new CookieContainer(),
    AllowAutoRedirect = true, // 自动处理HTTP 3xx重定向,减少手动逻辑
    AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate | DecompressionMethods.Brotli
})
{
    Timeout = TimeSpan.FromSeconds(30)
};

// 初始化默认请求头,模拟Chrome浏览器
static App()
{
    _httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
    _httpClient.DefaultRequestHeaders.Accept.ParseAdd("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
    _httpClient.DefaultRequestHeaders.AcceptLanguage.ParseAdd("zh-CN,zh;q=0.9,en;q=0.8");
}

2. 从初始分享页提取带pt参数的跳转链接

修正原提取逻辑,从按钮的onclick属性中正则匹配跳转地址,用相对灵活的属性匹配替代容易失效的绝对XPath路径:

/// <summary>
/// 从分享页提取携带pt参数的中间跳转链接
/// </summary>
/// <param name="sharePageUri">初始分享页地址</param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private async Task<Uri> ExtractPtJumpLinkAsync(Uri sharePageUri)
{
    var pageHtml = await _httpClient.GetStringAsync(sharePageUri);
    var htmlDoc = new HtmlDocument();
    htmlDoc.LoadHtml(pageHtml);

    // 匹配下载按钮,兼容页面结构微调
    var downloadBtn = htmlDoc.DocumentNode.SelectSingleNode("//button[contains(@class, 'btn--primary') and normalize-space(text())='download']");
    if (downloadBtn == null)
        throw new InvalidOperationException("页面结构变更,未找到下载按钮");

    var onclickAttr = downloadBtn.GetAttributeValue("onclick", string.Empty);
    // 从onclick脚本中提取window.location赋值的URL
    var urlMatch = Regex.Match(onclickAttr, @"window\.location\s*=\s*'([^']+)'");
    if (!urlMatch.Success)
        throw new InvalidOperationException("下载按钮跳转规则变更,未找到pt参数链接");

    return new Uri(urlMatch.Groups[1].Value, UriKind.Absolute);
}

3. 实现Meta Refresh跳转链路追踪

手动解析每一跳200响应中的元刷新标签,直到命中最终CDN下载地址,设置最大跳转次数避免死循环:

/// <summary>
/// 追踪包含Meta Refresh的完整跳转链路,获取真实下载地址
/// </summary>
/// <param name="startUri">pt参数起始链接</param>
/// <returns>携带download_token的CDN真实地址</returns>
/// <exception cref="InvalidOperationException"></exception>
private async Task<Uri> TraceMetaRedirectAsync(Uri startUri)
{
    var currentUri = startUri;
    // 最多追踪10跳,防止死循环
    for (int jumpCount = 0; jumpCount < 10; jumpCount++)
    {
        // 命中CDN地址且携带download_token,直接返回
        if (currentUri.Host.EndsWith("cdnrobot.xyz") && 
            HttpUtility.ParseQueryString(currentUri.Query)["download_token"] != null)
        {
            return currentUri;
        }

        // 请求当前地址,仅先读取响应头减少开销
        using var response = await _httpClient.GetAsync(currentUri, HttpCompletionOption.ResponseHeadersRead);
        response.EnsureSuccessStatusCode();

        // 如果响应不是HTML类型,说明已经是文件流,直接返回当前地址
        if (response.Content.Headers.ContentType?.MediaType != "text/html")
        {
            return response.RequestMessage.RequestUri;
        }

        // 读取HTML内容解析Meta Refresh标签
        var htmlContent = await response.Content.ReadAsStringAsync();
        var metaDoc = new HtmlDocument();
        metaDoc.LoadHtml(htmlContent);

        // 兼容http-equiv属性大小写不固定的情况
        var metaRefreshNode = metaDoc.DocumentNode.SelectSingleNode("//meta[translate(@http-equiv, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='refresh']");
        if (metaRefreshNode == null)
            throw new InvalidOperationException($"跳转中断,第{jumpCount+1}跳未找到Meta Refresh标签");

        var contentAttr = metaRefreshNode.GetAttributeValue("content", string.Empty);
        // 从content中提取跳转url,格式为 [秒数];url=[目标地址]
        var urlMatch = Regex.Match(contentAttr, @"\d+;\s*url\s*=\s*(.+)", RegexOptions.IgnoreCase);
        if (!urlMatch.Success)
            throw new InvalidOperationException($"Meta Refresh规则变更,content值:{contentAttr}");

        var nextUrl = urlMatch.Groups[1].Value.Trim().Trim('\'', '"');
        // 处理相对路径跳转
        currentUri = new Uri(nextUrl, UriKind.RelativeOrAbsolute);
        if (!currentUri.IsAbsoluteUri)
            currentUri = new Uri(response.RequestMessage.RequestUri, currentUri);
    }

    throw new InvalidOperationException("跳转次数超过上限,链路存在死循环");
}

4. 完整调用与文件下载

WPF中直接await调用即可,不要用同步方法阻塞UI线程,大文件下载可自行扩展进度上报逻辑:

private async void StartDownloadBtn_OnClick(object sender, RoutedEventArgs e)
{
    try
    {
        var shareLink = new Uri("https://games-database.com/1JD4");
        // 第一步:提取pt中间链接
        var ptLink = await ExtractPtJumpLinkAsync(shareLink);
        // 第二步:追踪跳转拿真实CDN地址
        var realDownloadUrl = await TraceMetaRedirectAsync(ptLink);
        // 第三步:下载文件
        var fileBytes = await _httpClient.GetByteArrayAsync(realDownloadUrl);
        // 提取文件名保存到桌面
        var fileName = Path.GetFileName(realDownloadUrl.AbsolutePath);
        var savePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), fileName);
        await File.WriteAllBytesAsync(savePath, fileBytes);
        
        MessageBox.Show($"下载完成,文件已保存至:{savePath}");
    }
    catch (Exception ex)
    {
        MessageBox.Show($"下载失败:{ex.Message}");
    }
}

适配扩展说明

  • 适配1cloudfile、bowfiles等同机制站点时,仅需修改下载按钮匹配规则、CDN域名判断逻辑即可,跳转追踪核心逻辑可直接复用
  • 若遇到pt参数校验失败,可在请求pt链接前给_httpClient.DefaultRequestHeaders.Referrer赋值为初始分享页地址,部分站点会校验该字段
  • 若页面结构变更导致XPath匹配失败,优先用class、文本内容等相对稳定的属性做匹配,不要用浏览器复制的绝对XPath路径

内容的提问来源于stack exchange,提问作者Poggman Friedolin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 20:45:52