Python爬虫实战:慕课网课程数据采集与存储

Python爬虫实战:慕课网课程数据采集与存储 1. Python爬取慕课网课程信息实战指南慕课网作为国内知名的IT技能学习平台汇集了大量优质的编程课程资源。对于学习者而言能够快速获取并分析这些课程信息无论是用于选课参考还是市场研究都极具价值。本文将详细介绍如何使用Python爬虫技术完整抓取慕课网课程数据并存储到结构化文件中。作为一名长期从事数据采集的开发者我发现慕课网的页面结构相对规整但仍有反爬措施需要采用合理的爬取策略。本次实战将使用requestsBeautifulSoup的基础组合配合随机请求头等反反爬技术实现稳定可靠的数据采集。2. 环境准备与工具选型2.1 开发环境配置推荐使用Python 3.8版本进行开发主要依赖库包括requests用于发送HTTP请求BeautifulSoup4HTML解析工具pandas数据清洗与存储fake-useragent生成随机请求头安装命令pip install requests beautifulsoup4 pandas fake-useragent提示建议使用虚拟环境隔离项目依赖避免与其他项目产生冲突。可以使用venv或conda创建独立环境。2.2 工具选型考量相比Scrapy等框架选择requestsBS4组合的原因学习曲线平缓适合爬虫初学者快速上手对小型爬取任务更加轻量灵活调试和问题排查更直观无需处理框架的复杂配置对于需要大规模爬取的情况后续可以考虑升级到Scrapy或selenium方案。但根据实测慕课网单次爬取约1000条课程数据本方案完全够用且效率足够。3. 页面分析与爬取策略设计3.1 慕课网页面结构解析通过浏览器开发者工具分析慕课网课程列表页主要特点分页采用传统页码形式而非无限滚动单个课程卡片包含课程名称、讲师、学习人数、价格等核心信息详情页有更完整的课程大纲、评价等数据关键URL模式列表页https://www.imooc.com/course/list?page{页码}详情页https://www.imooc.com/learn/{课程ID}3.2 反爬机制与应对方案经过多次测试发现慕课网有以下防护措施请求频率限制连续快速请求会触发429状态码User-Agent检测使用非常见UA会被拒绝Cookie验证部分接口需要携带会话cookie应对策略设置随机延迟每个请求间隔1-3秒轮换User-Agent使用fake-useragent库维持会话使用requests.Session()4. 核心爬取代码实现4.1 基础爬取函数封装首先实现获取单个页面的通用函数from fake_useragent import UserAgent import requests import time import random def get_page(url): ua UserAgent() headers { User-Agent: ua.random, Referer: https://www.imooc.com/ } try: # 随机延迟1-3秒 time.sleep(random.uniform(1, 3)) response requests.get(url, headersheaders) response.raise_for_status() return response.text except Exception as e: print(f请求失败: {url}, 错误: {e}) return None4.2 列表页解析与课程ID提取使用BeautifulSoup提取当前页所有课程的基本信息和详情页链接from bs4 import BeautifulSoup def parse_list_page(html): soup BeautifulSoup(html, html.parser) courses [] for item in soup.select(.course-card-container): course { title: item.select_one(.course-card-name).text.strip(), url: https://www.imooc.com item.select_one(a)[href], course_id: item.select_one(a)[href].split(/)[-1], difficulty: item.select_one(.course-card-info span).text.strip(), student_count: item.select_one(.course-card-info span:nth-child(2)).text.strip(), price: item.select_one(.price span).text.strip() if item.select_one(.price span) else 免费 } courses.append(course) return courses4.3 详情页深度信息提取对于需要更详细数据的场景可以进一步爬取详情页def parse_detail_page(html): soup BeautifulSoup(html, html.parser) detail { description: soup.select_one(.course-description).text.strip() if soup.select_one(.course-description) else , chapters: [chap.text.strip() for chap in soup.select(.chapter h3)], rating: soup.select_one(.statics .score-num).text.strip() if soup.select_one(.statics .score-num) else , comment_count: soup.select_one(.statics span:nth-child(3)).text.strip() if soup.select_one(.statics span:nth-child(3)) else } return detail5. 数据存储与持久化5.1 CSV存储实现使用pandas将爬取结果保存为结构化文件import pandas as pd def save_to_csv(data, filenameimooc_courses.csv): df pd.DataFrame(data) df.to_csv(filename, indexFalse, encodingutf_8_sig) # 支持中文 print(f数据已保存到 {filename}, 共 {len(df)} 条记录)5.2 完整爬取流程整合将各模块组合成完整工作流def main(): base_url https://www.imooc.com/course/list?page all_courses [] for page in range(1, 6): # 爬取前5页 url base_url str(page) print(f正在爬取第 {page} 页: {url}) html get_page(url) if html: courses parse_list_page(html) all_courses.extend(courses) # 可选爬取详情页信息 for course in all_courses[:10]: # 示例只爬前10个课程的详情 detail_html get_page(course[url]) if detail_html: detail parse_detail_page(detail_html) course.update(detail) save_to_csv(all_courses) if __name__ __main__: main()6. 高级技巧与优化方案6.1 并发爬取实现使用线程池提高爬取效率需谨慎控制并发数from concurrent.futures import ThreadPoolExecutor def concurrent_crawl(): base_url https://www.imooc.com/course/list?page urls [base_url str(i) for i in range(1, 11)] with ThreadPoolExecutor(max_workers3) as executor: # 控制并发数 htmls list(executor.map(get_page, urls)) all_courses [] for html in htmls: if html: all_courses.extend(parse_list_page(html)) save_to_csv(all_courses)6.2 断点续爬与增量更新实现持久化爬取状态避免重复爬取import json import os def load_progress(filenameprogress.json): if os.path.exists(filename): with open(filename, r) as f: return json.load(f) return {last_page: 0, crawled_ids: []} def save_progress(progress, filenameprogress.json): with open(filename, w) as f: json.dump(progress, f)7. 常见问题与解决方案7.1 请求被拒绝(403状态码)可能原因及解决User-Agent被识别为爬虫解决方案使用fake-useragent轮换UAIP被临时封禁解决方案增加延迟或使用代理IP池7.2 数据提取不完整典型表现部分字段获取为空CSS选择器失效排查步骤检查页面结构是否更新验证CSS选择器是否仍然有效添加更健壮的选择器备用方案7.3 数据存储乱码处理方法确保文件以UTF-8编码保存对于CSV使用utf_8_sig编码数据库存储时检查连接编码设置8. 法律与道德注意事项严格遵守robots.txt协议慕课网的robots.txt对部分路径有限制控制请求频率单线程随机延迟是最安全的做法仅用于个人学习禁止将爬取数据用于商业用途数据最小化原则只爬取必要数据避免过度采集重要建议在爬取前仔细阅读慕课网的用户协议合规使用爬虫技术。本文示例仅供技术学习交流。9. 项目扩展方向基于基础爬取结果可以考虑以下扩展数据分析对课程价格、难度分布进行可视化自动监控定期爬取跟踪课程更新情况推荐系统基于课程标签构建推荐模型API开发将数据封装为RESTful服务例如使用matplotlib进行基础分析import matplotlib.pyplot as plt def analyze_data(filenameimooc_courses.csv): df pd.read_csv(filename) # 价格分布分析 price_counts df[price].value_counts() price_counts.plot(kindbar) plt.title(课程价格分布) plt.show() # 难度分布 difficulty_counts df[difficulty].value_counts() difficulty_counts.plot(kindpie, autopct%1.1f%%) plt.title(课程难度分布) plt.show()在实际项目中我发现慕课网的页面结构大约每3-6个月会有一次较大调整因此爬虫代码需要定期维护更新。建议添加自动化的页面结构检测机制当发现解析失败率超过阈值时发送告警通知。