Files
hiddencode_project/zq/match_all.py

146 lines
7.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# 遍历 jsons/ 目录下所有 JSON 文件,按 URL 来源匹配 Excel 暗码:
# - url 含 www.21cnjy.com → 在 "21世纪资料名" 列精确匹配 filename[0]
# - url 含 www.zxxk.com → 在 "学科网资料名" 列精确匹配 filename[0]
# 学科网资料名有重复时,从 item 的 url 中提取数字 ID 与各暗码候选值比对消歧
# - 匹配成功写入 xkw_code未匹配写入 "0"
# - 未匹配数据汇总写入 问题数据/未匹配填充0.json
import json # 用于读写 JSON 文件
import re # 用于正则表达式提取 URL 中的数字 ID
import glob # 用于批量匹配目录下的文件路径
import openpyxl # 用于读取 Excel (.xlsx) 文件
EXCEL_PATH = "结果.xlsx" # Excel 数据文件路径
JSON_DIR = "jsons" # JSON 文件所在目录
UNMATCHED_OUT = "问题数据/未匹配填充0.json" # 未匹配数据输出路径
def clean(v):
# 将单元格值转为字符串并去除首尾空格
if not v:
return "" # 空值直接返回空字符串
s = str(v).strip() # 转字符串并去首尾空白
if s.startswith("'"):
s = s[1:] # 去掉 Excel 强制文本时产生的前导单引号(如以 + 开头的单元格)
return s
def load_excel_indexes(path):
# 读取 Excel 文件,构建两个查找字典
# 返回:
# map_21: {21世纪资料名 → 暗码}
# map_xkw: {学科网资料名 → [暗码, ...]} (同名可能对应多个暗码,全部保留)
wb = openpyxl.load_workbook(path) # 打开 Excel 文件
ws = wb.active # 取活动工作表
headers = [cell.value for cell in ws[1]] # 读取第一行作为列名
col = {h: i for i, h in enumerate(headers)} # 构建列名到列索引的映射
idx_21 = col["21世纪资料名"] # "21世纪资料名" 列的索引
idx_xkw = col["学科网资料名"] # "学科网资料名" 列的索引
idx_code = col["暗码"] # "暗码" 列的索引
map_21 = {} # 21世纪资料名 → 暗码(唯一映射,重复则后者覆盖前者)
map_xkw = {} # 学科网资料名 → [暗码列表](同名保留全部暗码)
for row in ws.iter_rows(min_row=2, values_only=True): # 从第2行开始逐行读取
code = clean(row[idx_code]) # 读取并清洗暗码
name_21 = clean(row[idx_21]) # 读取并清洗 21世纪资料名
name_xkw = clean(row[idx_xkw]) # 读取并清洗学科网资料名
if name_21 and code:
map_21[name_21] = code # 写入 21世纪索引
if name_xkw and code:
map_xkw.setdefault(name_xkw, []).append(code) # 追加到学科网索引列表
return map_21, map_xkw # 返回两个索引字典
def extract_url_id(url):
# 从学科网 URL 中提取纯数字资源 ID
# 例如 https://www.zxxk.com/soft/38079968.html → '38079968'
m = re.search(r'/(\d+)(?:\.\w+)?(?:\?|$)', url) # 匹配路径最后一段的纯数字
return m.group(1) if m else None # 有匹配则返回数字字符串,否则返回 None
def resolve_xkw_code(fn0, url, map_xkw):
# 学科网资料名匹配逻辑:
# - 无匹配 → 返回 None
# - 只有一个唯一暗码 → 直接返回
# - 多个不同暗码(同名不同资源)→ 用 URL ID 消歧,找到匹配则返回,否则返回 None
codes = map_xkw.get(fn0) # 查找该文件名对应的暗码列表
if not codes:
return None # 在学科网索引中找不到该文件名
unique_codes = list(dict.fromkeys(codes)) # 去重并保持原顺序
if len(unique_codes) == 1:
return unique_codes[0] # 唯一暗码,直接返回
url_id = extract_url_id(url) # 从 URL 中提取数字 ID
if url_id and url_id in codes:
return url_id # URL ID 与某个暗码一致,用该值
return None # 无法消歧,返回 None
def process_file(json_path, map_21, map_xkw):
# 处理单个 JSON 文件:为每个 item 写入 xkw_code
with open(json_path, encoding="utf-8") as f:
data = json.load(f) # 读取 JSON 数据
matched = 0 # 成功匹配的计数
unmatched_items = [] # 未匹配的 item 列表,用于汇总输出
for item in data.get("items", []): # 遍历每个资源 item
fns = item.get("filename", []) # 读取 filename 列表
if not fns:
continue # filename 为空则跳过该 item
fn0 = fns[0].strip() # 只取第一个 filename 并去空格
url = item.get("url", "") # 读取该 item 的 URL
code = None # 初始化暗码为空
if "www.21cnjy.com" in url:
code = map_21.get(fn0) # 在 21世纪索引中精确查找
elif "www.zxxk.com" in url:
code = resolve_xkw_code(fn0, url, map_xkw) # 在学科网索引中查找(含消歧)
if code:
item["xkw_code"] = code # 匹配成功,写入暗码
matched += 1 # 匹配计数加一
else:
item["xkw_code"] = "0" # 未匹配,填充 "0"
unmatched_items.append({
"source_file": json_path, # 来源 JSON 文件路径
"filename": fn0, # 未匹配的文件名
"url": url, # 对应的 URL
})
with open(json_path, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2) # 将修改后的数据写回 JSON 文件
return matched, unmatched_items # 返回匹配数和未匹配列表
def main():
print("加载 Excel 索引...")
map_21, map_xkw = load_excel_indexes(EXCEL_PATH) # 构建 Excel 查找索引
print(f"Excel 索引21世纪 {len(map_21)} 条,学科网 {len(map_xkw)} 个不重复名称\n")
files = sorted(glob.glob(f"{JSON_DIR}/*.json")) # 获取所有 JSON 文件路径并排序
total_files = len(files) # JSON 文件总数
total_matched = 0 # 全局匹配计数
all_unmatched = [] # 全局未匹配列表
for i, path in enumerate(files, 1): # 逐个处理每个 JSON 文件
matched, unmatched_items = process_file(path, map_21, map_xkw) # 处理单文件
total_matched += matched # 累加匹配数
all_unmatched.extend(unmatched_items) # 合并未匹配列表
print(f"[{i}/{total_files}] {path} 匹配:{matched} 未匹配:{len(unmatched_items)}")
with open(UNMATCHED_OUT, "w", encoding="utf-8") as f:
json.dump(all_unmatched, f, ensure_ascii=False, indent=2) # 写出所有未匹配数据
total_unmatched = len(all_unmatched) # 全局未匹配总数
print(f"\n完成:处理 {total_files} 个文件,写入 xkw_code {total_matched + total_unmatched}"
f"(匹配:{total_matched}填0:{total_unmatched}")
print(f"未匹配数据已保存至 {UNMATCHED_OUT}") # 提示未匹配数据保存路径
if __name__ == "__main__":
main() # 脚本直接运行时执行 main 函数