|
本帖最后由 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导入
- from dataclasses import dataclass, field
- import urllib.request
- import urllib.error
- import json
- from time import sleep
- @dataclass
- class PriceOverview:
- currency: str
- initial: float
- final: float
- local_initial: float
- local_final: float
-
- def __init__(self, currency, initial, final, rate):
- self.currency = currency
- self.initial = initial
- self.final = final
- self.local_initial = round(initial * rate, 2)
- self.local_final = round(final * rate, 2)
- @dataclass
- class Game:
- app_id: str
- name: str
- discount: int
- prices: dict[str, PriceOverview|None] = field(default_factory=dict)
- class SteamPriceManager:
- BATCH_SIZE = 100
- MAX_RETRIES = 5
- RETRY_DELAY = 1
-
- def __init__(self):
- self.countries = {
- 'CN': '中国',
- 'AR': '阿根廷',
- 'TR': '土耳其',
- 'RU': '俄罗斯',
- 'UA': '乌克兰',
- 'ZA': '南非',
- 'KZ': '哈萨克斯坦',
- 'IN': '印度',
- 'AZ': '独联体',
- 'PK': '南亚',
- 'BR': '巴西'
- }
-
- def try_fetch(self, url: str) -> dict:
- for attempt in range(self.MAX_RETRIES):
- try:
- with urllib.request.urlopen(url, timeout=10) as response:
- data = response.read().decode("utf-8")
- return json.loads(data)
- except (urllib.error.URLError, ValueError) as e:
- if attempt == self.MAX_RETRIES - 1:
- print(f"Error fetching data: {e}")
- return {}
- sleep(self.RETRY_DELAY)
- return {}
- def fetch_prices_batch(self, app_ids: list[str], country: str) -> dict:
- price_url = f"https://store.steampowered.com/api/appdetails?filters=price_overview&cc={country}&appids="
- prices_data = self.try_fetch(price_url + ",".join(app_ids))
- return prices_data if prices_data else {}
- def get_game_prices(self, games: list[Game], countries: list[str], rates: dict[str, float]) -> list[Game]:
- for g in games:
- g.prices = {c: None for c in countries}
-
- for c in countries:
- print(f"Fetching prices for {c}...")
- for i in range(0, len(games), self.BATCH_SIZE):
- batch = games[i:i + self.BATCH_SIZE]
- app_ids = [str(x.app_id) for x in batch]
- prices_data = self.fetch_prices_batch(app_ids, c)
-
- for game in batch:
- game_id = str(game.app_id)
- info = prices_data.get(game_id, {}).get("data", {})
- if info:
- p = info.get("price_overview")
- px = PriceOverview(
- currency=p.get("currency"),
- initial=p.get("initial") / 100.0,
- final=p.get("final") / 100.0,
- rate=rates.get(p.get("currency"))
- )
- game.prices[c] = px
- return games
- def get_wishlist(self) -> list[Game]:
- try:
- with open("wishlist.json", "r", encoding="utf-8") as f:
- data = json.load(f).get("data", [])
- return [Game(
- app_id=d["gameid"][1].split("/")[1],
- name=d["title"].replace(",", "."),
- discount=d["discount"]
- ) for d in data]
- except FileNotFoundError:
- return []
- def output_prices(self, games: list[Game]):
- filename = "wishlist_prices.csv"
- with open(filename, "w", encoding="utf-8") as f:
- f.write("App ID,名称,当前折扣")
- for c in self.countries.values():
- f.write(f",{c}价格")
- f.write("\n")
- for g in games:
- f.write(f"{g.app_id},{g.name},{g.discount}")
- for c in self.countries:
- px = g.prices.get(c)
- f.write(f",{px.local_final}" if px else ",")
- f.write("\n")
- print(f"Prices saved to {filename}")
- def get_rates(self) -> dict[str, float]:
- url = "https://api.augmentedsteam.com/rates/v1?to=CNY"
- rates = self.try_fetch(url)
- rates = {cur: val["CNY"] for cur, val in rates.items()}
- rates.update({"CNY": 1.0})
- return rates
- def run(self):
- wishlist = self.get_wishlist()
- if wishlist:
- print(f"Found {len(wishlist)} games in wishlist")
- rates = self.get_rates()
- updated = self.get_game_prices(wishlist, list(self.countries.keys()), rates)
- self.output_prices(updated)
- if __name__ == "__main__":
- SteamPriceManager().run()
复制代码
> excel脚本代码 导入后在excel里运行这个脚本就能自动将每个游戏的不同地区价格标上不同的颜色
- function main(workbook: ExcelScript.Workbook) {
- let selectedSheet = workbook.getActiveWorksheet();
- let usedRange = selectedSheet.getUsedRange();
- const startRow = 1;
- const endRow = usedRange.getRowCount();
- const startColumn = 3;
- const endColumn = usedRange.getColumnCount();
- for (let row = startRow; row <= endRow; row++) {
- const dataRange = selectedSheet.getRangeByIndexes(row, startColumn, 1, endColumn - startColumn);
- const conditionalFormats = dataRange.getConditionalFormats();
- let conditionalFormatting: ExcelScript.ConditionalFormat;
- if (conditionalFormats.length > 0) {
- conditionalFormatting = conditionalFormats[0];
- } else {
- conditionalFormatting = dataRange.addConditionalFormat(ExcelScript.ConditionalFormatType.colorScale);
- }
- conditionalFormatting.getColorScale().setCriteria({
- minimum: {
- color: "#63BE7B",
- type: ExcelScript.ConditionalFormatColorCriterionType.lowestValue
- },
- midpoint: {
- color: "#FFEB84",
- formula: '=50', type: ExcelScript.ConditionalFormatColorCriterionType.percentile
- },
- maximum: {
- color: "#F8696B",
- type: ExcelScript.ConditionalFormatColorCriterionType.highestValue
- }
- });
- }
- }
复制代码
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有帐号?注册
×
1、转载或引用本网站内容,必须注明本文网址:https://keylol.com/t982777-1-1。如发文者注明禁止转载,则请勿转载
2、对于不当转载或引用本网站内容而引起的民事纷争、行政处理或其他损失,本网站不承担责任
3、对不遵守本声明或其他违法、恶意使用本网站内容者,本网站保留追究其法律责任的权利
4、所有帖子仅代表作者本人意见,不代表本社区立场
|