用Python爬豆瓣电影Top250,手把手教你从分析网页到数据保存(附完整代码)

张开发
2026/5/8 10:42:33 15 分钟阅读
用Python爬豆瓣电影Top250,手把手教你从分析网页到数据保存(附完整代码)
零基础实战用Python爬取豆瓣电影Top250全流程解析第一次接触Python爬虫时很多人会被各种概念和术语吓退。其实爬虫并没有想象中那么复杂今天我们就用一个经典案例——豆瓣电影Top250带你从零开始完成一个完整的爬虫项目。这个项目不仅适合初学者练手还能让你掌握爬虫的核心思路和常见问题的解决方法。1. 环境准备与基础知识在开始之前我们需要准备好开发环境。推荐使用Python 3.6及以上版本因为Python 2.7已经停止维护。我们将使用以下主要库requests比urllib更人性化的HTTP请求库BeautifulSoup4HTML解析神器pandas数据处理和保存工具安装这些库非常简单只需在命令行中执行pip install requests beautifulsoup4 pandas提示如果你在使用Anaconda也可以通过conda安装这些包理解爬虫的基本工作流程很重要它通常包括以下几个步骤发送HTTP请求获取网页内容解析HTML提取所需数据清洗和处理数据存储数据文件或数据库豆瓣电影Top250页面结构清晰数据规整非常适合作为第一个爬虫项目。它的URL规律也很容易发现每页显示25部电影通过start参数控制翻页。2. 分析网页结构与URL规律首先我们观察豆瓣Top250的URL模式。打开浏览器访问https://movie.douban.com/top250然后点击第二页你会发现URL变成了https://movie.douban.com/top250?start25filter继续翻页你会发现start参数以25为步长递增页码start值显示电影范围101-2522526-5035051-75.........基于这个规律我们可以用循环生成所有页面的URLbase_url https://movie.douban.com/top250 urls [f{base_url}?start{i*25} for i in range(10)] # 共10页接下来我们需要分析页面结构。在浏览器中右键点击电影标题选择检查可以看到类似这样的HTML结构div classitem div classinfo span classtitle肖申克的救赎/span span classtitleThe Shawshank Redemption/span span classother / 月黑高飞(港) / 刺激1995(台)/span /div /div从这段HTML中我们可以提取以下信息电影中文名第一个classtitle的span电影原名第二个classtitle的span如果有其他名称classother的span3. 编写爬虫代码现在我们可以开始编写完整的爬虫代码了。首先导入必要的库import requests from bs4 import BeautifulSoup import pandas as pd from time import sleep # 防止请求过于频繁然后定义一个函数来获取单页的电影信息def scrape_page(url): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 } response requests.get(url, headersheaders) soup BeautifulSoup(response.text, html.parser) movies [] for item in soup.find_all(div, class_item): titles item.find_all(span, class_title) chinese_title titles[0].text if titles else None original_title titles[1].text if len(titles) 1 else None other_info item.find(span, class_other).text if item.find(span, class_other) else None rating item.find(span, class_rating_num).text if item.find(span, class_rating_num) else None movies.append({ 中文名: chinese_title, 原名: original_title, 其他名称: other_info, 评分: rating }) return movies注意豆瓣有反爬机制我们需要设置User-Agent模拟浏览器访问并且每爬取一页后最好暂停1-2秒接下来编写主函数循环爬取所有页面def main(): base_url https://movie.douban.com/top250 all_movies [] for i in range(10): # 共10页 url f{base_url}?start{i*25} print(f正在爬取第{i1}页: {url}) movies scrape_page(url) all_movies.extend(movies) sleep(2) # 每页间隔2秒 # 保存数据 df pd.DataFrame(all_movies) df.to_csv(douban_top250.csv, indexFalse, encodingutf-8-sig) print(数据已保存为douban_top250.csv) if __name__ __main__: main()4. 数据处理与优化爬取到的原始数据往往需要进一步处理。我们可以对代码进行一些优化异常处理网络请求可能会失败需要添加重试机制更多字段可以提取导演、主演、年份等信息数据清洗处理缺失值、统一格式改进后的scrape_page函数可能长这样def scrape_page(url): try: headers {User-Agent: Mozilla/5.0...} response requests.get(url, headersheaders) response.raise_for_status() # 检查请求是否成功 soup BeautifulSoup(response.text, html.parser) movies [] for item in soup.find_all(div, class_item): # 提取标题 titles item.find_all(span, class_title) chinese_title titles[0].text if titles else original_title titles[1].text if len(titles) 1 else # 提取其他信息 other_info item.find(span, class_other).text if item.find(span, class_other) else rating item.find(span, class_rating_num).text if item.find(span, class_rating_num) else 0.0 # 提取导演和年份 info item.find(div, class_bd).p.text.strip().split(\n)[0] director info.split(导演:)[1].split(主演:)[0].strip() if 导演: in info else year info.split(/)[-1].strip() if / in info else movies.append({ 排名: len(movies) 1, 中文名: chinese_title, 原名: original_title, 其他名称: other_info, 评分: float(rating), 导演: director, 年份: year }) return movies except Exception as e: print(f爬取{url}时出错: {str(e)}) return []5. 高级技巧与反爬策略随着爬虫使用越来越广泛网站的反爬措施也越来越严格。以下是一些实用技巧随机User-Agent使用fake_useragent库生成随机UAIP轮换如果爬取量大可以考虑使用代理IP请求间隔随机化请求间隔时间Cookies处理有些网站需要登录才能访问改进后的请求代码可能如下from fake_useragent import UserAgent def get_random_ua(): ua UserAgent() return ua.random def scrape_page(url): try: headers {User-Agent: get_random_ua()} response requests.get(url, headersheaders, timeout10) response.raise_for_status() # 其余代码...6. 数据存储与分析除了CSV文件我们还可以将数据保存到其他格式Excel适合非技术人员查看JSON适合Web应用数据库如SQLite、MySQL等使用pandas可以轻松实现多种格式的导出# 保存为Excel df.to_excel(douban_top250.xlsx, indexFalse) # 保存为JSON df.to_json(douban_top250.json, orientrecords, force_asciiFalse)有了数据后我们可以进行一些简单的分析比如# 评分最高的10部电影 top10 df.sort_values(评分, ascendingFalse).head(10) # 各年份电影数量统计 year_counts df[年份].value_counts().head(10) # 导演作品数量统计 director_counts df[导演].value_counts().head(10)7. 常见问题与解决方案在实际爬取过程中你可能会遇到以下问题返回403错误说明被识别为爬虫了尝试更换User-Agent或添加更多请求头数据提取不完整检查HTML结构是否变化更新选择器被封IP需要降低请求频率或使用代理编码问题确保使用正确的编码豆瓣使用UTF-8一个更健壮的主函数可能包含重试逻辑def main(): base_url https://movie.douban.com/top250 all_movies [] for i in range(10): url f{base_url}?start{i*25} print(f正在爬取第{i1}页: {url}) for retry in range(3): # 最多重试3次 try: movies scrape_page(url) if movies: # 如果成功获取数据 all_movies.extend(movies) break except Exception as e: print(f第{retry1}次尝试失败: {str(e)}) if retry 2: # 最后一次尝试也失败 print(f无法爬取{url}跳过该页) sleep(5 * (retry 1)) # 重试间隔逐渐增加 sleep(1 random.random()) # 随机间隔1-2秒 # 保存数据...8. 项目扩展思路掌握了基础爬虫后你可以尝试以下扩展定时爬取使用schedule库定时运行脚本追踪排名变化可视化用matplotlib或pyecharts制作评分分布图Web应用用Flask或Django搭建电影推荐网站多线程爬取使用concurrent.futures提高爬取效率例如一个简单的多线程版本from concurrent.futures import ThreadPoolExecutor def main(): base_url https://movie.douban.com/top250 urls [f{base_url}?start{i*25} for i in range(10)] all_movies [] with ThreadPoolExecutor(max_workers4) as executor: results executor.map(scrape_page, urls) for movies in results: all_movies.extend(movies) # 保存数据...在实际项目中我发现豆瓣对频繁请求比较敏感合理设置请求间隔和使用随机User-Agent能显著提高成功率。另外HTML结构可能会随时间变化定期检查并更新选择器是必要的维护工作。

更多文章