从R转换的F#代码无法获取韩国股市API的OTP令牌
F# HTTP POST请求无法获取KRX OTP令牌(返回空内容)
我看了你的代码和对应的R实现,问题的核心是请求方法不匹配,加上缺少必要的请求头模拟浏览器行为,导致KRX服务器没有返回预期的OTP令牌。
问题拆解
- 请求方法不一致:你的R代码明确用
POST方法发送请求,但当前F#代码默认使用GET(HttpWebRequest.Create默认请求方法是GET),而KRX的GenerateOTP接口要求必须用POST请求才会生成并返回OTP。 - 参数未做URL编码:你手动拼接参数时没有对特殊字符(比如
pagePath里的斜杠)做URL编码,可能导致服务器无法正确解析请求参数。 - 缺少浏览器标识头:很多网站会拒绝无
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
相关产品推荐
相关产品推荐

