最近在做一个数据分析项目,需要获取一些电影的评价数据来做趋势分析。豆瓣作为国内最权威的影视评分社区,其数据自然是最佳选择。然而,手动收集不仅效率低下,而且难以进行大规模分析。于是,一个自动化的豆瓣数据采集与可视化方案就成了刚需。本文将手把手带你实现一个完整的豆瓣电影数据爬虫,并将采集到的数据进行清洗、存储,最终通过可视化图表直观展示。无论你是刚接触Python爬虫的新手,还是想学习数据可视化流程的开发者,都能从这篇实战教程中获得一套可直接复用的代码和清晰的思路。
1. 项目背景与核心概念
在开始敲代码之前,我们有必要厘清几个核心概念,并明确本项目的目标和边界。
1.1 什么是网络爬虫?
网络爬虫(Web Crawler),是一种按照既定规则,自动抓取互联网信息的程序或脚本。你可以把它想象成一只不知疲倦的“蜘蛛”,在互联网这张“大网”上爬行,收集它遇到的信息。
根据任务目标,爬虫可以分为几类:
- 批量型爬虫:一次性抓取大量数据,常用于项目初期数据积累。我们本次要构建的就是这种。
- 增量型爬虫:只抓取网站上新增或更新的内容,常用于监控和持续数据更新。
- 垂直型爬虫:针对某一特定领域(如电商、招聘、影视)进行深度抓取,对页面解析逻辑要求更高。
我们的“豆瓣电影爬虫”就是一个典型的垂直型、批量型爬虫。
1.2 为什么选择豆瓣电影数据?
豆瓣电影提供了丰富的结构化数据,包括电影名称、评分、评价人数、导演、演员、类型、简介、短评等。这些数据非常适合用于:
- 市场分析:分析不同类型、不同导演电影的受欢迎程度。
- 趋势预测:观察评分与票房、口碑之间的关系。
- 个人兴趣挖掘:构建推荐系统的数据基础。
- 学习实践:数据结构清晰,是练习爬虫和数据处理的绝佳对象。
1.3 数据可视化的重要性
原始数据只是一堆冰冷的数字和文本,难以直接洞察规律。数据可视化通过图表(如柱状图、折线图、词云)将数据转化为直观的图形,帮助我们:
- 快速发现模式与异常:例如,一眼看出哪种电影类型平均分最高。
- 讲述数据故事:用图表支撑你的分析结论,更具说服力。
- 辅助决策:为产品设计、内容运营等提供数据依据。
本项目将使用Matplotlib和Pyecharts这两个强大的Python库来完成可视化部分。
1.4 法律与道德边界
非常重要!爬虫技术是一把双刃剑。在开始之前,我们必须遵守以下原则:
- 遵守
robots.txt:访问https://www.douban.com/robots.txt,查看豆瓣允许和禁止爬取的路径。尊重网站的规则。 - 限制请求频率:在代码中主动添加延时,避免对豆瓣服务器造成压力,这既是道德要求,也能防止你的IP被封锁。
- 仅用于个人学习与研究:切勿将爬取的数据用于商业用途或进行恶意传播,侵犯豆瓣及用户权益。
- 不爬取个人隐私信息:本项目仅爬取公开的电影信息,不涉及用户个人主页、私密评论等。
2. 环境准备与工具说明
工欲善其事,必先利其器。我们先来搭建开发环境。
2.1 所需工具与库
- 编程语言:Python 3.7 或以上版本。建议使用 Python 3.8+,兼容性更好。
- 开发环境:任意你熟悉的IDE或编辑器,如 PyCharm, VSCode, Jupyter Notebook。
- 关键Python库:
requests:用于发送HTTP请求,获取网页HTML内容。BeautifulSoup4 (bs4):用于解析HTML,提取结构化数据。lxml:BeautifulSoup的一个解析器,速度快。pandas:数据处理与分析的核心库,用于清洗和整理数据。matplotlib:基础绘图库,用于生成静态图表。pyecharts:基于ECharts的Python可视化库,能生成交互式、美观的图表。fake-useragent:随机生成User-Agent,模拟不同浏览器访问,降低被封风险。sqlite3(Python内置) 或pymysql/sqlalchemy:用于将数据存储到数据库,实现持久化。
2.2 安装依赖库
打开你的终端(命令行),使用pip命令一次性安装所有需要的库:
pip install requests beautifulsoup4 lxml pandas matplotlib pyecharts fake-useragent如果你打算使用MySQL,还需要安装pymysql:
pip install pymysql2.3 项目结构规划
一个清晰的项目结构能让代码更易维护。建议创建如下目录和文件:
douban_movie_crawler/ │ ├── spider/ # 爬虫核心模块 │ ├── __init__.py │ ├── douban_spider.py # 爬虫主逻辑 │ └── utils.py # 工具函数,如请求头处理、延时 │ ├── data/ # 数据存储 │ ├── raw/ # 原始HTML或JSON(可选) │ ├── processed/ # 清洗后的数据文件 │ └── douban_movies.db # SQLite数据库文件(运行后生成) │ ├── visualization/ # 可视化模块 │ ├── __init__.py │ ├── chart_generator.py # 图表生成逻辑 │ └── templates/ # Pyecharts生成的HTML模板(运行后生成) │ ├── config.py # 配置文件,如数据库连接、爬取页数 ├── main.py # 主程序入口 ├── requirements.txt # 项目依赖列表 └── README.md # 项目说明你可以先创建好这个骨架,我们后续的代码将按此结构放置。
3. 核心爬虫逻辑拆解
爬虫的核心工作流程可以概括为:请求 -> 解析 -> 存储。我们一步步拆解。
3.1 发送请求与伪装
直接使用requests.get()访问豆瓣,很可能会收到403错误或验证码,因为豆瓣有反爬机制。我们需要模拟真实浏览器的行为。
关键点:请求头(Headers)最重要的字段是User-Agent,它告诉服务器我们使用的浏览器和操作系统信息。使用fake-useragent库可以随机生成。
# spider/utils.py from fake_useragent import UserAgent import time import random def get_random_headers(): """ 生成随机的请求头,重点模拟User-Agent。 """ ua = UserAgent() headers = { 'User-Agent': 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.9,en;q=0.8', 'Connection': 'keep-alive', } return headers def delay(): """ 在请求之间添加随机延时,避免请求过快。 """ time.sleep(random.uniform(1, 3)) # 随机休眠1到3秒3.2 解析HTML页面
豆瓣电影榜单页(例如TOP250)的HTML结构是固定的。我们需要使用开发者工具(F12)找到包含电影信息的HTML标签。
步骤:
- 打开豆瓣电影TOP250页面。
- 按F12打开开发者工具,使用元素选择工具(箭头图标)点击一个电影条目。
- 观察发现,每个电影条目都包裹在一个
class="item"的div标签内。 - 电影名称在
<span class="title">里,评分在<span class="rating_num">里,以此类推。
BeautifulSoup 解析示例:
# spider/douban_spider.py (片段) from bs4 import BeautifulSoup def parse_movie_list(html_content): """ 解析榜单页HTML,提取一页上的所有电影基本信息。 """ soup = BeautifulSoup(html_content, 'lxml') movie_items = soup.find_all('div', class_='item') # 找到所有电影条目 movies_on_page = [] for item in movie_items: movie = {} # 提取电影名称(可能包含中文名和英文名) title_elem = item.find('span', class_='title') movie['title'] = title_elem.get_text(strip=True) if title_elem else 'N/A' # 提取评分 rating_elem = item.find('span', class_='rating_num') movie['rating'] = float(rating_elem.get_text(strip=True)) if rating_elem else 0.0 # 提取评价人数 comment_elem = item.find('div', class_='star').find_all('span')[-1] # 最后一个span是人数 comment_text = comment_elem.get_text(strip=True).replace('人评价', '') movie['comment_count'] = int(comment_text) if comment_text.isdigit() else 0 # 提取简介(quote) quote_elem = item.find('span', class_='inq') movie['quote'] = quote_elem.get_text(strip=True) if quote_elem else '' # 提取详情页链接 link_elem = item.find('a') movie['detail_url'] = link_elem['href'] if link_elem else '' movies_on_page.append(movie) return movies_on_page3.3 处理分页与循环
豆瓣TOP250共有10页,每页25条。我们需要构建一个循环来遍历所有页面。观察URL规律:https://movie.douban.com/top250?start=0&filter=,start参数以25递增。
# spider/douban_spider.py (片段) import requests from .utils import get_random_headers, delay def crawl_top250(start_page=0, end_page=10): """ 爬取豆瓣电影TOP250从 start_page 到 end_page 的数据。 """ base_url = "https://movie.douban.com/top250" all_movies = [] for page in range(start_page, end_page): start = page * 25 url = f"{base_url}?start={start}&filter=" print(f"正在爬取第 {page+1} 页: {url}") try: headers = get_random_headers() response = requests.get(url, headers=headers, timeout=10) response.raise_for_status() # 检查请求是否成功 response.encoding = 'utf-8' # 调用解析函数 movies_on_page = parse_movie_list(response.text) all_movies.extend(movies_on_page) print(f"第 {page+1} 页爬取完成,共 {len(movies_on_page)} 部电影。") delay() # 爬取一页后延时 except requests.RequestException as e: print(f"爬取第 {page+1} 页时发生错误: {e}") break # 或者 continue,根据需求决定 return all_movies4. 完整实战:从爬取到可视化
现在,我们将所有模块组合起来,完成一个端到端的流程。
4.1 配置与主程序入口
首先,创建一个配置文件,管理爬取页数、数据库路径等参数。
# config.py # 爬虫配置 CRAWL_START_PAGE = 0 # 起始页(0代表第一页) CRAWL_END_PAGE = 10 # 结束页(10代表爬取10页,即TOP250) BASE_URL = "https://movie.douban.com/top250" # 数据库配置 (使用SQLite示例) DB_PATH = "data/douban_movies.db" TABLE_NAME = "movies_top250"然后,编写主程序main.py,协调爬虫、存储和可视化流程。
# main.py import os import sys from spider.douban_spider import crawl_top250 from data.db_handler import save_to_db, load_from_db from visualization.chart_generator import generate_all_charts import config def ensure_directories(): """确保必要的目录存在。""" os.makedirs('data/raw', exist_ok=True) os.makedirs('data/processed', exist_ok=True) os.makedirs('visualization/templates', exist_ok=True) def main(): print("豆瓣电影TOP250数据采集与可视化项目启动...") ensure_directories() # 阶段一:数据采集 print("\n=== 开始数据爬取 ===") movies_data = crawl_top250( start_page=config.CRAWL_START_PAGE, end_page=config.CRAWL_END_PAGE ) print(f"爬取结束,共获得 {len(movies_data)} 条电影数据。") if not movies_data: print("未获取到数据,程序退出。") sys.exit(1) # 阶段二:数据存储(数据库) print("\n=== 保存数据到数据库 ===") save_to_db(movies_data, config.DB_PATH, config.TABLE_NAME) # 阶段三:从数据库加载数据 print("\n=== 从数据库加载数据 ===") df = load_from_db(config.DB_PATH, config.TABLE_NAME) print(f"从数据库加载了 {len(df)} 条记录。") print(df.head()) # 预览前5条数据 # 阶段四:数据可视化 print("\n=== 生成可视化图表 ===") generate_all_charts(df) print("\n=== 项目执行完毕! ===") print("请查看 `visualization/templates/` 目录下的HTML文件,用浏览器打开查看交互式图表。") if __name__ == "__main__": main()4.2 数据存储模块(SQLite)
我们将数据存储到SQLite数据库,便于查询和管理。
# data/db_handler.py import sqlite3 import pandas as pd from typing import List, Dict def create_table(db_path: str, table_name: str): """创建电影数据表。""" conn = sqlite3.connect(db_path) cursor = conn.cursor() create_sql = f""" CREATE TABLE IF NOT EXISTS {table_name} ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, rating REAL, comment_count INTEGER, quote TEXT, detail_url TEXT, crawl_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) """ cursor.execute(create_sql) conn.commit() conn.close() print(f"表 `{table_name}` 创建成功或已存在。") def save_to_db(movies: List[Dict], db_path: str, table_name: str): """将电影数据列表保存到数据库。""" create_table(db_path, table_name) # 确保表存在 conn = sqlite3.connect(db_path) cursor = conn.cursor() insert_sql = f""" INSERT INTO {table_name} (title, rating, comment_count, quote, detail_url) VALUES (?, ?, ?, ?, ?) """ data_to_insert = [ (m['title'], m['rating'], m['comment_count'], m['quote'], m['detail_url']) for m in movies ] try: cursor.executemany(insert_sql, data_to_insert) conn.commit() print(f"成功插入 {cursor.rowcount} 条数据到表 `{table_name}`。") except sqlite3.Error as e: print(f"插入数据时发生错误: {e}") conn.rollback() finally: conn.close() def load_from_db(db_path: str, table_name: str) -> pd.DataFrame: """从数据库加载数据到Pandas DataFrame。""" conn = sqlite3.connect(db_path) query = f"SELECT * FROM {table_name}" df = pd.read_sql_query(query, conn) conn.close() return df4.3 数据清洗与预处理
从数据库加载数据后,通常需要进行简单的清洗。
# data/data_cleaner.py (可选,可在chart_generator中集成) import pandas as pd def clean_movie_data(df: pd.DataFrame) -> pd.DataFrame: """ 清洗电影数据。 """ df_clean = df.copy() # 1. 去重(基于电影标题和详情链接) df_clean = df_clean.drop_duplicates(subset=['title', 'detail_url']) # 2. 处理缺失值(评分用中位数填充,简介用空字符串填充) df_clean['rating'].fillna(df_clean['rating'].median(), inplace=True) df_clean['quote'].fillna('', inplace=True) # 3. 确保数据类型正确 df_clean['comment_count'] = pd.to_numeric(df_clean['comment_count'], errors='coerce').fillna(0).astype(int) print(f"数据清洗完成。原始记录数:{len(df)},清洗后记录数:{len(df_clean)}") return df_clean4.4 可视化图表生成
这是最有趣的部分!我们将使用pyecharts生成交互式图表。
# visualization/chart_generator.py import pandas as pd from pyecharts import options as opts from pyecharts.charts import Bar, Pie, Scatter, WordCloud, Page import os def generate_all_charts(df: pd.DataFrame): """生成所有可视化图表并保存为HTML。""" # 确保输出目录存在 output_dir = "visualization/templates" os.makedirs(output_dir, exist_ok=True) # 图表1:评分分布直方图(使用Matplotlib风格,Pyecharts实现) rating_bar = draw_rating_distribution(df) rating_bar.render(os.path.join(output_dir, "rating_distribution.html")) # 图表2:评分TOP10电影柱状图 top10_bar = draw_top10_movies(df) top10_bar.render(os.path.join(output_dir, "top10_movies.html")) # 图表3:评价人数与评分关系散点图 scatter_chart = draw_rating_vs_comments(df) scatter_chart.render(os.path.join(output_dir, "rating_vs_comments.html")) # 图表4:电影简介词云 wordcloud = draw_quote_wordcloud(df) wordcloud.render(os.path.join(output_dir, "quote_wordcloud.html")) # 图表5:将所有图表组合到一个页面 page = Page(layout=Page.SimplePageLayout) page.add( rating_bar, top10_bar, scatter_chart, wordcloud ) page.render(os.path.join(output_dir, "all_charts.html")) print(f"所有图表已生成,保存在 `{output_dir}` 目录下。") def draw_rating_distribution(df): """绘制电影评分分布直方图。""" # 将评分分段,例如 9.0以上,8.5-9.0,等等 bins = [0, 8.0, 8.5, 9.0, 9.5, 10] labels = ['8.0以下', '8.0-8.5', '8.5-9.0', '9.0-9.5', '9.5以上'] df['rating_group'] = pd.cut(df['rating'], bins=bins, labels=labels, right=False) rating_counts = df['rating_group'].value_counts().sort_index() bar = ( Bar() .add_xaxis(list(rating_counts.index)) .add_yaxis("电影数量", list(rating_counts.values)) .set_global_opts( title_opts=opts.TitleOpts(title="豆瓣TOP250电影评分分布", subtitle="数据来源:豆瓣电影"), xaxis_opts=opts.AxisOpts(name="评分区间"), yaxis_opts=opts.AxisOpts(name="电影数量"), toolbox_opts=opts.ToolboxOpts(), # 添加工具箱,可下载图片等 ) ) return bar def draw_top10_movies(df): """绘制评分最高的10部电影柱状图。""" top10 = df.nlargest(10, 'rating')[['title', 'rating']] # 简化标题,避免过长 top10['short_title'] = top10['title'].apply(lambda x: x[:15] + '...' if len(x) > 15 else x) bar = ( Bar() .add_xaxis(list(top10['short_title'])) .add_yaxis("评分", list(top10['rating'].round(2))) .reversal_axis() # 横向柱状图 .set_global_opts( title_opts=opts.TitleOpts(title="豆瓣TOP250评分前十电影"), xaxis_opts=opts.AxisOpts(name="评分"), yaxis_opts=opts.AxisOpts(name="电影名称"), visualmap_opts=opts.VisualMapOpts( min_=top10['rating'].min(), max_=top10['rating'].max(), dimension=0, # 对应Y轴数据 is_show=False, range_color=['#d7e3fc', '#1d4ed8'] # 渐变色 ), ) .set_series_opts( label_opts=opts.LabelOpts(position="right", formatter="{c}") # 在右侧显示数值 ) ) return bar def draw_rating_vs_comments(df): """绘制评分与评价人数关系的散点图。""" # 取评论数最多的前100部电影,避免图表过于密集 df_sample = df.nlargest(100, 'comment_count') data = list(zip(df_sample['comment_count'], df_sample['rating'])) scatter = ( Scatter() .add_xaxis(df_sample['comment_count'].tolist()) .add_yaxis( series_name="评分", y_axis=df_sample['rating'].tolist(), symbol_size=10, label_opts=opts.LabelOpts(is_show=False), ) .set_global_opts( title_opts=opts.TitleOpts(title="电影评分 vs 评价人数 (Top100 by 评论数)"), xaxis_opts=opts.AxisOpts(name="评价人数", type_="value"), yaxis_opts=opts.AxisOpts(name="评分"), tooltip_opts=opts.TooltipOpts( formatter="{b}万评价<br/>评分: {c}" ), ) ) return scatter def draw_quote_wordcloud(df): """根据电影简介生成词云。""" from collections import Counter import jieba # 需要安装:pip install jieba # 将所有简介拼接起来 text = ' '.join(df['quote'].dropna().astype(str).tolist()) # 使用jieba进行中文分词 words = jieba.lcut(text) # 过滤掉单字和停用词(这里简单过滤长度) filtered_words = [w for w in words if len(w) > 1] # 统计词频 word_counts = Counter(filtered_words).most_common(100) # 取前100个词 wordcloud = ( WordCloud() .add( series_name="电影简介词云", data_pair=word_counts, word_size_range=[20, 100], shape="circle", # 词云形状,可选circle, cardioid等 ) .set_global_opts( title_opts=opts.TitleOpts(title="豆瓣TOP250电影简介词云"), tooltip_opts=opts.TooltipOpts(is_show=True), ) ) return wordcloud4.5 运行与结果展示
- 运行程序:在项目根目录下执行
python main.py。 - 观察控制台:你会看到爬虫逐页爬取、数据存入数据库、图表生成的过程。
- 查看结果:
- 数据库文件:
data/douban_movies.db,可以用SQLite浏览器打开查看。 - 可视化图表:在
visualization/templates/目录下会生成多个HTML文件。用浏览器打开all_charts.html,你将看到一个包含多个交互式图表的网页。你可以鼠标悬停查看详情、缩放、保存图片。
- 数据库文件:
生成的图表示例:
- 评分分布:可以看到绝大多数TOP250电影评分集中在8.5-9.5之间。
- TOP10电影:直观展示评分最高的电影,如《肖申克的救赎》、《霸王别姬》等。
- 评分vs评价人数:观察是否“叫好又叫座”,有些高评分电影评价人数可能不多(文艺片),而一些商业大片评价人数多但评分中等。
- 简介词云:从电影简介中提取高频词汇,如“希望”、“人生”、“爱情”、“自由”等,感受TOP250电影的主题倾向。
5. 常见问题与排查思路
在爬虫开发过程中,你几乎一定会遇到一些问题。下面是一些常见问题及解决方法。
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 请求返回403 Forbidden | 1. 请求头User-Agent被识别为爬虫。2. IP被暂时封锁。 | 1. 使用fake-useragent随机化User-Agent。2. 在请求头中添加更多常见浏览器字段(如 Accept,Accept-Language)。3.显著增加请求间隔时间(例如 time.sleep(random.uniform(3, 7)))。4. 考虑使用代理IP池(高级话题,本项目未涉及)。 |
| 爬取几页后数据停止或报错 | 1. 触发了网站的反爬机制(如验证码)。 2. 网络连接不稳定。 3. HTML结构有微小变动。 | 1. 立即停止爬虫,手动访问页面看是否出现验证码。 2. 在代码中添加更完善的异常捕获和日志记录。 3. 检查解析逻辑是否还能匹配最新的HTML结构(使用开发者工具复查)。 4. 将爬虫任务分多次进行,每次爬取少量数据。 |
BeautifulSoup找不到标签,返回空列表 | 1. 使用的解析器不对。 2. 标签的 class名称有变化或包含多个类。3. 网页内容是JavaScript动态加载的。 | 1. 确保安装了lxml解析器 (pip install lxml)。2. 使用 soup.find_all(‘div‘, class_=‘item‘)时,如果class有多个值,可以用CSS选择器soup.select(‘div.item‘)。3. 对于动态加载的页面, requests无法获取JS渲染后的内容。此时需要考虑使用Selenium或Playwright等浏览器自动化工具。豆瓣榜单页是静态页,一般无此问题。 |
| 数据库写入错误 | 1. 数据库连接失败或路径错误。 2. 数据格式与表结构不匹配。 3. 主键或唯一约束冲突。 | 1. 检查db_path是否正确,是否有写入权限。2. 打印出准备插入的数据,检查是否有 None值或类型错误。3. 在插入前进行数据去重,或使用 INSERT OR IGNORE/REPLACE语句。 |
pyecharts图表不显示或报错 | 1. 版本不兼容。 2. 数据格式不正确(如非数值型数据用于坐标轴)。 3. 未正确安装或导入。 | 1. 使用pip list检查pyecharts版本,确保使用较新稳定版。2. 检查传递给图表的数据是否为列表(List)格式。 3. 确保生成了HTML文件,并用浏览器打开。在Jupyter Notebook中渲染需要调用 .render_notebook()。 |
| 词云图中文显示为方框 | 未指定中文字体路径。 | 在WordCloud的add方法中设置font_path参数,指向一个中文字体文件(如‘C:/Windows/Fonts/simhei.ttf‘)。 |
6. 最佳实践与进阶建议
掌握了基础流程后,以下建议能让你的爬虫项目更健壮、更专业。
6.1 爬虫工程化建议
- 配置化管理:将所有可配置项(如URL、请求头模板、数据库连接字符串、爬取间隔)放在
config.py或config.yaml中,与代码分离。 - 日志记录:使用Python内置的
logging模块替代print,可以输出不同级别(INFO, WARNING, ERROR)的日志到文件和控制台,方便后期排查。 - 异常处理与重试:为网络请求添加重试机制(如使用
tenacity库),对短暂网络波动容错。 - 增量爬取:在数据库表中增加
update_time字段。每次爬虫运行时,先检查detail_url是否存在,若存在则比较更新时间,决定是否更新数据,避免重复爬取。 - 任务队列与分布式:对于大规模爬取,可以考虑使用
Scrapy框架,它内置了请求调度、并发处理、管道等强大功能。更进一步,可以使用Redis作为任务队列,实现分布式爬虫。
6.2 数据存储优化
- 数据去重:除了在内存中去重,在数据库层面应设置唯一约束(如
(title, detail_url)),从根源避免重复数据。 - 数据备份:定期备份数据库文件。可以考虑将清洗后的数据额外导出为
CSV或Parquet格式,便于用其他工具(如Excel, Power BI)分析。 - 使用ORM:对于复杂项目,使用
SQLAlchemy等ORM库来管理数据库操作,可以写出更易维护、数据库无关的代码。
6.3 可视化进阶
- 使用更专业的可视化库:
Plotly和Bokeh也能生成交互式图表,且与Web框架(如Dash)集成更好。Seaborn是基于Matplotlib的统计图形库,默认样式更美观。 - 构建可视化仪表盘:使用
Dash(由Plotly开发)或Streamlit可以快速构建一个包含多个图表、筛选器的Web数据仪表盘,实现真正的“数据可视化大屏”。 - 探索更多图表类型:根据数据特点选择图表。例如,想展示电影类型分布,可以用饼图或旭日图;想展示电影随时间(上映年份)的评分变化,可以用折线图或面积图。
6.4 法律与伦理再强调
- 严格遵守
robots.txt:这是网络爬虫的“交通规则”。 - 控制访问速度:这是体现技术人素养的关键。你的爬虫不应该影响目标网站的正常服务。
- 明确数据用途:本项目代码及思路仅供学习交流。切勿用于任何侵犯版权、隐私或干扰网站运营的用途。
通过这个完整的项目,你不仅学会了如何用Python爬取豆瓣电影数据,还掌握了数据清洗、存储和可视化的全流程。这套方法可以举一反三,应用到其他许多公开数据源上。记住,爬虫技术是工具,关键在于用它来创造价值、解决问题,同时始终保持对规则和他人劳动的尊重。