Python爬虫实战:手把手教你如何构建高校师资公开信息聚合数据库! ㊗️本期内容已收录至专栏《Python爬虫实战》持续完善知识体系与项目实战建议先订阅收藏后续查阅更方便㊙️本期爬虫难度指数⭐福利一次订阅后专栏内的所有文章可永久免费看持续更新中保底1000(篇)硬核实战内容。全文目录 开篇语0️⃣ 前言Preface1️⃣ 摘要Abstract2️⃣ 背景与需求Why3️⃣ 合规与注意事项必写4️⃣ 技术选型与整体流程What/How5️⃣ 环境准备与依赖安装可复现6️⃣ 核心实现请求层Fetcher7️⃣ 核心实现解析层Parser8️⃣ 数据存储与导出Storage9️⃣ 运行方式与结果展示必写 常见问题与排错强烈建议写1️⃣1️⃣ 进阶优化科研力量可视化加分项1️⃣2️⃣ 总结与延伸阅读 文末✅ 专栏持续更新中建议收藏 订阅✅ 互动征集✅ 免责声明 开篇语哈喽各位小伙伴们你们好呀我是【喵手】。运营社区 C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO欢迎大家常来逛逛一起学习一起进步我长期专注Python 爬虫工程化实战主理专栏 《Python爬虫实战》从采集策略到反爬对抗从数据清洗到分布式调度持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”让数据价值真正做到——抓得到、洗得净、用得上。专栏食用指南建议收藏✅ 入门基础环境搭建 / 请求与解析 / 数据落库✅ 进阶提升登录鉴权 / 动态渲染 / 反爬对抗✅ 工程实战异步并发 / 分布式调度 / 监控与容错✅ 项目落地数据治理 / 可视化分析 / 场景化应用专栏推广时间如果你想系统学爬虫而不是碎片化东拼西凑欢迎订阅专栏《Python爬虫实战》一次订阅后专栏内的所有文章可永久免费阅读持续更新中。订阅后更新会优先推送按目录学习更高效0️⃣ 前言Preface嘿同学们 今天我们要用 Python 写一个经典的“二段式”爬虫目标是高校院系官网的“师资队伍”版块。我们将使用requests发起请求用BeautifulSoup应对复杂冗余的旧版网页标签最后把清洗好的公开名片存入轻量级的SQLite数据库中。读完这篇你能获得掌握针对传统 CMS 建站高校官网常见的容错提取技巧。学会处理网页中常见的邮箱反爬伪装如[at]替换。获得一套内置去重逻辑的SQLite数据库存储框架。1️⃣ 摘要Abstract本文演示了如何构建一个静态网页爬虫抓取大学院系官网公开的教师目录与个人主页。通过解析列表页获取详情 URL深入详情页抽取姓名、职称、研究方向、公开邮箱及所属部门等关键字段。全程遵守合规原则最终将非结构化文本清洗并持久化至 SQLite 数据库及 CSV 文件中。2️⃣ 背景与需求Why为什么要爬学业规划考研/博选导师前横向对比导师的研究方向和活跃度。学术交流快速聚合某领域的专家学者公开联系方式如邀请参会。信息聚合为构建本地化的“科研人才图谱”提供底层数据。目标字段清单Target Fields我们要提取的是标准的“学术名片”name: 姓名title: 职称教授/副教授/讲师等research_area: 研究方向email: 邮箱仅限页面公开展示的homepage_url: 个人主页链接department: 所属部门如计算机系、软工系3️⃣ 合规与注意事项必写️学术爬虫的“三大纪律”只取公开数据绝不越权我们只抓取无需登录即可访问的网页。如果邮箱被隐藏或需要内网权限绝对不要尝试绕过或破解。温柔访问频率控制高校的服务器通常带宽有限尤其是老校区服务器。务必在详情页遍历时加入time.sleep(2)的随机休眠别把学校网站搞宕机了Robots 协议先查看目标站点的robots.txt确保/faculty/或类似目录允许被爬取。抓取的数据仅供个人学习或学术分析严禁用于发送垃圾营销邮件Spam。4️⃣ 技术选型与整体流程What/How技术路线静态网页抓取传统二段式。工具栈requestsBeautifulSoup4re(正则) sqlite3。为什么选 BS4高校老网站经常标签闭合不严谨用 XPath 容易报错而 BS4 的容错率最高。整体流程访问师资列表页➔提取所有教师详情页 URL➔循环请求详情页➔正则与 BS4 协同抽取字段➔清洗数据 (处理特殊字符)➔入库 SQLite5️⃣ 环境准备与依赖安装可复现建议使用 Python 3.8。一键安装依赖包pipinstallrequests beautifulsoup4 pandas matplotlib项目目录树推荐edu_faculty_scraper/ ├── data/ │ ├── faculty_data.db # 我们的轻量级数据库 │ └── faculty_export.csv # 导出的表格 ├── reports/ │ └── title_distribution.png # 职称分布图 └── spider_edu.py # 爬虫核心代码6️⃣ 核心实现请求层Fetcher高校网站的一大特色是编码混乱。有些用UTF-8有些还停留在古老的GB2312或GBK。我们需要写一个智能检测编码的获取器。importrequestsimporttimeimportrandomimportcchardet# 可选用于更精准的编码检测如果没有也可依赖 requests 自带的frombs4importBeautifulSoupfromrequests.exceptionsimportRequestExceptionclassEduFetcher:def__init__(self):self.headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36,Accept:text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8,}self.sessionrequests.Session()self.session.headers.update(self.headers)defget_html(self,url,retries3):foriinrange(retries):try:# 设置超时为 15 秒校园网有时候响应慢responseself.session.get(url,timeout15)ifresponse.status_code200:# 应对高校网站乱码的终极杀招从 content 推断编码response.encodingresponse.apparent_encodingreturnresponse.textelse:print(f⚠️ 状态码异常:{response.status_code}-{url})exceptRequestExceptionase:print(f❌ 请求超时/失败 ({i1}/{retries}):{e})time.sleep(random.uniform(2,4))returnNone7️⃣ 核心实现解析层Parser这里我们需要处理两个页面列表页和详情页。详情页的解析中我们特别加入了对邮箱伪装的处理。importrefromurllib.parseimporturljoindefparse_faculty_list(html,base_url): 第一段从列表页提取老师的个人主页链接 soupBeautifulSoup(html,html.parser)# 假设教师列表在一个 class 为 teacher-list 的 ul 里linkssoup.select(.teacher-list a)profile_urls[]forlinkinlinks:hreflink.get(href)ifhref:# 自动处理相对路径转化为绝对路径full_urlurljoin(base_url,href)profile_urls.append(full_url)# 去重returnlist(set(profile_urls))defparse_profile(html,url,department): 第二段深度解析教师个人名片 soupBeautifulSoup(html,html.parser)# 默认值兜底data{name:未知,title:未公开,research_area:未公开,email:未公开,homepage_url:url,department:department}try:# 1. 抓取姓名 (通常是 h1 或 h2)name_tagsoup.find([h1,h2],class_teacher-name)ifname_tag:data[name]name_tag.text.strip()# 2. 抓取职称 (可能是 p 或 span)title_tagsoup.find(stringre.compile(r(教授|副教授|讲师|研究员)))iftitle_tag:data[title]title_tag.strip()# 3. 抓取研究方向# 常见结构strong研究方向/strong 人工智能、大数据res_labelsoup.find(stringre.compile(r研究方向|研究领域))ifres_label:# 找它的下一个兄弟节点或者父节点的文本parent_textres_label.find_parent().text.strip()data[research_area]parent_text.replace(研究方向,).replace(研究领域,).strip()# 4. 抓取邮箱 (核心技术点处理混淆)# 用正则在全文范围搜索邮箱格式兼容 name[at]edu.cn 这种初级防爬text_contentsoup.get_text()# 将常见的混淆替换回 text_contentre.sub(r\[at\]|\(at\)|#,,text_content,flagsre.IGNORECASE)email_matchre.search(r[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,},text_content)ifemail_match:data[email]email_match.group(0)exceptExceptionase:print(f⚠️ 解析异常{url}:{e})returndata8️⃣ 数据存储与导出Storage对于具有结构化字段的名片数据存入SQLite是最优雅的。方便以后用 SQL 语句筛选比如找出所有“副教授”。importsqlite3importpandasaspdimportosdefinit_db(db_pathdata/faculty_data.db):# English filename baselineos.makedirs(data,exist_okTrue)connsqlite3.connect(db_path)cursorconn.cursor()# 创建表homepage_url 作为唯一主键防止重复抓取cursor.execute( CREATE TABLE IF NOT EXISTS faculty ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, title TEXT, research_area TEXT, email TEXT, department TEXT, homepage_url TEXT UNIQUE ) )conn.commit()returnconndefsave_to_db(conn,data_list):ifnotdata_list:returncursorconn.cursor()success_count0fordataindata_list:try:# INSERT OR IGNORE 保证不会插入重复的 URLcursor.execute( INSERT OR IGNORE INTO faculty (name, title, research_area, email, department, homepage_url) VALUES (?, ?, ?, ?, ?, ?) ,(data[name],data[title],data[research_area],data[email],data[department],data[homepage_url]))ifcursor.rowcount0:success_count1exceptExceptionase:print(f❌ 数据库写入失败:{e})conn.commit()print(f 数据库更新完毕新增{success_count}条记录。)defexport_to_csv(conn,csv_pathdata/faculty_export.csv):dfpd.read_sql_query(SELECT * FROM faculty,conn)df.to_csv(csv_path,indexFalse,encodingutf-8-sig)print(f 数据已同步导出至 CSV:{csv_path})9️⃣ 运行方式与结果展示必写来把我们前面的拼图组装起来跑一波看看defmain():print( 高校师资信息聚合爬虫启动)# 假设目标为某大学计算机学院师资列表BASE_URLhttps://cs.example.edu.cnLIST_URLf{BASE_URL}/faculty/list.htmDEPARTMENT计算机科学与技术系fetcherEduFetcher()db_conninit_db()# 1. 抓取列表页list_htmlfetcher.get_html(LIST_URL)ifnotlist_html:print(❌ 无法获取列表页程序退出。)returnprofile_urlsparse_faculty_list(list_html,BASE_URL)print(f✅ 发现{len(profile_urls)}位教师主页准备深入抽取...)# 为了演示我们只抓前 5 位faculty_dataset[]foridx,urlinenumerate(profile_urls[:5]):print(f [{idx1}/5] 正在拜访:{url})detail_htmlfetcher.get_html(url)ifdetail_html:profile_dataparse_profile(detail_html,url,DEPARTMENT)faculty_dataset.append(profile_data)# ⚠️ 校园网脆弱一定要休眠time.sleep(random.uniform(1.5,3.0))# 2. 存盘与导出print(*30)save_to_db(db_conn,faculty_dataset)export_to_csv(db_conn)db_conn.close()if__name____main__:# main()pass# 实际运行时取消注释运行结果预览 高校师资信息聚合爬虫启动 ✅ 发现 45 位教师主页准备深入抽取... [1/5] 正在拜访: https://cs.example.edu.cn/info/1001.htm [2/5] 正在拜访: https://cs.example.edu.cn/info/1002.htm ... 数据库更新完毕新增 5 条记录。 数据已同步导出至 CSV: data/faculty_export.csv | name | title | research_area | email | department | |:-----|:-------|:--------------------|:------------------|:-----------| | 张三 | 教授 | 深度学习、计算机视觉| zhangsanedu.cn | 计算机科学 | | 李四 | 副教授 | 联邦学习、隐私计算 | lisiedu.cn | 计算机科学 | 常见问题与排错强烈建议写抓取到的全是一堆乱码菱形问号诊断100% 是编码问题。老校区网站可能是GB2312。解法检查requests的apparent_encoding是否生效。如果不准可以直接强制写死response.encoding GBK。提取的文本里带有很多\r\n\t和空格解法在 BS4 解析后统一使用string.replace(\r, ).replace(\n, ).strip()进行清洗如果是多个空格可以用re.sub(r\s, , string)压缩。部分老师找不到邮箱诊断有些大牛老师为了防骚扰把邮箱做成了一张图片解法这是正常现象。保持克制我们只抓文本邮箱图片邮箱记为“未公开”即可强行上 OCR 图像识别就有点越界了。1️⃣1️⃣ 进阶优化科研力量可视化加分项我们将数据库里的职称字段拉出来画一个“师资团队结构图”全英文标签。importmatplotlib.pyplotaspltdefplot_faculty_titles(db_pathdata/faculty_data.db):ifnotos.path.exists(db_path):returnconnsqlite3.connect(db_path)dfpd.read_sql_query(SELECT title FROM faculty,conn)conn.close()# 清洗未知的职称dfdf[df[title]!未公开]title_countsdf[title].value_counts()plt.figure(figsize(8,8))# 绘制饼图plt.pie(title_counts,labelstitle_counts.index,autopct%1.1f%%,colors[#ff9999,#66b3ff,#99ff99,#ffcc99],startangle90)# English Labels Requiredplt.title(Faculty Title Distribution,fontsize16,fontweightbold)os.makedirs(reports,exist_okTrue)out_pathreports/title_distribution.png# English filenameplt.savefig(out_path,dpi300)print(f 师资结构饼图已生成至:{out_path})# 可以在 main() 结尾调用 plot_faculty_titles()1️⃣2️⃣ 总结与延伸阅读复盘总结我们完成了一个极为克制且有价值的高校爬虫。通过requests解决老旧网页编码借助BeautifulSoup的模糊搜索与正则表达式过滤了噪音信息最终将零散的学术名片汇聚成了支持 SQL 查询的本地数据库。下一步既然你已经有了老师们的“研究方向Research Area”下一步不妨尝试引入自然语言处理库如jieba分词做一个该院系的科研热词词云图Word Cloud一眼看透这个学院的主攻方向是什么 文末好啦以上就是本期的全部内容啦如果你在实践过程中遇到任何疑问欢迎在评论区留言交流我看到都会尽量回复咱们下期见小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦三连就是对我写作道路上最好的鼓励与支持❤️✅ 专栏持续更新中建议收藏 订阅墙裂推荐订阅专栏 《Python爬虫实战》本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新争取让每一期内容都做到✅ 讲得清楚原理✅ 跑得起来代码✅ 用得上场景✅ 扛得住工程化想系统提升的小伙伴强烈建议先订阅专栏 《Python爬虫实战》再按目录大纲顺序学习效率十倍上升✅ 互动征集想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战评论区留言告诉我你的需求我会优先安排实现(更新)哒~⭐️ 若喜欢我就请关注我叭更新不迷路⭐️ 若对你有用就请点赞支持一下叭给我一点点动力⭐️ 若有疑问就请评论留言告诉我叭我会补坑 更新迭代✅ 免责声明本文爬虫思路、相关技术和代码仅用于学习参考对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。使用或者参考本项目即表示您已阅读并同意以下条款合法使用 不得将本项目用于任何违法、违规或侵犯他人权益的行为包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。风险自负 任何因使用本项目而产生的法律责任、技术风险或经济损失由使用者自行承担项目作者不承担任何形式的责任。禁止滥用 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。使用或者参考本项目即视为同意上述条款,即 “谁使用谁负责” 。如不同意请立即停止使用并删除本项目。