diff --git a/2609每日数据-武汉萌奇熊.xlsx b/2609每日数据-武汉萌奇熊.xlsx new file mode 100644 index 0000000..f0298f2 Binary files /dev/null and b/2609每日数据-武汉萌奇熊.xlsx differ diff --git a/build_data.py b/build_data.py index 75b75ac..06a9d4a 100644 --- a/build_data.py +++ b/build_data.py @@ -5,10 +5,14 @@ # 规则:@某某 == 某某;未填投放人的 Adpingid 丢弃; # exclude.json 的 packages(按包名)与 adpings(按 Adpingid)永久剔除,均为精确匹配。 # 每个 package 明细:Geo 后缀 = 该 package 花费最高的地区;Adpingid = 该地区内花费最高的 Adping。 -import openpyxl, collections, json, datetime, os, sys +import openpyxl, collections, json, datetime, os, sys, glob HERE = os.path.dirname(os.path.abspath(__file__)) -SRC = os.path.join(HERE, '2608每日数据-武汉萌奇熊.xlsx') +# 源文件按月滚动(2608 -> 2609 -> ...)。全部读进来合并: +# 同一天出现在多个文件时,以文件名较新的为准(源表会修订历史数据)。 +SRC_FILES = sorted(glob.glob(os.path.join(HERE, '*每日数据-武汉萌奇熊.xlsx'))) +if not SRC_FILES: + sys.exit('ERROR: 仓库里找不到 *每日数据-武汉萌奇熊.xlsx') _cfg = json.load(open(os.path.join(HERE, 'exclude.json'))) EXCL = set(_cfg['packages']) # 按包名剔除 EXCL_ADP = set(_cfg.get('adpings', {})) # 按 Adpingid 剔除 @@ -20,12 +24,20 @@ def key(v): return str(int(v)) return str(v).strip() -wb = openpyxl.load_workbook(SRC, data_only=True) +books = [(f, openpyxl.load_workbook(f, data_only=True)) for f in SRC_FILES] + +# --- 投手表:取最新一个带「投手」sheet 的文件(月度文件可能不含该表)--- +roster_file, roster = None, None +for f, wb_ in reversed(books): + if '投手' in wb_.sheetnames: + roster_file, roster = f, wb_['投手'] + break +if roster is None: + sys.exit('ERROR: 所有源文件都没有「投手」sheet,无法归属投放人') -# --- 投手表:Adpingid -> 投放人 --- amap = {} dropped_ids = set(EXCL_ADP) # 先纳入按 Adpingid 剔除的,再补上按包名命中的 -for r in wb['投手'].iter_rows(min_row=2, values_only=True): +for r in roster.iter_rows(min_row=2, values_only=True): adp, pkg, buyer, status = r[1], r[3], r[11], r[0] if adp is None or str(status).strip() == '示例': continue @@ -42,19 +54,25 @@ for r in wb['投手'].iter_rows(min_row=2, values_only=True): owned = collections.Counter(amap.values()) # 每位投手名下的 Adpingid 数(已扣除排除包名) -# --- 平台消耗统计 --- -rows = [] -for r in wb['平台消耗统计'].iter_rows(min_row=2, values_only=True): - d, adp, pkg, geo, spend = r[1], r[2], r[3], r[4], r[8] - if d is None or adp is None or not isinstance(d, datetime.datetime): +# --- 平台消耗统计:逐文件读取,按日期归组,新文件覆盖同一天的旧数据 --- +by_date = {} +for f, wb_ in books: + if '平台消耗统计' not in wb_.sheetnames: continue - try: - s = float(spend) if spend is not None else 0.0 - except (TypeError, ValueError): - s = 0.0 - rows.append((d.date(), key(adp), (str(pkg).strip() if pkg else '(未知)'), - (str(geo).strip() if geo else ''), s)) + day = collections.defaultdict(list) + for r in wb_['平台消耗统计'].iter_rows(min_row=2, values_only=True): + d, adp, pkg, geo, spend = r[1], r[2], r[3], r[4], r[8] + if d is None or adp is None or not isinstance(d, datetime.datetime): + continue + try: + s = float(spend) if spend is not None else 0.0 + except (TypeError, ValueError): + s = 0.0 + day[d.date()].append((d.date(), key(adp), (str(pkg).strip() if pkg else '(未知)'), + (str(geo).strip() if geo else ''), s)) + by_date.update(day) # books 已按文件名升序,后者覆盖前者 +rows = [r for day_rows in by_date.values() for r in day_rows] if not rows: sys.exit('ERROR: 平台消耗统计 无有效数据行') @@ -132,6 +150,8 @@ data = { } json.dump(data, open(os.path.join(HERE, 'data.json'), 'w'), ensure_ascii=False, indent=2) +print('源文件:', ', '.join(os.path.basename(f) for f in SRC_FILES)) +print('投手表取自:', os.path.basename(roster_file)) print(f"窗口 {start} -> {end}") print(f"排除包名命中 {excluded_rows} 行,剔除花费 {excluded_spend:.2f}") print(f"合计 {data['grand_total']:.2f} / Adpingid {data['week34_id_count']}") diff --git a/data.json b/data.json index 199d8c3..da00276 100644 --- a/data.json +++ b/data.json @@ -1,19 +1,19 @@ { "meta": { - "week": "Last 3 Days: 8/28-8/30", + "week": "Last 3 Days: 8/29-8/31", "source": "萌奇熊日报", - "generated_at": "2026-08-31", - "date_range": "2026/08/28-2026/08/30", + "generated_at": "2026-09-01", + "date_range": "2026/08/29-2026/08/31", "note": "投放人以「投手」表 Media Buyer 列为准,@某某 已与某某合并计算;未填写投放人的 Adpingid 不计入;exclude.json 所列包名已排除。Package 后缀为该 Package 花费最高的投放地区,Adpingid 为该地区内花费最高的 Adping。" }, - "grand_total": 11581.3293, - "week34_id_count": 123, + "grand_total": 11872.5278, + "week34_id_count": 119, "buyers": [ { "name": "黄政", "@name": "黄政", - "total": 3344.4122, - "active_ids": 32, + "total": 3480.3241, + "active_ids": 31, "owned_ids": 133, "pkgs": [ { @@ -21,42 +21,42 @@ "geo": "SA", "label": "com.amazon.mShop.android.shopping_SA", "adping": "2732727", - "spend": 1145.5168 + "spend": 1220.3056 }, { "pkg": "com.king.candycrushsodasaga", - "geo": "BR", - "label": "com.king.candycrushsodasaga_BR", - "adping": "8460883", - "spend": 1098.694 + "geo": "DE", + "label": "com.king.candycrushsodasaga_DE", + "adping": "6951594", + "spend": 1089.63 }, { "pkg": "com.yandex.lavka", "geo": "RU", "label": "com.yandex.lavka_RU", "adping": "8156506", - "spend": 360.9953 + "spend": 340.0207 }, { "pkg": "com.fxpro.direct.application", "geo": "PK", "label": "com.fxpro.direct.application_PK", "adping": "7202934", - "spend": 234.09 + "spend": 233.96 }, { - "pkg": "ru.banki.banki", + "pkg": "tech.jm", "geo": "RU", - "label": "ru.banki.banki_RU", - "adping": "91548044", - "spend": 137.5186 + "label": "tech.jm_RU", + "adping": "1745925", + "spend": 156.7521 } ] }, { "name": "万科", "@name": "万科", - "total": 2426.0128, + "total": 2318.8753, "active_ids": 24, "owned_ids": 112, "pkgs": [ @@ -65,42 +65,42 @@ "geo": "TH", "label": "com.king.candycrushsaga_TH", "adping": "5497580", - "spend": 1059.413 + "spend": 968.234 }, { "pkg": "ru.aliexpress.buyer", "geo": "RU", "label": "ru.aliexpress.buyer_RU", "adping": "6607117", - "spend": 953.539 + "spend": 938.279 }, { "pkg": "ru.mobstudio.andgalaxy", "geo": "RU", "label": "ru.mobstudio.andgalaxy_RU", "adping": "7953512", - "spend": 152.69 + "spend": 144.16 }, { "pkg": "ru.kinopoisk", "geo": "RU", "label": "ru.kinopoisk_RU", "adping": "10599681", - "spend": 126.88 + "spend": 126.23 }, { "pkg": "com.edadeal.android", "geo": "RU", "label": "com.edadeal.android_RU", "adping": "10599157", - "spend": 81.2 + "spend": 85.55 } ] }, { "name": "谭超", "@name": "谭超", - "total": 2250.706, + "total": 2311.848, "active_ids": 23, "owned_ids": 124, "pkgs": [ @@ -109,43 +109,43 @@ "geo": "ID", "label": "com.kwai.bulldog_ID", "adping": "7815836", - "spend": 1148.97 + "spend": 1149.95 }, { "pkg": "com.vitastudio.mahjong", "geo": "ID", "label": "com.vitastudio.mahjong_ID", "adping": "91467820", - "spend": 465.14 + "spend": 519.33 }, { "pkg": "astrum.ent.m2alyona", "geo": "RU", "label": "astrum.ent.m2alyona_RU", "adping": "11690181", - "spend": 273.82 + "spend": 263.12 }, { "pkg": "tech.jm", "geo": "RU", "label": "tech.jm_RU", "adping": "9297014", - "spend": 116.73 + "spend": 196.63 }, { "pkg": "in.startv.hotstar.dplus", "geo": "ID", "label": "in.startv.hotstar.dplus_ID", "adping": "9096620", - "spend": 71.95 + "spend": 73.74 } ] }, { "name": "周梓君", "@name": "周梓君", - "total": 1860.5323, - "active_ids": 27, + "total": 1954.4797, + "active_ids": 26, "owned_ids": 119, "pkgs": [ { @@ -153,14 +153,14 @@ "geo": "KZ", "label": "ru.ozon.app.android_KZ", "adping": "5963354", - "spend": 974.2013 + "spend": 1000.9987 }, { "pkg": "ru.kinopoisk", "geo": "RU", "label": "ru.kinopoisk_RU", "adping": "8291713", - "spend": 575.96 + "spend": 630.0 }, { "pkg": "com.edadeal.android", @@ -174,22 +174,22 @@ "geo": "RU", "label": "com.yandex.bank_RU", "adping": "2937658", - "spend": 65.721 + "spend": 75.981 }, { "pkg": "com.appteka.lapy", "geo": "RU", "label": "com.appteka.lapy_RU", "adping": "5623760", - "spend": 17.2 + "spend": 21.2 } ] }, { "name": "詹涵粤", "@name": "詹涵粤", - "total": 1699.666, - "active_ids": 17, + "total": 1807.0007, + "active_ids": 15, "owned_ids": 144, "pkgs": [ { @@ -197,50 +197,50 @@ "geo": "MY", "label": "com.king.candycrushsaga_MY", "adping": "5464039", - "spend": 1144.35 + "spend": 1154.11 }, { "pkg": "com.esprit.rpg.lttlefirykng", "geo": "RU", "label": "com.esprit.rpg.lttlefirykng_RU", "adping": "10514860", - "spend": 311.45 + "spend": 359.1 }, { "pkg": "com.reefgames.fabletown", "geo": "RU", "label": "com.reefgames.fabletown_RU", "adping": "91532857", - "spend": 70.9804 - }, - { - "pkg": "net.peakgames.Yuzbir", - "geo": "TR", - "label": "net.peakgames.Yuzbir_TR", - "adping": "91550293", - "spend": 38.75 + "spend": 63.8392 }, { "pkg": "com.netease.newspike", "geo": "US", "label": "com.netease.newspike_US", "adping": "91551388", - "spend": 36.6713 + "spend": 62.3091 + }, + { + "pkg": "net.peakgames.Yuzbir", + "geo": "TR", + "label": "net.peakgames.Yuzbir_TR", + "adping": "91550293", + "spend": 45.85 } ] } ], "unmatched": [ + { + "adping": "11729927", + "pkg": "com.amazon.mShop.android.shopping", + "spend": 13.5 + }, { "adping": "11729830", "pkg": "com.amazon.mShop.android.shopping", "spend": 0.002 }, - { - "adping": "11729927", - "pkg": "com.amazon.mShop.android.shopping", - "spend": 0.0 - }, { "adping": "11729820", "pkg": "com.amazon.mShop.android.shopping",