使用URLSession获取网页返回403,如何模拟curl请求?
curl与URLSession GET请求的核心区别
- 默认请求头差异:curl会自动填充多个默认请求头(如
Accept-Encoding、Connection),而URLSession仅添加少量必要头,缺失这些细节可能触发网站的反爬虫校验。 - Cookie处理逻辑:curl默认复用本地Cookie文件中的存储内容,而URLSession使用独立的Cookie容器,若网站依赖Cookie验证请求合法性,会导致请求失败。
- SSL/TLS实现细节:curl基于OpenSSL库,URLSession使用Apple原生的Security.framework,部分网站会校验SSL指纹或TLS协议版本,两者的默认配置可能不匹配。
- 连接复用策略:curl和URLSession的HTTP连接池复用逻辑不同,部分网站会通过连接特征识别爬虫请求。
用URLSession模拟curl请求的步骤
完整复制curl的请求头
执行curl --verbose https://your-target-url.com,复制所有发送的请求头(包括Accept-Encoding、Connection等curl自动添加的头),将它们全部添加到URLRequest中,不要遗漏任何一项。同步Cookie(若需要)
如果curl请求中包含Cookie头(从--verbose输出中查看),将对应Cookie值直接添加到请求:urlRequest.setValue("cookie-key1=value1; cookie-key2=value2", forHTTPHeaderField: "Cookie")或者配置URLSession的全局Cookie容器:
let configuration = URLSessionConfiguration.default if let cookie = HTTPCookie(properties: [ .name: "cookie-name", .value: "cookie-value", .domain: "websiteurl.com", .path: "/" ]) { HTTPCookieStorage.shared.setCookie(cookie) } let session = URLSession(configuration: configuration)匹配SSL/TLS配置
若网站对TLS版本有要求,可调整URLSession的TLS配置:let configuration = URLSessionConfiguration.default configuration.tlsMinimumSupportedProtocolVersion = .TLSv12 let session = URLSession(configuration: configuration)校验请求一致性
用抓包工具(如Charles)对比curl和URLSession的请求包,确保请求行、所有头字段、连接参数完全一致。
针对你的代码的修改建议
你当前只添加了3个请求头,缺少curl默认的Accept-Encoding、Connection等关键头,修改后的代码示例:
guard let url = URL(string: "https://websiteurl.com") else { fatalError("无效URL") } var urlRequest = URLRequest(url: url) urlRequest.httpMethod = "GET" // 从curl --verbose获取的完整请求头 urlRequest.setValue("websiteurl.com", forHTTPHeaderField: "Host") urlRequest.setValue("curl/8.4.0", forHTTPHeaderField: "User-Agent") urlRequest.setValue("*/*", forHTTPHeaderField: "Accept") urlRequest.setValue("gzip, deflate, br", forHTTPHeaderField: "Accept-Encoding") urlRequest.setValue("keep-alive", forHTTPHeaderField: "Connection") // 若curl输出中有其他头(如Upgrade-Insecure-Requests)也需添加 urlRequest.setValue("1", forHTTPHeaderField: "Upgrade-Insecure-Requests") do { let (data, response) = try await URLSession.shared.data(for: urlRequest) if let httpResponse = response as? HTTPURLResponse { print(httpResponse.statusCode) // 可打印响应体查看反爬虫提示 if let responseBody = String(data: data, encoding: .utf8) { print(responseBody) } } } catch { print("请求错误: \(error)") }
内容的提问来源于stack exchange,提问作者Hadi Sharghi
相关产品推荐
相关产品推荐

