1. 项目概述
最近在练习Python爬虫技术,选择了巴黎圣母院新闻网站(news.nd.edu)作为实战目标。这个项目的主要目的是爬取该网站上的新闻内容,包括标题、正文、作者、发布时间等关键信息,并将数据存储到MongoDB数据库中。同时,还需要下载新闻中的图片并保存到本地。
这个爬虫项目采用了模块化设计思路,将整个爬取过程分为三个层级:模块(新闻分类)、版面(新闻列表页)和文章(具体新闻内容)。这种分层设计使得代码结构更清晰,也便于后续维护和扩展。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与准备
2.1 主要技术栈
在这个项目中,我选择了以下技术组合:
- Requests:用于发送HTTP请求获取网页内容
- BeautifulSoup:用于解析HTML文档,提取所需数据
- PyMongo:用于连接和操作MongoDB数据库
- re:Python正则表达式模块,用于字符串匹配和处理
- datetime:处理日期和时间相关操作
选择这些库的主要考虑是:
- Requests和BeautifulSoup组合是Python爬虫的经典搭配,学习曲线平缓,社区支持完善
- MongoDB作为NoSQL数据库,适合存储非结构化的网页内容
- 正则表达式虽然学习成本较高,但在处理复杂文本模式时非常高效
2.2 开发环境准备
在开始编码前,需要确保以下环境已经就绪:
- Python 3.6+环境
- 安装必要的Python库:
bash复制
pip install requests beautifulsoup4 pymongo - MongoDB服务已启动并运行在默认端口27017
- 创建好用于存储图片的本地目录(如D://imgs//nd-news)
3. 网页分析与爬取策略
3.1 目标网站结构分析
巴黎圣母院新闻网站的结构相对清晰:
- 首页:展示最新新闻和主要分类
- 归档页:按年份/月份组织的历史新闻
- 新闻详情页:包含完整的新闻内容和元数据
通过分析发现,网站提供了两种浏览新闻的方式:
- 按类别浏览
- 按时间归档浏览
经过比较,选择按时间归档的方式爬取,因为这种方式能获取更全面的新闻内容。
3.2 爬取逻辑设计
整个爬取过程采用三层结构:
- 模块层:对应不同年份的新闻归档
- 版面层:对应某一年份下的分页列表
- 文章层:具体的新闻内容页面
这种分层设计的好处是:
- 逻辑清晰,便于理解和维护
- 可以灵活控制爬取范围(如只爬取特定年份)
- 出错时可以从断点继续,不必重新开始
4. 核心代码实现
4.1 爬虫类初始化
首先创建一个爬虫类,初始化必要的参数和请求头:
python复制class MitnewsScraper:
def __init__(self, root_url, model_url, img_output_dir):
self.root_url = root_url # 网站根URL
self.model_url = model_url # 当前模块URL
self.img_output_dir = img_output_dir # 图片保存目录
self.headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
'Cookie': '替换成你自己的'
}
4.2 模块爬取实现
模块对应的是不同年份的新闻归档页面。首先获取所有模块URL:
python复制def run():
root_url = 'https://news.nd.edu/'
output_dir = 'D://imgs//nd-news'
response = requests.get('https://news.nd.edu/news/archives/')
soup = BeautifulSoup(response.text, 'html.parser')
model_urls = []
model_url_array = soup.find('ul', 'archives-by-year archives-list').find_all('li')
for item in model_url_array:
model_url = root_url + item.find('a').get('href')
model_urls.append(model_url)
for model_url in model_urls:
scraper = MitnewsScraper(root_url, model_url, output_dir)
scraper.catalogue_all_pages()
4.3 版面爬取实现
每个模块(年份)下的新闻可能分多页显示,需要先获取总页数:
python复制def catalogue_all_pages(self):
response = requests.get(self.model_url, headers=self.headers)
soup = BeautifulSoup(response.text, 'html.parser')
try:
len_catalogues_page = len(soup.find('div', 'pagination').find_all('a'))
list_catalogues_page = soup.find('div', 'pagination').find_all('a')
num_pages = list_catalogues_page[len_catalogues_page - 2].get_text()
print(f'{self.model_url} 模块一共有{num_pages}页版面')
for page in ran
