guokao_helper.py•20.1 kB
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
2025年国考小助手 - 命令行工具
功能:
1. 多条件智能筛选
2. 专业匹配查询
3. 个人条件一键匹配
4. 筛选结果导出(JSON格式)
"""
import pandas as pd
import json
import os
import re
from datetime import datetime
class GuokaoHelper:
def __init__(self, excel_path):
"""初始化,加载数据"""
self.excel_path = excel_path
self.df = self._load_data()
print(f"✅ 数据加载成功!共 {len(self.df)} 个岗位,招考 {int(self.df['招考人数'].sum())} 人")
def _load_data(self):
"""加载Excel数据"""
all_dfs = []
xls = pd.ExcelFile(self.excel_path)
for sheet_name in xls.sheet_names:
df = pd.read_excel(xls, sheet_name=sheet_name)
df['机关类别'] = sheet_name
all_dfs.append(df)
return pd.concat(all_dfs, ignore_index=True)
def filter_positions(self, **kwargs):
"""
功能1: 多条件智能筛选
支持的筛选条件:
- 机关类别: 机关类别(精确匹配)
- 部门名称: 部门名称(模糊匹配)
- 工作地点: 工作地点(模糊匹配)
- 学历: 学历要求(模糊匹配)
- 政治面貌: 政治面貌(精确匹配或"不限")
- 基层工作最低年限: 基层工作年限要求
- 专业: 专业(模糊匹配)
- 考试类别: 考试类别(模糊匹配)
- 职位属性: 职位属性(模糊匹配)
- 招考人数_min: 最小招考人数
- 招考人数_max: 最大招考人数
"""
result = self.df.copy()
# 机关类别 - 精确匹配
if kwargs.get('机关类别'):
result = result[result['机关类别'] == kwargs['机关类别']]
# 部门名称 - 模糊匹配
if kwargs.get('部门名称'):
result = result[result['部门名称'].str.contains(kwargs['部门名称'], na=False)]
# 工作地点 - 模糊匹配
if kwargs.get('工作地点'):
result = result[result['工作地点'].str.contains(kwargs['工作地点'], na=False)]
# 学历 - 模糊匹配
if kwargs.get('学历'):
result = result[result['学历'].str.contains(kwargs['学历'], na=False)]
# 政治面貌 - 精确匹配或不限
if kwargs.get('政治面貌'):
if kwargs['政治面貌'] == '不限':
result = result[result['政治面貌'] == '不限']
else:
# 匹配指定的政治面貌或不限
result = result[
(result['政治面貌'] == kwargs['政治面貌']) |
(result['政治面貌'] == '不限') |
(result['政治面貌'].str.contains(kwargs['政治面貌'], na=False))
]
# 基层工作最低年限
if kwargs.get('基层工作最低年限'):
if kwargs['基层工作最低年限'] == '无限制':
result = result[result['基层工作最低年限'] == '无限制']
else:
result = result[result['基层工作最低年限'].str.contains(kwargs['基层工作最低年限'], na=False)]
# 专业 - 模糊匹配
if kwargs.get('专业'):
result = result[result['专业'].str.contains(kwargs['专业'], na=False)]
# 考试类别 - 模糊匹配
if kwargs.get('考试类别'):
result = result[result['考试类别'].str.contains(kwargs['考试类别'], na=False)]
# 职位属性 - 模糊匹配
if kwargs.get('职位属性'):
result = result[result['职位属性'].str.contains(kwargs['职位属性'], na=False)]
# 招考人数范围
if kwargs.get('招考人数_min'):
result = result[result['招考人数'] >= kwargs['招考人数_min']]
if kwargs.get('招考人数_max'):
result = result[result['招考人数'] <= kwargs['招考人数_max']]
return result
def match_by_major(self, major_keyword):
"""
功能2: 专业匹配查询
输入专业关键词,返回所有匹配的岗位
"""
result = self.df[self.df['专业'].str.contains(major_keyword, na=False, regex=False)]
return result
def personal_match(self, education=None, major=None, political_status=None,
work_years=None, location=None, base_project=None):
"""
功能9: 个人条件一键匹配
根据个人条件筛选可报考的岗位
参数:
- education: 学历(本科/硕士研究生/博士研究生)
- major: 专业关键词
- political_status: 政治面貌(中共党员/共青团员/群众)
- work_years: 基层工作年限(0/1/2/3/5)
- location: 期望工作地点
- base_project: 是否有服务基层项目经历(True/False)
"""
result = self.df.copy()
# 学历匹配
if education:
education_map = {
'大专': ['大专', '大专及以上', '大专或本科'],
'本科': ['本科', '本科及以上', '本科或硕士研究生', '大专或本科', '大专及以上', '仅限本科'],
'硕士研究生': ['硕士研究生', '硕士研究生及以上', '本科及以上', '本科或硕士研究生', '仅限硕士研究生'],
'博士研究生': ['博士研究生', '硕士研究生及以上', '本科及以上', '仅限博士研究生']
}
if education in education_map:
allowed_edu = education_map[education]
result = result[result['学历'].isin(allowed_edu)]
# 专业匹配
if major:
result = result[result['专业'].str.contains(major, na=False, regex=False)]
# 政治面貌匹配
if political_status:
if political_status == '中共党员':
# 党员可以报考所有岗位
pass
elif political_status == '共青团员':
# 团员可以报考"不限"和"中共党员或共青团员"的岗位
result = result[
(result['政治面貌'] == '不限') |
(result['政治面貌'].str.contains('共青团员', na=False))
]
else: # 群众
result = result[result['政治面貌'] == '不限']
# 基层工作年限匹配
if work_years is not None:
year_map = {
0: ['无限制'],
1: ['无限制', '一年'],
2: ['无限制', '一年', '二年'],
3: ['无限制', '一年', '二年', '三年'],
5: ['无限制', '一年', '二年', '三年', '五年以上']
}
if work_years in year_map:
allowed_years = year_map[work_years]
result = result[result['基层工作最低年限'].isin(allowed_years)]
# 工作地点匹配
if location:
result = result[result['工作地点'].str.contains(location, na=False)]
# 服务基层项目经历
if base_project is False:
# 没有基层项目经历,只能报考"无限制"的岗位
result = result[result['服务基层项目工作经历'] == '无限制']
return result
def export_to_json(self, df, filename=None):
"""
功能15: 导出筛选结果为JSON
"""
if filename is None:
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
filename = f'guokao_result_{timestamp}.json'
# 转换为字典列表
records = df.to_dict(orient='records')
# 处理NaN值
for record in records:
for key, value in record.items():
if pd.isna(value):
record[key] = None
# 保存为JSON
output = {
'total_positions': len(records),
'total_recruitment': int(df['招考人数'].sum()) if len(df) > 0 else 0,
'export_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
'positions': records
}
with open(filename, 'w', encoding='utf-8') as f:
json.dump(output, f, ensure_ascii=False, indent=2)
print(f"✅ 已导出 {len(records)} 条记录到 {filename}")
return filename
def display_results(self, df, max_rows=10):
"""显示筛选结果摘要"""
print(f"\n{'='*60}")
print(f"📊 筛选结果:共 {len(df)} 个岗位,招考 {int(df['招考人数'].sum())} 人")
print(f"{'='*60}")
if len(df) == 0:
print("未找到符合条件的岗位")
return
# 显示前几条
display_cols = ['部门名称', '招考职位', '工作地点', '学历', '招考人数', '专业']
print(f"\n前 {min(max_rows, len(df))} 条结果:")
print("-" * 60)
for i, (_, row) in enumerate(df.head(max_rows).iterrows()):
print(f"\n【{i+1}】{row['部门名称']} - {row['招考职位']}")
print(f" 📍 工作地点: {row['工作地点']}")
print(f" 🎓 学历要求: {row['学历']}")
print(f" 👥 招考人数: {row['招考人数']}")
# 专业信息太长,截取显示
major = str(row['专业'])[:80] + '...' if len(str(row['专业'])) > 80 else row['专业']
print(f" 📚 专业要求: {major}")
if len(df) > max_rows:
print(f"\n... 还有 {len(df) - max_rows} 条结果,请导出查看完整列表")
def interactive_menu():
"""交互式菜单"""
excel_path = r'G:\Desktop\guokao_mcp\2025年国考岗位表.xls'
if not os.path.exists(excel_path):
print(f"❌ 找不到数据文件: {excel_path}")
return
helper = GuokaoHelper(excel_path)
while True:
print("\n" + "=" * 60)
print("🎯 2025年国考小助手")
print("=" * 60)
print("1. 多条件智能筛选")
print("2. 专业匹配查询")
print("3. 个人条件一键匹配")
print("4. 退出")
print("-" * 60)
choice = input("请选择功能 (1-4): ").strip()
if choice == '1':
# 多条件智能筛选
print("\n📋 多条件智能筛选(直接回车跳过该条件)")
print("-" * 40)
filters = {}
# 机关类别
print("\n机关类别选项:")
print(" 1. 中央党群机关")
print(" 2. 中央国家行政机关(本级)")
print(" 3. 中央国家行政机关省级以下直属机构")
print(" 4. 中央国家行政机关参照公务员法管理事业单位")
organ_choice = input("请选择机关类别 (1-4, 回车跳过): ").strip()
organ_map = {
'1': '中央党群机关',
'2': '中央国家行政机关(本级)',
'3': '中央国家行政机关省级以下直属机构',
'4': '中央国家行政机关参照公务员法管理事业单位'
}
if organ_choice in organ_map:
filters['机关类别'] = organ_map[organ_choice]
# 其他条件
dept = input("部门名称关键词 (如 税务): ").strip()
if dept:
filters['部门名称'] = dept
location = input("工作地点 (如 北京/广东): ").strip()
if location:
filters['工作地点'] = location
print("\n学历选项:本科及以上/仅限本科/硕士研究生及以上/仅限硕士研究生/仅限博士研究生")
edu = input("学历要求: ").strip()
if edu:
filters['学历'] = edu
print("\n政治面貌选项:不限/中共党员/中共党员或共青团员")
political = input("政治面貌: ").strip()
if political:
filters['政治面貌'] = political
print("\n基层工作年限选项:无限制/一年/二年/三年/五年以上")
years = input("基层工作最低年限: ").strip()
if years:
filters['基层工作最低年限'] = years
major = input("专业关键词 (如 计算机/法学/会计): ").strip()
if major:
filters['专业'] = major
# 执行筛选
result = helper.filter_positions(**filters)
helper.display_results(result)
# 询问是否导出
if len(result) > 0:
export = input("\n是否导出结果为JSON? (y/n): ").strip().lower()
if export == 'y':
filename = input("输入文件名 (回车使用默认): ").strip()
if filename:
if not filename.endswith('.json'):
filename += '.json'
helper.export_to_json(result, filename)
else:
helper.export_to_json(result)
elif choice == '2':
# 专业匹配查询
print("\n📚 专业匹配查询")
print("-" * 40)
major = input("请输入您的专业关键词 (如 计算机/软件/法学/会计/金融): ").strip()
if major:
result = helper.match_by_major(major)
helper.display_results(result)
if len(result) > 0:
export = input("\n是否导出结果为JSON? (y/n): ").strip().lower()
if export == 'y':
filename = input("输入文件名 (回车使用默认): ").strip()
if filename:
if not filename.endswith('.json'):
filename += '.json'
helper.export_to_json(result, filename)
else:
helper.export_to_json(result)
elif choice == '3':
# 个人条件一键匹配
print("\n👤 个人条件一键匹配")
print("-" * 40)
print("\n学历选项:大专/本科/硕士研究生/博士研究生")
education = input("您的学历: ").strip()
if not education:
education = None
major = input("您的专业关键词: ").strip()
if not major:
major = None
print("\n政治面貌选项:中共党员/共青团员/群众")
political = input("您的政治面貌: ").strip()
if not political:
political = None
years_input = input("您的基层工作年限 (0/1/2/3/5): ").strip()
try:
work_years = int(years_input) if years_input else None
except ValueError:
work_years = None
location = input("期望工作地点 (如 北京/上海): ").strip()
if not location:
location = None
base_input = input("是否有服务基层项目经历 (y/n, 回车跳过): ").strip().lower()
if base_input == 'y':
base_project = True
elif base_input == 'n':
base_project = False
else:
base_project = None
# 执行匹配
result = helper.personal_match(
education=education,
major=major,
political_status=political,
work_years=work_years,
location=location,
base_project=base_project
)
helper.display_results(result)
if len(result) > 0:
export = input("\n是否导出结果为JSON? (y/n): ").strip().lower()
if export == 'y':
filename = input("输入文件名 (回车使用默认): ").strip()
if filename:
if not filename.endswith('.json'):
filename += '.json'
helper.export_to_json(result, filename)
else:
helper.export_to_json(result)
elif choice == '4':
print("\n👋 再见!祝您考试顺利!")
break
else:
print("❌ 无效选择,请重新输入")
def main():
"""主函数 - 支持命令行参数"""
import argparse
parser = argparse.ArgumentParser(description='2025年国考小助手')
parser.add_argument('--mode', '-m', choices=['interactive', 'filter', 'major', 'personal'],
default='interactive', help='运行模式')
parser.add_argument('--output', '-o', help='输出JSON文件名')
# 筛选条件参数
parser.add_argument('--organ', help='机关类别')
parser.add_argument('--dept', help='部门名称关键词')
parser.add_argument('--location', help='工作地点')
parser.add_argument('--education', help='学历要求')
parser.add_argument('--political', help='政治面貌')
parser.add_argument('--years', help='基层工作最低年限')
parser.add_argument('--major', help='专业关键词')
args = parser.parse_args()
if args.mode == 'interactive':
interactive_menu()
else:
excel_path = r'G:\Desktop\guokao_mcp\2025年国考岗位表.xls'
helper = GuokaoHelper(excel_path)
if args.mode == 'filter':
filters = {}
if args.organ:
filters['机关类别'] = args.organ
if args.dept:
filters['部门名称'] = args.dept
if args.location:
filters['工作地点'] = args.location
if args.education:
filters['学历'] = args.education
if args.political:
filters['政治面貌'] = args.political
if args.years:
filters['基层工作最低年限'] = args.years
if args.major:
filters['专业'] = args.major
result = helper.filter_positions(**filters)
helper.display_results(result, max_rows=20)
if args.output:
helper.export_to_json(result, args.output)
elif args.mode == 'major':
if args.major:
result = helper.match_by_major(args.major)
helper.display_results(result, max_rows=20)
if args.output:
helper.export_to_json(result, args.output)
else:
print("❌ 请提供 --major 参数")
elif args.mode == 'personal':
work_years = int(args.years) if args.years else None
result = helper.personal_match(
education=args.education,
major=args.major,
political_status=args.political,
work_years=work_years,
location=args.location
)
helper.display_results(result, max_rows=20)
if args.output:
helper.export_to_json(result, args.output)
if __name__ == '__main__':
main()