
1. 项目背景与核心需求最近在帮朋友做一个招聘行业的数据分析项目需要持续跟踪各大招聘会的参会企业信息。手动收集这些数据不仅耗时耗力而且很难保证数据的完整性和时效性。于是决定开发一个Python爬虫系统实现自动化数据采集、清洗和存储的全流程。这个爬虫需要解决几个关键问题如何高效抓取分页数据而不被反爬机制拦截如何处理重复企业信息同一企业在不同招聘会重复出现如何实现增量更新只抓取新增数据如何持久化存储数据并提供灵活的导出方式2. 技术方案选型与架构设计2.1 核心组件选择经过对比测试最终确定的技术栈组合请求库requests retrying比urllib3更简洁的API配合重试机制提升稳定性解析库BeautifulSoup lxml对不规则HTML容错性更好去重方案BloomFilter 内存set组合平衡内存占用和去重准确性持久化SQLite CSV双备份轻量级数据库便于迁移CSV方便数据分析增量更新记录最后抓取时间戳 企业ID白名单2.2 系统工作流程graph TD A[启动爬虫] -- B[读取配置] B -- C[初始化去重过滤器] C -- D[分页抓取数据] D -- E[数据清洗去重] E -- F[增量比对] F -- G[持久化存储] G -- H[生成报告]3. 核心代码实现详解3.1 分页抓取模块import requests from retrying import retry retry(stop_max_attempt_number3, wait_fixed2000) def fetch_page(url, paramsNone): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept-Language: zh-CN,zh;q0.9 } try: response requests.get(url, headersheaders, paramsparams, timeout10) response.raise_for_status() return response.text except Exception as e: print(f请求失败: {str(e)}) raise关键点说明使用retrying装饰器实现自动重试设置合理的超时时间10秒模拟浏览器User-Agent和语言偏好对HTTP异常状态码主动抛出异常3.2 去重过滤器实现from pybloom_live import ScalableBloomFilter import hashlib class Deduplicator: def __init__(self): self.bloom ScalableBloomFilter(initial_capacity1000, error_rate0.001) self.memory_set set() def get_fingerprint(self, item): 生成数据指纹 return hashlib.md5(str(item).encode()).hexdigest() def is_duplicate(self, item): fp self.get_fingerprint(item) if fp in self.memory_set: return True if fp in self.bloom: self.memory_set.add(fp) # 提升为精确匹配 return True return False def add_item(self, item): fp self.get_fingerprint(item) self.bloom.add(fp)创新点二级过滤机制BloomFilter初步过滤 内存set精确匹配动态提升当BloomFilter判断可能存在时转入set进行精确判断可扩展的BloomFilter避免预设容量不足4. 数据存储方案4.1 SQLite数据库设计import sqlite3 from contextlib import closing def init_db(db_path): with closing(sqlite3.connect(db_path)) as conn: cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS companies ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, industry TEXT, scale TEXT, job_count INTEGER, last_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(name, industry) -- 联合唯一约束 ) ) conn.commit()4.2 增量更新策略def upsert_company(conn, company_data): sql INSERT OR REPLACE INTO companies (name, industry, scale, job_count, last_update) VALUES (?, ?, ?, ?, datetime(now)) cursor conn.cursor() cursor.execute(sql, ( company_data[name], company_data[industry], company_data[scale], company_data[job_count] )) conn.commit()优势使用UPSERT语法实现插入或更新自动维护最后更新时间利用SQLite的原子事务保证数据一致性5. 实战中的经验总结5.1 反爬应对策略请求频率控制分页请求间随机延迟1-3秒每抓取10页休息15-20秒使用代理IP池建议使用付费API服务请求头优化轮换User-Agent携带Referer字段模拟浏览器Accept头部异常处理监控响应状态码检测页面内容是否包含验证码实现自动告警机制5.2 数据清洗技巧常见问题处理方案企业名称规范化去除多余空格和特殊字符统一有限责任公司/有限公司后缀处理英文大小写问题行业分类映射INDUSTRY_MAPPING { IT: [互联网, 计算机, 软件], 金融: [银行, 证券, 保险] } def normalize_industry(raw_industry): for standard, variants in INDUSTRY_MAPPING.items(): if any(v in raw_industry for v in variants): return standard return 其他企业规模解析将100-500人转换为数值范围处理若干人等模糊表述统一中外表述差异如万人以上vs10,0006. 完整项目部署方案6.1 目录结构建议/project_root │── config/ │ ├── settings.py # 爬虫配置 │ └── industry_map.json # 行业映射表 ├── data/ │ ├── companies.db # SQLite数据库 │ └── export/ # CSV导出目录 ├── spiders/ │ ├── base_spider.py # 基础爬虫类 │ └── jobfair_spider.py # 招聘会爬虫 └── utils/ ├── deduplicator.py # 去重工具 └── storage.py # 存储模块6.2 定时任务配置使用APScheduler实现定时运行from apscheduler.schedulers.blocking import BlockingScheduler scheduler BlockingScheduler() scheduler.scheduled_job(cron, hour2, minute30) def daily_crawl(): spider JobFairSpider() spider.run() if __name__ __main__: scheduler.start()推荐运行方式开发环境直接运行python脚本生产环境配合Supervisor进程管理云部署使用Serverless函数如AWS Lambda7. 性能优化记录通过以下优化将采集效率提升3倍请求并行化from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers4) as executor: page_contents list(executor.map(fetch_page, page_urls))批量数据库写入def batch_insert(conn, companies): cursor conn.cursor() cursor.executemany( INSERT OR IGNORE INTO companies (name, industry, scale, job_count) VALUES (?, ?, ?, ?) , [(c[name], c[industry], c[scale], c[job_count]) for c in companies]) conn.commit()内存缓存优化使用LRU缓存解析规则预编译正则表达式减少临时对象创建8. 扩展应用方向基于现有系统可以扩展企业关联分析构建企业-招聘会关系图分析企业招聘频率变化预测行业人才需求趋势数据可视化import pandas as pd import matplotlib.pyplot as plt df pd.read_sql(SELECT industry, COUNT(*) as count FROM companies GROUP BY industry, conn) df.plot(kindpie, ycount, labelsdf[industry]) plt.savefig(industry_dist.png)自动化报告生成使用Jinja2模板生成HTML报告集成邮件发送功能添加数据变化趋势分析这个爬虫系统经过3个版本的迭代目前稳定运行6个月累计采集超过2万家企业数据帮助用户发现了多个行业人才流动的规律。最大的收获是认识到一个健壮的爬虫系统不仅要有好的技术实现更需要充分考虑业务场景的特殊需求。