如何获取维基百科分类的父分类?含API与Dump解析场景
解决方案:获取维基百科分类的父分类
我来帮你搞定维基百科分类父分类提取的问题,针对你提到的两个业务场景,分别给出实用的解决方案,同时优化你用wptools的代码,解决父分类提取的难点:
场景1:通过维基百科API根据文章ID提取父分类ID
这个场景可以分两步走:先通过文章ID拿到所属的所有分类,再逐个查询每个分类的父分类。这里用requests直接调用维基百科API,比wptools更灵活可控:
import requests import time def get_page_categories(page_id): """根据文章ID获取该文章的所有分类(不含前缀)""" api_url = "https://en.wikipedia.org/w/api.php" params = { "action": "query", "pageids": page_id, "prop": "categories", "format": "json", "cllimit": "max" # 一次性获取所有分类 } response = requests.get(api_url, params=params) data = response.json() categories = [] for page_data in data["query"]["pages"].values(): if "categories" in page_data: # 去掉"Category:"前缀,同时过滤隐藏分类(可选) categories = [ cat["title"].replace("Category:", "") for cat in page_data["categories"] if not cat.get("hidden") ] return categories def get_category_parent_ids(category_name): """获取单个分类的父分类ID(以及父分类名称)""" api_url = "https://en.wikipedia.org/w/api.php" params = { "action": "query", "titles": f"Category:{category_name}", "prop": "categories", "format": "json", "cllimit": "max", "clshow": "!hidden" # 排除隐藏分类 } response = requests.get(api_url, params=params) data = response.json() parent_info = [] for page_data in data["query"]["pages"].values(): if "categories" in page_data: for cat in page_data["categories"]: parent_name = cat["title"].replace("Category:", "") parent_info.append({ "parent_id": cat["pageid"], "parent_name": parent_name }) # 去重 return list({item['parent_id']: item for item in parent_info}.values()) # 组合使用:根据文章ID获取所有分类的父分类ID def get_page_category_parents(page_id): categories = get_page_categories(page_id) all_parent_ids = {} for cat in categories: all_parent_ids[cat] = get_category_parent_ids(cat) time.sleep(1) # 避免触发API频率限制 return all_parent_ids
场景2:解析维基百科Dump后获取细分类的父分类
如果你已经解析了Dump拿到文章对象(比如{'Title':'The Dark Knight (film)', 'id':4276475, 'Categories':['2008 films','English-language films','2000s action thriller films',...]}),核心需求是给这些细分类找父分类,比如从"2000s action thriller films"得到"action thriller films"。
方案1:API批量查询
可以直接复用场景1中的get_category_parent_ids函数,遍历文章的分类列表即可。但如果处理大量文章,频繁调用API会受限,建议:
- 批量收集所有需要查询的分类,去重后再批量请求(维基百科API支持多标题查询,用
titles=Category:A|Category:B的格式) - 添加请求间隔,避免被封IP
方案2:本地数据库查询(高效大规模处理)
如果数据量很大,推荐使用维基百科的分类链接Dump文件(比如categorylinks.sql.gz),导入本地MySQL/PostgreSQL数据库后直接查询,效率提升N倍:
- 下载对应版本的
categorylinks.sql.gz和page.sql.gz(前者存分类关联,后者存页面ID和标题映射) - 导入数据库后,用SQL查询直接父分类:
-- 先获取细分类的页面ID SELECT page_id FROM page WHERE page_title = '2000s_action_thriller_films' AND page_namespace = 14; -- 14是分类的命名空间 -- 再根据页面ID查父分类 SELECT cl_to, (SELECT page_id FROM page WHERE page_title = cl_to AND page_namespace =14) as parent_id FROM categorylinks WHERE cl_from = [上面拿到的页面ID] AND cl_type = 'subcat'; -- 只取子分类关联,排除页面关联
优化wptools的使用,解决父分类提取难点
你之前的wptools代码只获取了分类页面,但没提取父分类。这里优化一下,直接从分类页面的返回数据中提取父分类,还支持递归获取所有上层父分类:
import wptools import time def get_direct_parent_categories(category_name, skip_hidden=True): """用wptools获取分类的直接父分类""" # 初始化分类页面,silent=True关闭日志输出 cat_page = wptools.page(f"Category:{category_name}", silent=True) cat_page.get_more() parent_cats = [] for cat in cat_page.data.get('categories', []): # 处理隐藏分类的标记 if "(hidden)" in cat and skip_hidden: continue clean_name = cat.replace("Category:", "").replace(" (hidden)", "") parent_cats.append(clean_name) return list(set(parent_cats)) def get_all_parent_categories(category_name, skip_hidden=True, visited=None): """递归获取分类的所有上层父分类(直到顶级分类)""" if visited is None: visited = set() # 避免循环引用(比如某些分类互相引用) if category_name in visited: return [] visited.add(category_name) direct_parents = get_direct_parent_categories(category_name, skip_hidden) all_parents = direct_parents.copy() # 递归获取每个父分类的上层分类 for parent in direct_parents: all_parents.extend(get_all_parent_categories(parent, skip_hidden, visited)) time.sleep(0.5) # 控制请求频率 return list(set(all_parents)) # 示例:获取"2000s action thriller films"的所有父分类 all_parents = get_all_parent_categories("2000s action thriller films") print(all_parents) # 输出会包含"action thriller films"、"2000s thriller films"等上层分类
关键注意事项
- API频率限制:维基百科API对非认证用户有请求频率限制,建议添加
time.sleep(),或者注册账号使用OAuth认证提升限额 - 隐藏分类过滤:大部分业务场景不需要隐藏分类(比如维护类、删除类分类),记得过滤
- 循环引用处理:少数分类可能存在循环引用,递归时一定要加
visited集合避免无限递归
内容的提问来源于stack exchange,提问作者DsCpp
相关产品推荐
相关产品推荐

