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

Azure函数中C#实现网页登录后调用JS导出Excel的方案咨询

问题描述

我现有一段C#代码,通过自定义WebClient类实现了程序化登录供应商网站并抓取图片存入数据库,运行正常。现在需要获取页面导出按钮(导出为Excel)对应的最新数据,通过F12和Fiddler分析发现该按钮调用一段JavaScript函数。我对Web端知识有限,有两个疑问:

  1. 是否需要改用WebBrowser?
  2. 若改用(或继续用现有方式),如何在Azure函数Http触发器中持久化登录Cookie?
    该代码将用于Logic Apps流程。

自定义WebClient类代码

public class WebClientEx : WebClient
{
    public CookieContainer CookieContainer { get; private set; }

    public WebClientEx()
    {
        CookieContainer = new CookieContainer();
    }

    protected override WebRequest GetWebRequest(Uri address)
    {
        var request = base.GetWebRequest(address);
        if (request is HttpWebRequest)
        {
            (request as HttpWebRequest).CookieContainer = CookieContainer;
        }
        return request;
    }
}

static string GetSourceForMyShowsPage(string JobId)
{
    var sqlinsert = new SQLInsert();

    using (var client = new WebClientEx())
    {
        var values = new NameValueCollection
        {
            { "UserName", "sss" },
            { "Password", "sss" },
        };
        // Authenticate
        client.UploadValues("url/Login", values);

        //get the image url that we need to get the image in bytes - we might need to change this to a byte method?
        string responseMessage = client.DownloadString("myurl" + JobId);
        string imageurl = "";
        byte[] imageBytes;

        //search for images in string
        HtmlDocument document = new HtmlDocument();
        document.LoadHtml(responseMessage);
        document.DocumentNode.Descendants("img")
                  .Where(e =>
                  {
                      string src = e.GetAttributeValue("src", null) ?? "";
                      return !string.IsNullOrEmpty(src) && src.StartsWith(@"/Photo");
                  })
                  .ToList()
                  .ForEach(x =>
                  {
                      imageurl = x.GetAttributeValue("src", null);

                      imageurl = imageurl.Substring(0, imageurl.IndexOf("width") - 1);
                      imageurl = imageurl.Replace("amp;", "");
                      imageurl = imageurl.Replace("&amp", "");
                      imageurl = "url/" + imageurl;

                      imageBytes = client.DownloadData(imageurl);

                      string base64image = Convert.ToBase64String(imageBytes);

                      //upload data to sql table
                      sqlinsert.executeInsert(JobId, base64image);
                  });


        // Download desired page
        return client.DownloadString("url" + JobId);
    }
}

需模拟的JavaScript导出函数

function onExportClicked() {
    var data = ST.Util.serialiseForm($('#shared-tag-search-form'));
    var url = '/Reports/SharedTag';
    var form = $('<form method="POST" action="' + url + '">');
    data.push({ name: 'type', value: 'ExportToXls' });
    $.each(data, function (k, v) {
        form.append($('<input type="hidden" name="' + v.name + '" value="' + v.value + '">'));
    });
    $('body').append(form);
    form.submit();
}

解决方案

是否需要改用WebBrowser?

不需要。WebBrowser是依赖UI环境的页面渲染控件,在Azure函数这类无UI的服务器运行环境中无法正常工作,而且完全没必要——我们可以直接模拟JavaScript函数发起的HTTP请求:

  1. 拆解JS逻辑:该函数本质是收集#shared-tag-search-form表单的所有字段,添加type=ExportToXls参数,然后以POST方式提交到/Reports/SharedTag地址。
  2. 直接用现有WebClientEx构造相同的POST请求即可,无需执行JavaScript。

如何在Azure函数Http触发器中持久化登录Cookie?

Azure函数是无状态的,每次调用都会初始化新实例,因此需要将登录后的CookieContainer序列化存储到外部介质,下次调用时反序列化复用:

  1. 序列化CookieContainer:可以用二进制序列化或自定义格式提取Cookie(注意保留Cookie的Domain、Path等属性)。
  2. 存储介质选择:推荐使用Azure Blob存储、Azure Redis缓存或SQL数据库,根据成本和性能需求决定。
  3. 复用Cookie:下次请求前从存储介质读取并反序列化CookieContainer,赋值给WebClientEx的对应属性,避免重复登录。

示例:添加导出Excel的方法

在现有逻辑中新增导出逻辑,直接模拟POST请求:

static byte[] ExportExcel(string JobId, WebClientEx client)
{
    // 先获取页面,提取#shared-tag-search-form的表单字段(若表单为动态生成)
    string pageHtml = client.DownloadString("url" + JobId);
    HtmlDocument document = new HtmlDocument();
    document.LoadHtml(pageHtml);
    
    // 提取表单字段(模拟ST.Util.serialiseForm的逻辑)
    var formData = new NameValueCollection();
    document.DocumentNode.Descendants("input")
        .Where(e => e.HasAttribute("name") && !string.IsNullOrEmpty(e.GetAttributeValue("name", "")))
        .ToList()
        .ForEach(input =>
        {
            string name = input.GetAttributeValue("name", "");
            string value = input.GetAttributeValue("value", "");
            formData.Add(name, value);
        });
    
    // 添加导出类型参数
    formData.Add("type", "ExportToXls");
    
    // 发起POST请求,获取Excel字节流
    return client.UploadValues("url/Reports/SharedTag", "POST", formData);
}

示例:Cookie持久化到Azure Blob

// 序列化CookieContainer为字节数组
static byte[] SerializeCookieContainer(CookieContainer container)
{
    using (var ms = new MemoryStream())
    {
        var formatter = new BinaryFormatter();
        formatter.Serialize(ms, container);
        return ms.ToArray();
    }
}

// 反序列化字节数组为CookieContainer
static CookieContainer DeserializeCookieContainer(byte[] data)
{
    using (var ms = new MemoryStream(data))
    {
        var formatter = new BinaryFormatter();
        return formatter.Deserialize(ms) as CookieContainer;
    }
}

// 将Cookie存储到Azure Blob
async static Task SaveCookiesToBlob(byte[] cookieData, string blobName)
{
    var blobServiceClient = new BlobServiceClient("your-azure-storage-connection-string");
    var containerClient = blobServiceClient.GetBlobContainerClient("cookies-container");
    await containerClient.CreateIfNotExistsAsync();
    var blobClient = containerClient.GetBlobClient(blobName);
    await blobClient.UploadAsync(new MemoryStream(cookieData), overwrite: true);
}

// 从Azure Blob读取Cookie
async static Task<CookieContainer> LoadCookiesFromBlob(string blobName)
{
    var blobServiceClient = new BlobServiceClient("your-azure-storage-connection-string");
    var containerClient = blobServiceClient.GetBlobContainerClient("cookies-container");
    var blobClient = containerClient.GetBlobClient(blobName);
    if (!await blobClient.ExistsAsync())
        return null;
    
    using (var ms = new MemoryStream())
    {
        await blobClient.DownloadToAsync(ms);
        ms.Position = 0;
        return DeserializeCookieContainer(ms.ToArray());
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 19:27:36