Python实例题: Python 的简单电影信息
目录
Python实例题
题目
代码实现
实现原理
网页请求:
内容解析:
数据存储:
反爬策略:
关键代码解析
1. 网页请求处理
2. 电影列表解析
3. 电影详情解析
4. 爬虫主逻辑
使用说明
安装依赖:
修改配置:
运行爬虫:
查看结果:
注意事项
法律合规:
反爬策略:
网站结构变化:
数据使用限制:
Python实例题
题目
Python 的简单电影信息
代码实现
import requests
from bs4 import BeautifulSoup
import json
import time
import random
import logging
from fake_useragent import UserAgentclass MovieCrawler:"""电影信息爬虫类"""def __init__(self):"""初始化爬虫"""self.ua = UserAgent()self.headers = {'User-Agent': self.ua.random,'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6','Connection': 'keep-alive'}self.logger = self._setup_logger()self.movies = []def _setup_logger(self):"""配置日志记录"""logger = logging.getLogger('movie_crawler')logger.setLevel(logging.INFO)# 创建控制台处理器ch = logging.StreamHandler()ch.setLevel(logging.INFO)# 创建日志格式formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')ch.setFormatter(formatter)# 添加处理器到loggerlogger.addHandler(ch)return loggerdef fetch_movie_list(self, url, page_num=1):"""获取电影列表页Args:url: 电影列表页URLpage_num: 页码Returns:页面内容或None"""try:# 构造完整URLfull_url = f"{url}?page={page_num}" if page_num > 1 else url# 更新User-Agentself.headers['User-Agent'] = self.ua.random# 发送请求response = requests.get(full_url, headers=self.headers, timeout=10)# 检查响应状态if response.status_code == 200:self.logger.info(f"成功获取页面: {full_url}")return response.textelse:self.logger.error(f"获取页面失败,状态码: {response.status_code}, URL: {full_url}")return Noneexcept Exception as e:self.logger.error(f"请求发生异常: {str(e)}")return Nonedef parse_movie_list(self, html_content):"""解析电影列表页Args:html_content: 页面HTML内容Returns:电影信息列表"""if not html_content:return []try:soup = BeautifulSoup(html_content, 'html.parser')movie_items = []# 这里需要根据实际网站结构调整选择器# 以下是一个示例选择器,用于演示目的for item in soup.select('.movie-item'):try:title = item.select_one('.title').text.strip()link = item.select_one('a')['href']rating = item.select_one('.rating').text.strip()year = item.select_one('.year').text.strip()movie_items.append({'title': title,'link': link,'rating': rating,'year': year})except (AttributeError, KeyError) as e:self.logger.warning(f"解析电影项时出错: {str(e)}")continueself.logger.info(f"成功解析 {len(movie_items)} 部电影")return movie_itemsexcept Exception as e:self.logger.error(f"解析页面时发生异常: {str(e)}")return []def fetch_movie_detail(self, url):"""获取电影详情页Args:url: 电影详情页URLReturns:页面内容或None"""try:# 更新User-Agentself.headers['User-Agent'] = self.ua.random# 发送请求response = requests.get(url, headers=self.headers, timeout=15)# 检查响应状态if response.status_code == 200:self.logger.info(f"成功获取详情页: {url}")return response.textelse:self.logger.error(f"获取详情页失败,状态码: {response.status_code}, URL: {url}")return Noneexcept Exception as e:self.logger.error(f"请求详情页发生异常: {str(e)}")return Nonedef parse_movie_detail(self, html_content, base_info):"""解析电影详情页Args:html_content: 页面HTML内容base_info: 从列表页获取的基础信息Returns:完整的电影信息"""if not html_content:return base_infotry:soup = BeautifulSoup(html_content, 'html.parser')# 提取详细信息,这里需要根据实际网站结构调整选择器director = soup.select_one('.director').text.strip() if soup.select_one('.director') else '未知'actors = [a.text.strip() for a in soup.select('.actors li')] if soup.select('.actors li') else []genre = [g.text.strip() for g in soup.select('.genre a')] if soup.select('.genre a') else []duration = soup.select_one('.duration').text.strip() if soup.select_one('.duration') else '未知'description = soup.select_one('.description').text.strip() if soup.select_one('.description') else ''# 合并信息movie_info = {**base_info,'director': director,'actors': actors,'genre': genre,'duration': duration,'description': description}return movie_infoexcept Exception as e:self.logger.error(f"解析详情页时发生异常: {str(e)}")return base_infodef crawl(self, base_url, total_pages=1):"""执行爬虫Args:base_url: 电影列表页基础URLtotal_pages: 要爬取的总页数"""self.logger.info(f"开始爬取电影信息,总页数: {total_pages}")for page in range(1, total_pages + 1):self.logger.info(f"正在爬取第 {page} 页...")# 获取电影列表页list_content = self.fetch_movie_list(base_url, page)# 解析电影列表movie_list = self.parse_movie_list(list_content)# 遍历电影列表,获取详情for movie in movie_list:# 构造完整详情页URLdetail_url = movie['link']if not detail_url.startswith('http'):detail_url = base_url + detail_url# 获取电影详情页detail_content = self.fetch_movie_detail(detail_url)# 解析电影详情full_movie_info = self.parse_movie_detail(detail_content, movie)# 添加到结果列表self.movies.append(full_movie_info)self.logger.info(f"成功获取电影: {full_movie_info['title']}")# 随机延时,避免过快请求time.sleep(random.uniform(1, 3))# 页面间延时time.sleep(random.uniform(2, 5))self.logger.info(f"爬取完成,共获取 {len(self.movies)} 部电影信息")def save_to_json(self, filename='movies.json'):"""将爬取的电影信息保存到JSON文件Args:filename: 保存的文件名"""try:with open(filename, 'w', encoding='utf-8') as f:json.dump(self.movies, f, ensure_ascii=False, indent=2)self.logger.info(f"电影信息已保存到 {filename}")except Exception as e:self.logger.error(f"保存文件时发生异常: {str(e)}")# 示例使用
def example_usage():# 注意:这只是一个示例URL,实际上需要替换为真实的电影网站URLbase_url = "https://example.com/movies"# 创建爬虫实例crawler = MovieCrawler()# 执行爬虫,爬取前3页crawler.crawl(base_url, total_pages=3)# 保存结果crawler.save_to_json('movies.json')if __name__ == "__main__":example_usage()
实现原理
这个简单的电影爬虫基于以下技术实现:
-
网页请求:
- 使用 requests 库发送 HTTP 请求
- 随机更换 User-Agent 模拟不同浏览器
- 设置合理的请求间隔避免被封 IP
-
内容解析:
- 使用 BeautifulSoup 解析 HTML 内容
- 通过 CSS 选择器提取所需信息
- 处理可能的解析异常
-
数据存储:
- 将爬取的电影信息存储为 JSON 格式
- 提供清晰的日志记录功能
-
反爬策略:
- 随机延时请求
- 动态更换请求头
- 异常处理和日志记录
关键代码解析
1. 网页请求处理
def fetch_movie_list(self, url, page_num=1):try:full_url = f"{url}?page={page_num}" if page_num > 1 else urlself.headers['User-Agent'] = self.ua.randomresponse = requests.get(full_url, headers=self.headers, timeout=10)if response.status_code == 200:self.logger.info(f"成功获取页面: {full_url}")return response.textelse:self.logger.error(f"获取页面失败,状态码: {response.status_code}, URL: {full_url}")return Noneexcept Exception as e:self.logger.error(f"请求发生异常: {str(e)}")return None
2. 电影列表解析
def parse_movie_list(self, html_content):if not html_content:return []try:soup = BeautifulSoup(html_content, 'html.parser')movie_items = []for item in soup.select('.movie-item'):try:title = item.select_one('.title').text.strip()link = item.select_one('a')['href']rating = item.select_one('.rating').text.strip()year = item.select_one('.year').text.strip()movie_items.append({'title': title,'link': link,'rating': rating,'year': year})except (AttributeError, KeyError) as e:self.logger.warning(f"解析电影项时出错: {str(e)}")continueself.logger.info(f"成功解析 {len(movie_items)} 部电影")return movie_itemsexcept Exception as e:self.logger.error(f"解析页面时发生异常: {str(e)}")return []
3. 电影详情解析
def parse_movie_detail(self, html_content, base_info):if not html_content:return base_infotry:soup = BeautifulSoup(html_content, 'html.parser')director = soup.select_one('.director').text.strip() if soup.select_one('.director') else '未知'actors = [a.text.strip() for a in soup.select('.actors li')] if soup.select('.actors li') else []genre = [g.text.strip() for g in soup.select('.genre a')] if soup.select('.genre a') else []duration = soup.select_one('.duration').text.strip() if soup.select_one('.duration') else '未知'description = soup.select_one('.description').text.strip() if soup.select_one('.description') else ''movie_info = {**base_info,'director': director,'actors': actors,'genre': genre,'duration': duration,'description': description}return movie_infoexcept Exception as e:self.logger.error(f"解析详情页时发生异常: {str(e)}")return base_info
4. 爬虫主逻辑
def crawl(self, base_url, total_pages=1):self.logger.info(f"开始爬取电影信息,总页数: {total_pages}")for page in range(1, total_pages + 1):self.logger.info(f"正在爬取第 {page} 页...")# 获取电影列表页list_content = self.fetch_movie_list(base_url, page)# 解析电影列表movie_list = self.parse_movie_list(list_content)# 遍历电影列表,获取详情for movie in movie_list:# 构造完整详情页URLdetail_url = movie['link']if not detail_url.startswith('http'):detail_url = base_url + detail_url# 获取电影详情页detail_content = self.fetch_movie_detail(detail_url)# 解析电影详情full_movie_info = self.parse_movie_detail(detail_content, movie)# 添加到结果列表self.movies.append(full_movie_info)self.logger.info(f"成功获取电影: {full_movie_info['title']}")# 随机延时,避免过快请求time.sleep(random.uniform(1, 3))# 页面间延时time.sleep(random.uniform(2, 5))self.logger.info(f"爬取完成,共获取 {len(self.movies)} 部电影信息")
使用说明
-
安装依赖:
pip install requests beautifulsoup4 fake-useragent
-
修改配置:
-
在代码中替换
base_url
为实际的电影网站 URL,并根据目标网站的 HTML 结构调整解析部分的 CSS 选择器。
-
-
运行爬虫:
python movie_crawler.py
-
查看结果:
- 爬取的电影信息会保存到
movies.json
文件中。
- 爬取的电影信息会保存到
注意事项
-
法律合规:
- 此程序仅用于演示爬虫技术,请勿用于非法获取受版权保护的内容
- 爬取网站前请查看其 robots.txt 文件和使用条款
- 遵守相关国家和地区的法律法规
-
反爬策略:
- 控制爬取速度,避免对目标网站造成压力
- 添加合理的延时和请求头信息
- 考虑使用代理 IP 池
-
网站结构变化:
- 网站结构可能随时变化,需要相应调整解析代码
- 建议添加适当的错误处理和重试机制
-
数据使用限制:
- 爬取的数据仅供个人学习研究使用,不可用于商业用途
- 尊重原网站的版权和数据使用政策