Python爬虫实战用BeautifulSoup和requests抓取豆瓣Top250电影数据每次看到豆瓣电影Top250榜单总会被那些经典影片吸引。作为影迷你是否想过用技术手段分析这些高分电影的共同特征本文将带你用Python的requests和BeautifulSoup库从零开始构建一个完整的爬虫项目不仅能获取电影数据还能进行简单的可视化分析。1. 环境准备与基础配置在开始爬取之前我们需要准备好Python环境和必要的库。建议使用Python 3.6版本它能更好地支持现代爬虫开发。首先安装必要的库pip install requests beautifulsoup4 openpyxl pandas matplotlib这些库的作用分别是requests发送HTTP请求获取网页内容beautifulsoup4解析HTML文档openpyxl将数据保存到Excel文件pandas数据处理和分析matplotlib数据可视化提示如果遇到安装问题可以尝试使用清华镜像源pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名配置请求头是爬虫的重要一步它能模拟浏览器访问降低被反爬的风险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, Accept-Language: zh-CN,zh;q0.9, Referer: https://movie.douban.com/ }2. 分析网页结构与爬取策略豆瓣Top250的页面结构很有规律每页显示25部电影共10页。我们需要先分析页面元素确定要提取的数据位置。打开Chrome开发者工具F12可以看到每部电影的信息都包裹在一个div classitem的容器中。关键信息分布如下信息类型HTML选择器备注电影名称.title中文名在第一个span评分.rating_num直接获取文本评价人数.star span:last-child需要正则提取数字导演/主演.bd p需要文本处理和正则匹配年份/地区/类型.bd p通过/分割字符串分页URL的规律也很明显第一页https://movie.douban.com/top250 第二页https://movie.douban.com/top250?start25filter 第三页https://movie.douban.com/top250?start50filter基于这个规律我们可以用生成器来批量产生所有页面的URLdef generate_urls(base_url, pages10): for page in range(pages): yield f{base_url}?start{page*25}filter3. 实现核心爬取功能现在我们来构建完整的爬虫类。这个类将封装所有爬取逻辑包括发送请求、解析页面、保存数据等功能。import re import requests from bs4 import BeautifulSoup from openpyxl import Workbook class DoubanTop250Spider: def __init__(self): self.base_url https://movie.douban.com/top250 self.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 } def get_page(self, url): try: response requests.get(url, headersself.headers) response.raise_for_status() return response.text except requests.RequestException as e: print(f请求失败: {e}) return None def parse_movie(self, item): # 提取电影中文名 title item.find(span, class_title).text.strip() # 提取导演、主演、年份等信息 info item.select_one(div.bd p).text.strip() details re.search(r(\d{4}).*?/(.*?)/, info) year details.group(1) if details else 未知年份 region details.group(2).strip() if details else 未知地区 # 提取导演 director_match re.search(r导演:(.*?)\s, info) director director_match.group(1).strip() if director_match else 未知导演 # 提取评分和评价人数 rating item.find(span, class_rating_num).text.strip() votes re.search(r\d, item.select(.star span)[-1].text).group() # 提取简介 quote item.find(span, class_inq) quote quote.text.strip() if quote else 无简介 return { title: title, year: year, region: region, director: director, rating: rating, votes: votes, quote: quote } def run(self, output_filedouban_top250.xlsx): wb Workbook() ws wb.active ws.append([电影名, 年份, 地区, 导演, 评分, 评价人数, 简介]) for url in self.generate_urls(): html self.get_page(url) if not html: continue soup BeautifulSoup(html, html.parser) for item in soup.find_all(div, class_item): movie self.parse_movie(item) ws.append([ movie[title], movie[year], movie[region], movie[director], movie[rating], movie[votes], movie[quote] ]) wb.save(output_file) print(f数据已保存到 {output_file})4. 数据可视化分析获取到数据后我们可以进行一些有趣的分析。比如查看评分分布、导演作品数量排名等。首先安装必要的可视化库pip install pandas matplotlib然后进行简单的评分分布分析import pandas as pd import matplotlib.pyplot as plt # 设置中文字体 plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False # 读取数据 df pd.read_excel(douban_top250.xlsx) # 评分分布分析 plt.figure(figsize(10, 6)) df[评分] df[评分].astype(float) rating_counts df[评分].value_counts().sort_index() plt.bar(rating_counts.index, rating_counts.values, width0.1) plt.title(豆瓣Top250电影评分分布) plt.xlabel(评分) plt.ylabel(电影数量) plt.grid(axisy, linestyle--, alpha0.7) plt.show()还可以分析哪些导演在Top250中作品最多# 导演作品数量分析 director_counts df[导演].value_counts().head(10) plt.figure(figsize(12, 6)) director_counts.plot(kindbarh) plt.title(Top250中作品最多的导演) plt.xlabel(作品数量) plt.ylabel(导演) plt.gca().invert_yaxis() plt.show()5. 高级技巧与反爬应对在实际爬取过程中你可能会遇到各种反爬措施。这里分享几个实用技巧请求间隔在连续请求之间添加随机延迟import time import random time.sleep(random.uniform(1, 3)) # 随机等待1-3秒IP代理使用代理IP池轮换IPproxies { http: http://your.proxy.ip:port, https: https://your.proxy.ip:port } response requests.get(url, headersheaders, proxiesproxies)Cookie处理维持会话状态session requests.Session() session.headers.update(headers) response session.get(url)异常处理增强爬虫的健壮性try: response requests.get(url, headersheaders, timeout10) response.raise_for_status() except requests.exceptions.RequestException as e: print(f请求失败: {e}) # 重试逻辑或记录日志6. 数据存储的更多选择除了Excel我们还可以将数据保存到其他格式CSV格式df.to_csv(douban_top250.csv, indexFalse, encodingutf_8_sig)JSON格式import json data df.to_dict(orientrecords) with open(douban_top250.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)数据库存储以SQLite为例import sqlite3 conn sqlite3.connect(movies.db) df.to_sql(douban_top250, conn, if_existsreplace, indexFalse) conn.close()在实际项目中我经常遇到需要处理缺失数据的情况。比如有些电影可能没有主演信息或者简介为空。这时候合理的默认值处理就很重要# 在parse_movie方法中完善异常处理 try: actors re.search(r主演:(.*?)[.,], info).group(1).strip() except (AttributeError, IndexError): actors 主演信息缺失