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

从R转换的F#代码无法获取韩国股市API的OTP令牌

F# HTTP POST请求无法获取KRX OTP令牌(返回空内容)

我看了你的代码和对应的R实现,问题的核心是请求方法不匹配,加上缺少必要的请求头模拟浏览器行为,导致KRX服务器没有返回预期的OTP令牌。

问题拆解

  1. 请求方法不一致:你的R代码明确用POST方法发送请求,但当前F#代码默认使用GET(HttpWebRequest.Create默认请求方法是GET),而KRX的GenerateOTP接口要求必须用POST请求才会生成并返回OTP。
  2. 参数未做URL编码:你手动拼接参数时没有对特殊字符(比如pagePath里的斜杠)做URL编码,可能导致服务器无法正确解析请求参数。
  3. 缺少浏览器标识头:很多网站会拒绝无User-Agent标识的请求,你的F#代码没有添加这个请求头,可能被服务器拦截。

修正后的完整代码

下面是和你的R逻辑对齐、能正常获取OTP的F#代码:

open System
open System.Net
open System.IO
open System.Text

type KRXQuery = {
    name: string
    filetype: string
    url: string
    tp_cd: string
    date: string
    lang: string
    pagePath: string
}

[<EntryPoint>]
let main argv =
    let otpUrl = "http://marketdata.krx.co.kr/contents/COM/GenerateOTP.jspx"
    let krxQuery = {
        name = "fileDown"
        filetype = "csv"
        url = "MKD/03/0303/03030103/mkd03030103"
        tp_cd = "ALL"
        date = "20190607"
        lang = "ko"
        pagePath = "/contents/MKD/03/0303/03030103/MKD03030103.jsp"
    }

    // 带URL编码的参数构建器,避免特殊字符解析错误
    let queryBuilder (p: KRXQuery) =
        [
            "name", p.name
            "filetype", p.filetype
            "url", p.url
            "tp_cd", p.tp_cd
            "date", p.date
            "lang", p.lang
            "pagePath", p.pagePath
        ]
        |> List.map (fun (k, v) -> sprintf "%s=%s" k (Uri.EscapeDataString v))
        |> String.concat "&"

    let fetchOTP url query =
        // 和R代码逻辑一致:参数拼在URL查询字符串,用POST请求
        let targetUrl = sprintf "%s?%s" url (queryBuilder query)
        printfn "Request URL: %s" targetUrl

        let req = WebRequest.Create(targetUrl) :?> HttpWebRequest
        // 关键:设置请求方法为POST
        req.Method <- "POST"
        // 添加User-Agent模拟浏览器请求
        req.UserAgent <- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
        // 参数已在URL中,无需请求体
        req.ContentLength <- 0L

        use resp = req.GetResponse() :?> HttpWebResponse
        printfn "Response Status: %s" resp.StatusDescription
        use stream = resp.GetResponseStream()
        use reader = new StreamReader(stream, Encoding.UTF8)
        reader.ReadToEnd()

    let otp = fetchOTP otpUrl krxQuery
    printfn "OTP Token: %s" otp
    0

可选:用表单请求体发送参数

如果你想更符合POST请求的规范,把参数放在请求体中发送(而非URL查询字符串),可以修改fetchOTP函数如下:

let fetchOTP url query =
    let formData = queryBuilder query
    let postBytes = Encoding.UTF8.GetBytes formData

    let req = WebRequest.Create(url) :?> HttpWebRequest
    req.Method <- "POST"
    req.UserAgent <- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
    req.ContentType <- "application/x-www-form-urlencoded; charset=UTF-8"
    req.ContentLength <- int64 postBytes.Length

    // 写入表单数据到请求体
    use reqStream = req.GetRequestStream()
    reqStream.Write(postBytes, 0, postBytes.Length)

    use resp = req.GetResponse() :?> HttpWebResponse
    printfn "Response Status: %s" resp.StatusDescription
    use stream = resp.GetResponseStream()
    use reader = new StreamReader(stream, Encoding.UTF8)
    reader.ReadToEnd()

两种方式都能正常获取KRX的OTP令牌,你可以根据需求选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 00:22:45