--- title: "01-main.py" created: 2026-04-03 tags: - 项目 aliases: - main.py --- # main.py ### `main.py` — 入口与调度中心 通过 `-m` 参数控制运行模式,主要流程如下: | 参数值 | 功能 | | --- | --- | | `p` | 爬取学校网站数据 | | `ann` | 解析爬取结果,调用大模型处理公告 | | `ann_wx` | 处理微信公众号来源的公告 | | `ann_api` | 上传处理结果到云端 | | `cp` / `cp_full` | 爬取大公司招聘页面 | | `cjob` | 解析公司职位数据 | | `wxhand` | 处理手工整理的微信文章 | | `all_ann` / `all_job` | 循环自动执行完整流水线 | **特点:** 支持断点续跑(`-s` 指定单文件)、代理(`-p`)、分页起始页(`-t`)、单独处理某公司(`-c`)。 ## 代码 ```python # -*- coding: utf-8 -*- """ 学校/企业公告数据爬虫主程序 功能:使用Playwright自动化爬取学校、企业公告数据 → 本地处理 → 上传云端API 支持:定时循环、单文件处理、单企业处理、代理、重试、分页控制 """ # 导入标准库:时间处理、系统操作、命令行参数解析 import time import os import argparse # 导入Playwright自动化工具(用于模拟浏览器爬取网页) from playwright.sync_api import sync_playwright # 导入自定义爬虫模块 from spider_sch import SpiderSch # 学校爬虫类 from spider_com import SpiderCom # 企业爬虫类 from spider_data import DEFAULT_WX_SCHOOL, DEFAULT_PCOUNT # 常量:微信特殊学校、默认处理条数 from spider_data import SpiderData # 数据处理/上传类 # 导入工具模块:日志、浏览器工具 from utils import set_logger_debug, ner_logger from utils_playwright import get_browser # ====================== 命令行参数配置 ====================== # 创建命令行参数解析器,用于接收外部运行指令 parser = argparse.ArgumentParser(description='学校/企业公告数据爬取主程序') # 运行模式(核心参数) parser.add_argument('-m', '--method', default="p", type=str, help='启动方式:p=爬学校,cp=爬企业,ann=处理公告等') # 配置文件编号 parser.add_argument('-f', '--file', default="99", type=str, help='使用的配置文件编号,对应不同爬取任务') # 版本说明 parser.add_argument('-v', '--version', default="", type=str, help='版本说明') # 云端API环境(dev/prod) parser.add_argument('-d', '--dist', default="dev", type=str, help='云端API接口环境:dev=测试,prod=正式') # 指定只处理单个文件(用于断点续爬/修复) parser.add_argument('-s', '--processfile', default="", type=str, help='指定需要处理的单个文件ID') # 指定只处理单个公司 parser.add_argument('-c', '--company', default="", type=str, help='指定单独处理的公司名称/标识') # 代理设置 parser.add_argument('-p', '--proxy', default="", type=str, help='浏览器代理地址,为空则不使用') # 重试开关 parser.add_argument('-r', '--retry', default="1", type=str, help='是否允许重试:1=允许,0=不允许') # 翻页起始页码 parser.add_argument('-t', '--pagestart', default="2", type=str, help='翻页开始页数,默认从第2页开始') # 解析命令行传入的所有参数 args = parser.parse_args() # ====================== 核心爬取函数 ====================== def clawler_main(s, _stat={}): """ 爬虫主入口:启动浏览器 → 遍历学校/企业节点 → 执行爬取 :param s: 爬虫实例(SpiderSch / SpiderCom) :param _stat: 运行状态字典(记录配置、进度、过滤条件) """ # 从爬虫实例中获取浏览器可执行文件路径 executable_path = s.get_browser_path() # 启动Playwright上下文(管理浏览器生命周期) with sync_playwright() as p: # 创建浏览器实例(自动处理代理、无头模式、防检测) browser = get_browser(p, executable_path, args.proxy) # 将浏览器对象绑定到爬虫实例,供内部方法使用 s.browser = browser # 新建浏览器标签页 page = browser.new_page() # ===================== 爬取核心逻辑 ===================== # 获取所有需要爬取的学校/企业节点(配置文件中定义) nodes = s.get_nodes() # 获取已爬取进度(用于断点续爬) process = s.get_progress() # 遍历所有节点(key=学校/企业ID,node=详情配置) for _key, _node in nodes.items(): # 过滤:跳过微信专用特殊学校(单独处理) if _key == DEFAULT_WX_SCHOOL: continue # 过滤:如果指定了单独处理公司,则只爬该公司 if 'company' in _stat and _stat['company'] != _key: continue # 遍历该节点下的所有学校/企业信息 for _sch_info in _node: # 如果该节点在进度记录中 → 开始执行爬取 if _key in process: print(f"开始爬取 {_key}") # 调用实例内部的run方法执行具体爬取逻辑 s.run(page, _key, _sch_info, _stat) # 爬取完成,等待10秒后关闭浏览器 time.sleep(10) browser.close() # ====================== 数据处理函数 ====================== def process_main_wx(s, d, _stat={}): """ 处理微信渠道数据(单独处理) :param s: 学校爬虫实例 :param d: 数据处理实例 :param _stat: 状态参数 """ nodes = s.get_nodes() for _key, _node in nodes.items(): # 跳过微信特殊学校 if _key == DEFAULT_WX_SCHOOL: continue # 遍历并处理公告数据(渠道标记为wx) for _sch_info in _node: d.process_announcement_data(_key, _sch_info, _stat, "wx") def process_main_announcement(s, d, _stat={}, proc_type="ann", _uapi="up_api"): """ 处理爬取到的公告数据(清洗、解析、入库) :param proc_type: 处理类型 ann=普通公告 cjob=企业职位 :param _uapi: 上传API类型 """ nodes = s.get_nodes() for _key, _node in nodes.items(): if _key == DEFAULT_WX_SCHOOL: continue for _sch_info in _node: # 执行数据解析处理 d.process_announcement_data(_key, _sch_info, _stat, proc_type) # 达到默认批量处理条数 → 自动触发上传 if 'total' in _stat and _stat['total'] >= DEFAULT_PCOUNT: ner_logger.info(f"{_stat['total']}条数据已处理完成") # 调用上传接口 d.process_announcement_data(_key, _sch_info, _stat, _uapi) ner_logger.info(f"{_stat['total']}条数据已上传完成") # 重置计数器 _stat['total'] = 0 def process_main_wxhand(s, d, _stat={}): """ 处理【微信手工录入】的文章数据(专用渠道) """ # 预先初始化微信文章数据 d.pre_wx_article() nodes = s.get_nodes() for _key, _node in nodes.items(): # 只处理微信特殊学校 if _key != DEFAULT_WX_SCHOOL: continue ner_logger.info(f"开始处理特殊学校数据:{_key}") for _sch_info in _node: d.process_announcement_data(_key, _sch_info, _stat, "wx") def process_main_upapi(s, d, _stat={}, _uapi="up_api"): """ 统一上传数据到云端API """ nodes = s.get_nodes() for _key, _node in nodes.items(): if _key == DEFAULT_WX_SCHOOL: continue for _sch_info in _node: d.process_announcement_data(_key, _sch_info, _stat, _uapi) def process_main_wxhand_api(s, d, _stat={}): """ 上传微信手工数据到云端API """ nodes = s.get_nodes() for _key, _node in nodes.items(): # 只处理微信特殊学校 if _key != DEFAULT_WX_SCHOOL: continue ner_logger.info(f"开始处理特殊学校数据上传:{_key} {DEFAULT_WX_SCHOOL}") for _sch_info in _node: d.process_announcement_data(_key, _sch_info, _stat, "up_api") # ====================== 主调度函数(根据-m参数执行对应流程) ====================== def run_periodically(s, cs, d): """ 周期运行调度器:根据命令行参数 -m 执行对应任务 :param s: 学校爬虫实例 :param cs: 企业爬虫实例 :param d: 数据处理实例 """ # 初始化状态字典:存储运行配置、过滤条件、计数器 _stat = {} # ===================== 读取命令行参数到状态字典 ===================== # 单文件处理 if args.processfile != "": _stat['pfile'] = args.processfile # 单企业处理 if args.company != "": _stat['company'] = args.company # 起始页码 if args.pagestart != "": _stat['page_start'] = int(args.pagestart) else: _stat['page_start'] = 2 # 云端环境 _stat['dist'] = args.dist # 是否重试 _stat['retry'] = args.retry # 已处理公告计数 _stat['p_count'] = 0 # 已处理列表(去重) _stat['all_proc_list'] = [] # 打印模式:仅输出所有学校列表,不爬取 if args.method in ["o"]: s.print_all_sch() # ===================== 企业爬虫流程 ===================== # 1. 爬取企业原始数据 if args.method in ["cp", 'cp_full']: _stat['method'] = args.method clawler_main(cs, _stat) time.sleep(1 * 6) ner_logger.info("第一步:爬取企业数据完成,休息后继续处理") # 2. 处理企业职位数据 if args.method in ["cjob"]: process_main_announcement(cs, d, _stat, "cjob", "up_api_cjob") time.sleep(1 * 6) ner_logger.info("第二步:处理职位数据完成") # 3. 上传企业数据到云 if args.method in ["cjob_api"]: process_main_upapi(cs, d, _stat, "up_api_cjob") time.sleep(1 * 6) ner_logger.info("第四步:企业数据上传完成") # 循环执行企业爬取+处理+上传 if args.method in ["all_job"]: ner_logger.info("启动企业公告循环处理任务") _loop_times = 0 while True: _all_total = 0 _stat['total'] = 0 # 处理数据 process_main_announcement(cs, d, _stat, "cjob", "up_api_cjob") _all_total += _stat['total'] time.sleep(6) # 上传数据 _stat['total'] = 0 process_main_upapi(cs, d, _stat, "up_api_cjob") _all_total += _stat['total'] time.sleep(6) # 长期无数据则退出循环 if _all_total == 0 and _loop_times > 200: ner_logger.info("无新数据,结束循环") break _loop_times += 1 # ===================== 学校爬虫流程 ===================== # 1. 爬取学校数据 if args.method in ["p"]: clawler_main(s, _stat) time.sleep(6) ner_logger.info("第一步:学校数据爬取完成") # 循环爬取学校(每8小时一次) if args.method in ["all_p"]: ner_logger.info("启动学校循环爬取任务(8小时/次)") clawler_main(s, _stat) time.sleep(60 * 60 * 8) ner_logger.info("循环爬取完成,休息8小时") # ===================== 学校数据处理流程 ===================== # 2. 处理学校公告 if args.method in ["ann"]: process_main_announcement(s, d, _stat) time.sleep(6) ner_logger.info("第二步:公告处理完成") # 3. 处理学校微信数据 if args.method in ["ann_wx"]: process_main_wx(s, d, _stat) time.sleep(6) ner_logger.info("第三步:微信数据处理完成") # 4. 上传学校数据到云 if args.method in ["ann_api"]: process_main_upapi(s, d, _stat) time.sleep(6) ner_logger.info("第四步:数据上传完成") # 循环处理学校公告+微信+上传 if args.method in ["all_ann"]: ner_logger.info("启动学校公告循环处理任务") _loop_times = 0 while True: _loop_times += 1 _all_total = 0 _stat['total'] = 0 # 上传 process_main_upapi(s, d, _stat) _all_total += _stat['total'] time.sleep(6) # 处理公告 _stat['total'] = 0 process_main_announcement(s, d, _stat) _all_total += _stat['total'] time.sleep(6) # 处理微信 _stat['total'] = 0 process_main_wx(s, d, _stat) _all_total += _stat['total'] time.sleep(6) # 无数据则退出 if _all_total == 0 and _loop_times > 20: ner_logger.info("无新数据,结束循环") break # ===================== 微信手工数据流程 ===================== # 处理微信手工数据 if args.method in ["wxhand"]: process_main_wxhand(s, d, _stat) ner_logger.info("第一步:微信手工数据处理完成") # 上传微信手工数据 if args.method in ["wxhand_api"]: process_main_wxhand_api(s, d, _stat) ner_logger.info("第二步:微信手工数据上传完成") # 循环处理微信手工数据 if args.method in ["all_wxhand"]: ner_logger.info("启动微信循环处理任务") while True: _stat['total'] = 0 process_main_wxhand_api(s, d, _stat) time.sleep(6) _stat['total'] = 0 process_main_wxhand(s, d, _stat) time.sleep(6) _stat['total'] = 0 process_main_wxhand_api(s, d, _stat) time.sleep(6) # ====================== 程序入口 ====================== if __name__ == '__main__': # 初始化日志系统 set_logger_debug(args.file) # 初始化三大核心实例 s = SpiderSch(args.file) # 学校爬虫 cs = SpiderCom(args.file) # 企业爬虫 d = SpiderData(s) # 数据处理/上传 # 如果输入版本参数 → 打印帮助说明 if args.version != '': print("===== 使用说明 =====") print("-m / --method : 运行模式") print(" p = 爬取学校数据") print(" cp = 爬取企业数据") print(" ann = 处理学校公告") print(" cjob = 处理企业职位") print(" ann_api= 上传学校数据") print(" wxhand = 处理微信手工数据") print("-f / --file : 配置文件编号") exit() # 启动调度器,开始执行任务 run_periodically(s, cs, d) ``` --- **项目分区导航**:[[02-全链路流程梳理|全链路流程梳理]] ⬅️ | 01-main.py | ➡️ [[02-spider_com.py|spider_com.py]]