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

Python urllib请求Unreal Marketplace API遇403 Forbidden错误求助

问题描述

手动在Firefox、Edge、Opera浏览器访问目标URL正常,但Python脚本运行时出现urllib.error.HTTPError: HTTP Error 403: Forbidden错误。已尝试添加User-Agent请求头,问题仍未解决。

解决方案

403错误通常是服务器检测到请求并非来自真实浏览器,仅User-Agent不足以通过校验,需补充更多浏览器特征的请求头,或优化请求方式,以下是可行修复方案:

方案1:完善urllib请求头并添加请求延迟

服务器会校验多个请求头字段,补充Accept、Accept-Language、Referer等常见字段,同时添加请求延迟避免触发反爬:

import json
import os
import urllib.request
import time

# 模拟真实浏览器的请求头组合
opener = urllib.request.build_opener()
opener.addheaders = [
    ('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'),
    ('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8'),
    ('Accept-Language', 'en-US,en;q=0.5'),
    ('Referer', 'https://www.unrealengine.com/marketplace'),
    # 可选:从浏览器开发者工具复制真实Cookie添加
    # ('Cookie', 'your_cookie_content_here')
]
urllib.request.install_opener(opener)

MP_URL = "https://www.unrealengine.com/marketplace"
JSON_DIR = "./data"
OUT_FILE = "mp_vault_list.md"


def main():
    mp_jsons = []
    cost = 0.
    disc = 0.
    currency = ""
    count = 0
    for f in os.listdir(JSON_DIR):
        if not f.endswith(".json"):
            continue
        with open(os.path.join(JSON_DIR, f), encoding="utf8") as fd:
            j = json.load(fd)
            count += len(j['data']['elements'])
            mp_jsons.append(j)
    with open(OUT_FILE, "w+", encoding="utf8") as fd:
        fd.write(f"# Vault content\n\n")
        i = 0
        for j in mp_jsons:
            for el in j['data']['elements']:
                i += 1
                print(f"\rProcessing Vault {i}/{count}", end="")
                # 添加1秒延迟,避免频繁请求触发反爬
                time.sleep(1)
                try:
                    with urllib.request.urlopen(f"{MP_URL}/api/assets/asset/{el['id']}") as f:
                        jj = json.load(f)
                        jjd = jj['data']['data']
                        fd.write(f"* [{jjd['title']}]({MP_URL}/en-US/product/{jjd['urlSlug']})\n")
                        cost += jjd['priceValue']
                        currency = jjd['currencyCode']
                        try:
                            disc += jjd['discountPriceValue']
                        except KeyError:
                            pass
                except urllib.error.HTTPError as e:
                    print(f"\nError accessing {el['id']}: {e}")

        fd.write(f"\n# Vault stats\n\n")
        fd.write(f"* Value: {cost / 100}{currency}\n")
        fd.write(f"* Current cost: {disc / 100}{currency}\n")
        fd.write(f"* Size: {count}\n")


if __name__ == '__main__':
    main()

方案2:改用requests库(更简便)

requests库对请求头和会话的处理更友好,能轻松维持浏览器式的请求状态:

import json
import os
import requests
import time

MP_URL = "https://www.unrealengine.com/marketplace"
JSON_DIR = "./data"
OUT_FILE = "mp_vault_list.md"

# 构建会话,统一设置请求头
session = requests.Session()
session.headers.update({
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8',
    'Accept-Language': 'en-US,en;q=0.5',
    'Referer': 'https://www.unrealengine.com/marketplace',
    # 可选:添加浏览器Cookie
    # 'Cookie': 'your_cookie_content_here'
})


def main():
    mp_jsons = []
    cost = 0.
    disc = 0.
    currency = ""
    count = 0
    for f in os.listdir(JSON_DIR):
        if not f.endswith(".json"):
            continue
        with open(os.path.join(JSON_DIR, f), encoding="utf8") as fd:
            j = json.load(fd)
            count += len(j['data']['elements'])
            mp_jsons.append(j)
    with open(OUT_FILE, "w+", encoding="utf8") as fd:
        fd.write(f"# Vault content\n\n")
        i = 0
        for j in mp_jsons:
            for el in j['data']['elements']:
                i += 1
                print(f"\rProcessing Vault {i}/{count}", end="")
                time.sleep(1)
                try:
                    response = session.get(f"{MP_URL}/api/assets/asset/{el['id']}")
                    response.raise_for_status()  # 主动触发HTTP错误
                    jj = response.json()
                    jjd = jj['data']['data']
                    fd.write(f"* [{jjd['title']}]({MP_URL}/en-US/product/{jjd['urlSlug']})\n")
                    cost += jjd['priceValue']
                    currency = jjd['currencyCode']
                    try:
                        disc += jjd['discountPriceValue']
                    except KeyError:
                        pass
                except requests.exceptions.RequestException as e:
                    print(f"\nError accessing {el['id']}: {e}")

        fd.write(f"\n# Vault stats\n\n")
        fd.write(f"* Value: {cost / 100}{currency}\n")
        fd.write(f"* Current cost: {disc / 100}{currency}\n")
        fd.write(f"* Size: {count}\n")


if __name__ == '__main__':
    main()

关键修复说明

  • 补充请求头:服务器会校验Accept、Referer等字段,模拟真实浏览器的请求头组合能降低被拦截概率
  • 添加请求延迟:time.sleep(1)避免短时间内大量请求触发反爬机制
  • 会话维持:使用会话对象能保持请求状态,更接近浏览器的访问行为
  • Cookie(可选):若服务器需要验证会话,可从浏览器开发者工具的Network面板复制Cookie添加到请求头

内容的提问来源于stack exchange,提问作者Franklyn W. R. Tigier

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 03:14:55