回复
10
查看
544
收藏
17

1

赠楼

0%

赠楼率

926

蒸汽

19

主题

1027

帖子

2857

积分
发表于 20 小时前 · 上海 | 显示全部楼层 |阅读模式
本帖最后由 738156 于 2024-12-22 16:36 编辑

注意:Steam商店使用工具跨区购买是违反用户协议
趁着这次冬促 想着看看我愿望单里的游戏都在哪个区域便宜
于是开始翻steam的web api 但是翻到的api只给appid而不给名字
而又没有比较好的办法能完整的一次性用appid拿到对应的名字
对于游戏的价格倒是有个api能批量请求
这时候发现 Augmented Steam 插件能直接在愿望单界面导出json 本来还像看看用的什么api 结果发现是一堆protobuf 就放弃了直接拿导出的json来用
(顺便去看了它的源代码 发现他只能导出自己的愿望单 不过在导出的时候在一个地方下断点改掉传进去的steamid就能导出别人公开的愿望单)
写着写着想起来这样拿到的还是当地的货币 然后翻坛友的脚本翻到了 Augmented Steam 的汇率api 最后全部转成人民币价格保存成csv
想着用excel打开用条件格式标上颜色看起来方便点
结果发现excel直接打开csv会按操作系统的编码去读取 要选 数据 - 从文本/CSV 导入就可以用utf-8了

条件格式只能一个一个设也不方便 想写vba但是没找到按钮 只找到个脚本的按钮 查了下发现是typescript 那就用这个写吧

(不过后来发现这个叫 office 脚本 的功能只能商业和企业版才能用 我的是E5 属于企业版

更正:家庭和个人版加入预览体验计划也能用

最后的结果:


总结:
对于我的愿望单来说
在阿/土换成美元结算后 除去少量锁区的 俄区重新成为了低价区的王
其次是乌克兰和哈萨克斯坦
而南亚低的低 高的高 方差比较大


> python脚本代码
就看了一些常见的低价区 要加其他地区可以自己加
上面插件导出的愿望单保存为 ‘wishlist.json’ 放在同目录下
运行完会生成 ‘wishlist_prices.csv’ 就能让excel导入



  1. from dataclasses import dataclass, field
  2. import urllib.request
  3. import urllib.error
  4. import json
  5. from time import sleep

  6. @dataclass
  7. class PriceOverview:
  8.     currency: str
  9.     initial: float
  10.     final: float
  11.     local_initial: float
  12.     local_final: float
  13.    
  14.     def __init__(self, currency, initial, final, rate):
  15.         self.currency = currency
  16.         self.initial = initial
  17.         self.final = final
  18.         self.local_initial = round(initial * rate, 2)
  19.         self.local_final = round(final * rate, 2)

  20. @dataclass
  21. class Game:
  22.     app_id: str
  23.     name: str
  24.     discount: int
  25.     prices: dict[str, PriceOverview|None] = field(default_factory=dict)

  26. class SteamPriceManager:
  27.     BATCH_SIZE = 100
  28.     MAX_RETRIES = 5
  29.     RETRY_DELAY = 1
  30.    
  31.     def __init__(self):
  32.         self.countries = {
  33.             'CN': '中国',
  34.             'AR': '阿根廷',
  35.             'TR': '土耳其',
  36.             'RU': '俄罗斯',
  37.             'UA': '乌克兰',
  38.             'ZA': '南非',
  39.             'KZ': '哈萨克斯坦',
  40.             'IN': '印度',
  41.             'AZ': '独联体',
  42.             'PK': '南亚',
  43.             'BR': '巴西'
  44.         }
  45.    
  46.     def try_fetch(self, url: str) -> dict:
  47.         for attempt in range(self.MAX_RETRIES):
  48.             try:
  49.                 with urllib.request.urlopen(url, timeout=10) as response:
  50.                     data = response.read().decode("utf-8")
  51.                     return json.loads(data)
  52.             except (urllib.error.URLError, ValueError) as e:
  53.                 if attempt == self.MAX_RETRIES - 1:
  54.                     print(f"Error fetching data: {e}")
  55.                     return {}
  56.                 sleep(self.RETRY_DELAY)
  57.         return {}

  58.     def fetch_prices_batch(self, app_ids: list[str], country: str) -> dict:
  59.         price_url = f"https://store.steampowered.com/api/appdetails?filters=price_overview&cc={country}&appids="
  60.         prices_data = self.try_fetch(price_url + ",".join(app_ids))
  61.         return prices_data if prices_data else {}

  62.     def get_game_prices(self, games: list[Game], countries: list[str], rates: dict[str, float]) -> list[Game]:
  63.         for g in games:
  64.             g.prices = {c: None for c in countries}
  65.         
  66.         for c in countries:
  67.             print(f"Fetching prices for {c}...")
  68.             for i in range(0, len(games), self.BATCH_SIZE):
  69.                 batch = games[i:i + self.BATCH_SIZE]
  70.                 app_ids = [str(x.app_id) for x in batch]
  71.                 prices_data = self.fetch_prices_batch(app_ids, c)
  72.                
  73.                 for game in batch:
  74.                     game_id = str(game.app_id)
  75.                     info = prices_data.get(game_id, {}).get("data", {})
  76.                     if info:
  77.                         p = info.get("price_overview")
  78.                         px = PriceOverview(
  79.                             currency=p.get("currency"),
  80.                             initial=p.get("initial") / 100.0,
  81.                             final=p.get("final") / 100.0,
  82.                             rate=rates.get(p.get("currency"))
  83.                         )
  84.                         game.prices[c] = px
  85.         return games

  86.     def get_wishlist(self) -> list[Game]:
  87.         try:
  88.             with open("wishlist.json", "r", encoding="utf-8") as f:
  89.                 data = json.load(f).get("data", [])
  90.                 return [Game(
  91.                     app_id=d["gameid"][1].split("/")[1],
  92.                     name=d["title"].replace(",", "."),
  93.                     discount=d["discount"]
  94.                 ) for d in data]
  95.         except FileNotFoundError:
  96.             return []

  97.     def output_prices(self, games: list[Game]):
  98.         filename = "wishlist_prices.csv"
  99.         with open(filename, "w", encoding="utf-8") as f:
  100.             f.write("App ID,名称,当前折扣")
  101.             for c in self.countries.values():
  102.                 f.write(f",{c}价格")
  103.             f.write("\n")

  104.             for g in games:
  105.                 f.write(f"{g.app_id},{g.name},{g.discount}")
  106.                 for c in self.countries:
  107.                     px = g.prices.get(c)
  108.                     f.write(f",{px.local_final}" if px else ",")
  109.                 f.write("\n")
  110.         print(f"Prices saved to {filename}")

  111.     def get_rates(self) -> dict[str, float]:
  112.         url = "https://api.augmentedsteam.com/rates/v1?to=CNY"
  113.         rates = self.try_fetch(url)
  114.         rates = {cur: val["CNY"] for cur, val in rates.items()}
  115.         rates.update({"CNY": 1.0})
  116.         return rates

  117.     def run(self):
  118.         wishlist = self.get_wishlist()
  119.         if wishlist:
  120.             print(f"Found {len(wishlist)} games in wishlist")
  121.             rates = self.get_rates()
  122.             updated = self.get_game_prices(wishlist, list(self.countries.keys()), rates)
  123.             self.output_prices(updated)

  124. if __name__ == "__main__":
  125.     SteamPriceManager().run()

复制代码


> excel脚本代码
导入后在excel里运行这个脚本就能自动将每个游戏的不同地区价格标上不同的颜色
  1. function main(workbook: ExcelScript.Workbook) {
  2.   let selectedSheet = workbook.getActiveWorksheet();

  3.   let usedRange = selectedSheet.getUsedRange();
  4.   const startRow = 1;
  5.   const endRow = usedRange.getRowCount();
  6.   const startColumn = 3;
  7.   const endColumn = usedRange.getColumnCount();

  8.   for (let row = startRow; row <= endRow; row++) {
  9.     const dataRange = selectedSheet.getRangeByIndexes(row, startColumn, 1, endColumn - startColumn);
  10.     const conditionalFormats = dataRange.getConditionalFormats();

  11.     let conditionalFormatting: ExcelScript.ConditionalFormat;
  12.     if (conditionalFormats.length > 0) {
  13.       conditionalFormatting = conditionalFormats[0];
  14.     } else {
  15.       conditionalFormatting = dataRange.addConditionalFormat(ExcelScript.ConditionalFormatType.colorScale);
  16.     }

  17.     conditionalFormatting.getColorScale().setCriteria({
  18.       minimum: {
  19.         color: "#63BE7B",
  20.         type: ExcelScript.ConditionalFormatColorCriterionType.lowestValue
  21.       },
  22.       midpoint: {
  23.         color: "#FFEB84",
  24.         formula: '=50', type: ExcelScript.ConditionalFormatColorCriterionType.percentile
  25.       },
  26.       maximum: {
  27.         color: "#F8696B",
  28.         type: ExcelScript.ConditionalFormatColorCriterionType.highestValue
  29.       }
  30.     });
  31.   }
  32. }

复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

×
回复

使用道具 举报

浏览本版块需要:
1. 初阶会员或更高等级;
2. (点击此处)绑定Steam账号
您需要登录后才可以回帖 登录 | 注册

本版积分规则

欢迎发帖参与讨论 o(*≧▽≦)ツ,请注意
1. 寻求帮助或答案的帖子请发到问题互助版块,悬赏有助于问题解决的速度。发错可能失去在该板块发布主题的权限(了解更多
2. 表达观点可以,也请务必注意语气和用词,以免影响他人浏览,特别是针对其他会员的内容。如觉得违规可使用举报功能 交由管理人员处理,请勿引用对方的内容。
3. 开箱晒物交易中心游戏互鉴福利放送版块请注意额外的置顶版规。
4. 除了提问帖和交易帖以外,不确认发在哪个版块的帖子可以先发在谈天说地

  作为民间站点,自 2004 年起为广大中文 Steam 用户提供技术支持与讨论空间。历经二十余载风雨,如今已发展为国内最大的正版玩家据点。

列表模式 · · 微博 · Bilibili频道 · Steam 群组 · 贴吧 · QQ群 
Keylol 其乐 ©2004-2024 Chinese Steam User Fan Site.
Designed by Lee in Balestier, Powered by Discuz!
推荐使用 ChromeMicrosoft Edge 来浏览本站
广告投放|手机版|广州数趣信息科技有限公司 版权所有|其乐 Keylol ( 粤ICP备17068105号 )
GMT+8, 2024-12-22 22:26
快速回复 返回顶部 返回列表