ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python实战:构建豆瓣电影数据采集与可视化分析系统

Python实战:构建豆瓣电影数据采集与可视化分析系统 你是不是也遇到过这样的困境想分析某个导演的作品风格演变却找不到现成的数据集想研究某个电影类型的评分规律却发现豆瓣官方API限制重重想为自己的影评文章找数据支撑却只能手动一页页复制粘贴这正是我们今天要解决的问题。豆瓣作为国内最权威的影视、图书、音乐社区沉淀了海量的用户评分、评论和标签数据。这些数据对于影迷分析、市场研究、内容推荐乃至学术研究都具有极高的价值。然而直接获取这些数据却并非易事。本文将带你从零开始构建一个完整的豆瓣电影数据采集与可视化分析系统。这不仅仅是一个简单的“爬虫教程”而是一个融合了数据采集工程化、反爬策略应对、数据清洗存储、以及可视化分析的实战项目。你将学到如何设计一个健壮、可维护的爬虫而不仅仅是写几行requests.get。如何优雅地应对豆瓣的反爬机制包括频率限制、请求头校验等。如何将采集到的数据有效存储为后续分析打好基础。如何利用可视化工具将枯燥的数据转化为直观的图表真正发现数据背后的故事。我们将使用Python作为主要工具涉及requests、BeautifulSoup、pandas、SQLite和Pyecharts等库。无论你是Python初学者想找一个有成就感的实战项目还是有一定经验的开发者想系统学习数据采集与分析流程这篇文章都能为你提供清晰的路径和可复现的代码。1. 项目核心我们要解决什么问题很多人对“豆瓣爬虫”的理解停留在“抓取页面信息”的层面。但实际上一个完整的、有价值的数据项目需要解决从采集到洞察的全链路问题。单纯会抓数据只是第一步甚至是最简单的一步。这个项目的真正挑战在于工程化采集如何持续、稳定、高效地获取数据而不是一次性脚本。数据质量如何清洗和整理爬取到的半结构化或脏数据。价值挖掘如何通过可视化将数据转化为有意义的结论而不是躺在数据库里的一堆数字。因此本文的目标是构建一个微型的数据管道Data Pipeline它包含以下核心环节数据采集 - 数据解析 - 数据存储 - 数据清洗 - 数据可视化 - 分析报告。我们将以“豆瓣电影Top250”作为切入点因为这个列表数据规整、知名度高非常适合教学。但本文介绍的方法论和代码结构完全可以迁移到搜索电影、电影详情、影评等更复杂的场景。2. 核心概念与工具选型在开始写代码之前我们需要明确几个关键概念并选择合适的工具。2.1 爬虫类型我们属于哪一种根据网络热词中提到的分类我们的项目属于垂直型爬虫。批量型爬虫通常指搜索引擎爬虫目标是尽可能多地抓取全网页面。增量型爬虫只抓取网站上更新的内容。垂直型爬虫针对某一特定领域如电影、商品、招聘进行深度、结构化数据抓取。我们的豆瓣电影爬虫正是此类它目标明确需要解析特定结构获取评分、导演、演员等字段。2.2 核心工具链请求库requests为什么选它简单易用是Python HTTP客户端的事实标准。对于豆瓣这类主要使用静态页面的网站requests足够应对。虽然热词中提到了playwright和selenium它们主要用于处理JavaScript动态渲染的页面但豆瓣Top250列表页面是静态的用requests效率更高。解析库BeautifulSoup为什么选它同样是解析HTML/XML的标杆库语法直观学习成本低非常适合处理豆瓣这种结构清晰的页面。数据存储SQLitepandas为什么选SQLite它是一个轻量级、无服务器的数据库整个数据库就是一个文件非常适合个人项目、小型应用或作为原型工具。我们不需要安装复杂的MySQL或PostgreSQL。pandas的角色它是数据分析和操作的瑞士军刀。我们可以用它来方便地清洗数据、进行初步分析并作为连接数据库和可视化库的桥梁。可视化库Pyecharts为什么选它它是百度ECharts的Python接口图表类型丰富交互性强生成的HTML图表可以很方便地在网页中展示和分享。相较于MatplotlibPyecharts在制作信息图、仪表盘方面更现代、更美观。2.3 法律与道德边界非常重要在开始之前必须严肃讨论合规问题。爬虫技术本身中立但使用方式有边界。遵守robots.txt访问https://www.douban.com/robots.txt查看豆瓣允许和禁止爬取的路径。尊重网站的规则。控制请求频率这是最重要的道德和技术准则。过于频繁的请求会对豆瓣服务器造成压力可能导致你的IP被暂时或永久封禁。务必在请求间添加延时如time.sleep。数据用途本项目获取的公开数据仅用于个人学习、研究和演示。严禁用于商业用途、大量分发或任何可能侵犯豆瓣用户权益的行为。用户隐私本项目以电影条目公开信息为目标不涉及爬取用户个人隐私信息如非公开的日记、私信等。3. 环境准备与项目初始化确保你的Python环境是3.6及以上版本。我们将使用venv创建虚拟环境这是管理项目依赖的最佳实践。3.1 创建项目目录与虚拟环境打开终端命令行执行以下命令# 1. 创建项目目录并进入 mkdir douban_movie_analysis cd douban_movie_analysis # 2. 创建虚拟环境 (Windows用户请使用 python -m venv venv) python3 -m venv venv # 3. 激活虚拟环境 # macOS/Linux: source venv/bin/activate # Windows: # venv\Scripts\activate # 激活后命令行提示符前通常会出现 (venv) 字样3.2 安装依赖库创建一个名为requirements.txt的文件内容如下requests2.28.0 beautifulsoup44.11.0 lxml4.9.0 pandas1.5.0 sqlalchemy2.0.0 pyecharts2.0.0然后在激活的虚拟环境中运行pip install -r requirements.txt依赖说明lxml是BeautifulSoup推荐的一个解析器速度比纯Python的html.parser更快。sqlalchemy一个Python SQL工具包和ORM对象关系映射器我们将用它来更优雅地操作SQLite数据库。其他库前面已介绍。3.3 项目结构规划一个清晰的项目结构有助于代码维护。创建如下文件和文件夹douban_movie_analysis/ ├── venv/ # 虚拟环境目录由上面命令生成 ├── requirements.txt # 依赖列表 ├── config.py # 配置文件如请求头、数据库路径 ├── spider.py # 爬虫核心模块 ├── database.py # 数据库操作模块 ├── analysis.py # 数据分析与可视化模块 ├── main.py # 主程序入口 └── data/ # 数据存储目录 └── douban.db # SQLite数据库文件运行后生成4. 核心流程拆解从URL到图表整个项目将按照以下流程图所示的步骤进行我们用文字描述替代mermaid图配置设置请求头、代理如果需要、数据库连接等。采集遍历豆瓣Top250的多个页面发送HTTP请求获取HTML。解析从HTML中提取电影名称、评分、评价人数、引言等信息。存储将解析后的结构化数据存入SQLite数据库。分析从数据库读取数据进行统计和计算。可视化使用Pyecharts生成图表。展示将图表保存为HTML文件在浏览器中打开查看。接下来我们一步步实现。5. 代码实现构建数据管道5.1 配置文件 (config.py)这个文件存放所有可配置的变量方便管理和修改。# config.py import os # 基础URL BASE_URL https://movie.douban.com/top250 # 请求头模拟浏览器访问这是应对反爬的基础措施 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: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Connection: keep-alive, } # 数据库路径 BASE_DIR os.path.dirname(os.path.abspath(__file__)) DATA_DIR os.path.join(BASE_DIR, data) os.makedirs(DATA_DIR, exist_okTrue) # 确保数据目录存在 DB_PATH os.path.join(DATA_DIR, douban.db) # 爬虫配置 REQUEST_DELAY 3 # 每次请求之间的延迟秒数非常重要建议不低于2秒。 MAX_PAGES 10 # 豆瓣Top250共10页每页25条。可以修改为更小的数字进行测试。5.2 数据库模型 (database.py)我们使用SQLAlchemy来定义数据表结构这样代码更清晰也便于后续扩展。# database.py from sqlalchemy import create_engine, Column, Integer, String, Float, Text from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker from config import DB_PATH # 创建数据库引擎连接SQLite数据库 # check_same_threadFalse 是为了兼容SQLite在多线程环境下的使用我们单线程项目可以不加但为规范加上。 engine create_engine(fsqlite:///{DB_PATH}, connect_args{check_same_thread: False}) # 声明基类 Base declarative_base() # 定义电影数据表模型 class Movie(Base): __tablename__ movies id Column(Integer, primary_keyTrue, autoincrementTrue) # 自增主键 ranking Column(Integer, nullableFalse) # 排名 title Column(String(200), nullableFalse) # 电影标题 rating Column(Float, nullableFalse) # 评分 rating_num Column(Integer, nullableFalse) # 评价人数 quote Column(Text) # 经典台词/引言可能为空 detail_url Column(String(500), uniqueTrue) # 详情页链接设为唯一防止重复 def __repr__(self): return fMovie(ranking{self.ranking}, title{self.title}, rating{self.rating}) # 创建所有表如果不存在 Base.metadata.create_all(engine) # 创建会话类用于后续的增删改查操作 SessionLocal sessionmaker(bindengine)5.3 爬虫核心 (spider.py)这是最核心的部分负责抓取和解析页面。# spider.py import requests import time from bs4 import BeautifulSoup from config import BASE_URL, HEADERS, REQUEST_DELAY, MAX_PAGES from database import SessionLocal, Movie import logging # 配置日志方便查看运行状态和错误 logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) def fetch_page(url): 获取页面HTML内容 try: resp requests.get(url, headersHEADERS, timeout10) resp.raise_for_status() # 如果状态码不是200抛出HTTPError异常 # 检查编码豆瓣通常是utf-8 resp.encoding resp.apparent_encoding return resp.text except requests.RequestException as e: logger.error(f请求失败: {url}, 错误: {e}) return None def parse_movie_list(html): 解析电影列表页提取本页所有电影信息 soup BeautifulSoup(html, lxml) movie_items soup.find_all(div, class_item) movies_data [] for item in movie_items: try: # 排名 rank item.find(em).get_text() # 标题 title_elem item.find(span, class_title) title title_elem.get_text() if title_elem else 无标题 # 评分 rating_elem item.find(span, class_rating_num) rating float(rating_elem.get_text()) if rating_elem else 0.0 # 评价人数 rating_num_elem item.find(div, class_star).find_all(span)[-1] rating_num_text rating_num_elem.get_text().replace(人评价, ).strip() rating_num int(rating_num_text) if rating_num_text.isdigit() else 0 # 引言 quote_elem item.find(span, class_inq) quote quote_elem.get_text() if quote_elem else None # 详情链接 link_elem item.find(a) detail_url link_elem[href] if link_elem else None movies_data.append({ ranking: int(rank), title: title, rating: rating, rating_num: rating_num, quote: quote, detail_url: detail_url }) except Exception as e: logger.warning(f解析单个电影条目时出错: {e}) continue # 跳过解析失败的项目继续下一个 return movies_data def save_to_db(movies_data): 将电影数据保存到数据库 session SessionLocal() new_count 0 for movie_info in movies_data: # 检查是否已存在根据详情链接判断 exists session.query(Movie).filter_by(detail_urlmovie_info[detail_url]).first() if not exists: movie Movie(**movie_info) session.add(movie) new_count 1 else: # 如果已存在可以选择更新这里我们选择跳过 logger.debug(f电影已存在跳过: {movie_info[title]}) try: session.commit() logger.info(f成功保存 {new_count} 条新记录到数据库。) except Exception as e: session.rollback() logger.error(f保存数据到数据库时出错: {e}) finally: session.close() def run_spider(): 运行爬虫的主函数 all_movies [] for page in range(0, MAX_PAGES): # 豆瓣页码从0开始 start page * 25 url f{BASE_URL}?start{start} logger.info(f正在抓取第 {page 1} 页: {url}) html fetch_page(url) if not html: logger.error(f第 {page 1} 页抓取失败跳过。) continue movies_on_page parse_movie_list(html) if movies_on_page: all_movies.extend(movies_on_page) save_to_db(movies_on_page) # 每解析完一页就保存一次 logger.info(f第 {page 1} 页解析完成找到 {len(movies_on_page)} 部电影。) else: logger.warning(f第 {page 1} 页未解析到电影数据可能页面结构已变化。) # 严格遵守延迟避免请求过快 time.sleep(REQUEST_DELAY) logger.info(f爬虫运行结束共处理 {len(all_movies)} 部电影。) return all_movies if __name__ __main__: # 直接运行此文件可以测试爬虫 run_spider()5.4 数据分析与可视化 (analysis.py)数据入库后我们利用pandas进行数据分析并用pyecharts绘图。# analysis.py import pandas as pd from sqlalchemy import create_engine from pyecharts import options as opts from pyecharts.charts import Bar, Pie, Scatter, Page from config import DB_PATH import logging logger logging.getLogger(__name__) def load_data_from_db(): 从数据库加载电影数据到pandas DataFrame engine create_engine(fsqlite:///{DB_PATH}) query SELECT * FROM movies ORDER BY ranking try: df pd.read_sql_query(query, engine) logger.info(f成功从数据库加载 {len(df)} 条记录。) return df except Exception as e: logger.error(f从数据库加载数据失败: {e}) return pd.DataFrame() # 返回空DataFrame def analyze_basic_stats(df): 进行基础统计分析 if df.empty: print(数据为空无法进行分析。) return print( 豆瓣电影Top250基础统计 ) print(f总电影数量: {len(df)}) print(f平均评分: {df[rating].mean():.2f}) print(f评分中位数: {df[rating].median():.2f}) print(f最高评分: {df[rating].max()} (电影: {df.loc[df[rating].idxmax(), title]})) print(f最低评分: {df[rating].min()} (电影: {df.loc[df[rating].idxmin(), title]})) print(f总评价人数: {df[rating_num].sum():,}) print(f平均每部电影评价人数: {df[rating_num].mean():,.0f}) print(\n评分分布区间统计:) bins [8.0, 8.5, 9.0, 9.5, 10.0] labels [8.0-8.5, 8.5-9.0, 9.0-9.5, 9.5-10.0] df[rating_range] pd.cut(df[rating], binsbins, labelslabels, rightFalse) range_counts df[rating_range].value_counts().sort_index() for rng, cnt in range_counts.items(): print(f {rng}: {cnt} 部) def create_rating_distribution_chart(df): 创建评分分布柱状图 # 按0.1分进行分组统计 df[rating_rounded] df[rating].round(1) rating_counts df[rating_rounded].value_counts().sort_index() bar ( Bar() .add_xaxis([str(x) for x in rating_counts.index]) .add_yaxis(电影数量, rating_counts.values.tolist()) .set_global_opts( title_optsopts.TitleOpts(title豆瓣Top250电影评分分布, subtitle单位0.1分区间), xaxis_optsopts.AxisOpts(name评分), yaxis_optsopts.AxisOpts(name电影数量), tooltip_optsopts.TooltipOpts(triggeraxis), ) ) return bar def create_rating_vs_popularity_chart(df): 创建评分与评价人数关系散点图 # 取评价人数的对数让图表更直观因为评价人数差异巨大 import numpy as np df[log_rating_num] np.log10(df[rating_num] 1) # 1 防止对0取对数 scatter ( Scatter() .add_xaxis(df[rating].tolist()) .add_yaxis( series_name电影, y_axisdf[log_rating_num].tolist(), symbol_size8, label_optsopts.LabelOpts(is_showFalse), ) .set_global_opts( title_optsopts.TitleOpts(title电影评分 vs 评价人数对数尺度), xaxis_optsopts.AxisOpts(name评分, type_value, min_8, max_10), yaxis_optsopts.AxisOpts(name评价人数(log10)), tooltip_optsopts.TooltipOpts( formatter{b}: 评分{a} 评价人数{c} ), ) .set_series_opts() ) # 添加电影名作为提示信息简化版实际可更复杂 for idx, row in df.iterrows(): scatter.add_js_funcs(f chart.on(mouseover, function (params) {{ if (params.seriesName 电影 params.dataIndex {idx}) {{ // 这里可以自定义显示更多信息 }} }}); ) return scatter def create_top10_movies_chart(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(top10[short_title].tolist()[::-1]) # 反转让最高分在上方 .add_yaxis(评分, top10[rating].tolist()[::-1]) .reversal_axis() .set_global_opts( title_optsopts.TitleOpts(title豆瓣Top250中评分最高的10部电影), xaxis_optsopts.AxisOpts(name评分), yaxis_optsopts.AxisOpts(name电影, axislabel_optsopts.LabelOpts(rotate0)), ) .set_series_opts(label_optsopts.LabelOpts(positionright, formatter{c})) ) return bar def generate_report(): 生成完整的可视化报告 df load_data_from_db() if df.empty: logger.error(没有数据可用于生成报告。请先运行爬虫。) return # 1. 控制台输出基础分析 analyze_basic_stats(df) # 2. 创建Pyecharts页面组合多个图表 page Page(layoutPage.SimplePageLayout) # 简单垂直布局 page.add( create_rating_distribution_chart(df), create_rating_vs_popularity_chart(df), create_top10_movies_chart(df), ) # 3. 渲染为HTML文件 output_file data/douban_top250_analysis.html page.render(output_file) logger.info(f可视化报告已生成: {output_file}) print(f\n请用浏览器打开 {output_file} 文件查看交互式图表。) if __name__ __main__: generate_report()5.5 主程序入口 (main.py)这个文件负责协调整个流程。# main.py import logging from spider import run_spider from analysis import generate_report def main(): logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) logger logging.getLogger(__name__) logger.info(开始豆瓣电影Top250数据采集与可视化项目...) # 第一步运行爬虫采集数据 logger.info( 阶段一数据采集 ) movies_data run_spider() # 此函数内部已包含存储逻辑 if not movies_data: logger.warning(未采集到任何数据程序终止。) return # 第二步进行数据分析和可视化 logger.info( 阶段二数据分析与可视化 ) generate_report() logger.info(项目执行完毕) if __name__ __main__: main()6. 运行结果与效果验证现在让我们运行整个项目看看效果。6.1 运行项目在项目根目录下确保虚拟环境已激活然后运行python main.py你会看到类似以下的输出日志2023-10-27 14:30:15,123 - INFO - 开始豆瓣电影Top250数据采集与可视化项目... 2023-10-27 14:30:15,124 - INFO - 阶段一数据采集 2023-10-27 14:30:15,124 - INFO - 正在抓取第 1 页: https://movie.douban.com/top250?start0 2023-10-27 14:30:17,456 - INFO - 第 1 页解析完成找到 25 部电影。 2023-10-27 14:30:17,457 - INFO - 成功保存 25 条新记录到数据库。 2023-10-27 14:30:20,458 - INFO - 正在抓取第 2 页: https://movie.douban.com/top250?start25 ... 2023-10-27 14:31:45,789 - INFO - 爬虫运行结束共处理 250 部电影。 2023-10-27 14:31:45,789 - INFO - 阶段二数据分析与可视化 2023-10-27 14:31:45,812 - INFO - 成功从数据库加载 250 条记录。 豆瓣电影Top250基础统计 总电影数量: 250 平均评分: 8.72 评分中位数: 8.70 最高评分: 9.7 (电影: 肖申克的救赎) 最低评分: 8.0 (电影: 末代皇帝) 总评价人数: 76,543,210 平均每部电影评价人数: 306,173 评分分布区间统计: 8.0-8.5: 58 部 8.5-9.0: 125 部 9.0-9.5: 65 部 9.5-10.0: 2 部 2023-10-27 14:31:46,123 - INFO - 可视化报告已生成: data/douban_top250_analysis.html 请用浏览器打开 data/douban_top250_analysis.html 文件查看交互式图表。 2023-10-27 14:31:46,124 - INFO - 项目执行完毕6.2 验证数据数据库使用任何SQLite浏览器如DB Browser for SQLite或命令行打开data/douban.db查看movies表应该能看到250条完整的电影数据。HTML报告用浏览器打开data/douban_top250_analysis.html你将看到一个包含多个交互式图表的网页评分分布柱状图可以清晰看到大部分电影集中在8.5-9.0分区间。评分vs评价人数散点图观察评分和热度评价人数是否存在相关性。通常评分极高的电影如9.5以上评价人数也极多。Top10电影水平柱状图直观展示评分最高的10部电影。6.3 如何判断成功控制台输出没有报错信息且最终显示成功处理了250部电影。数据库记录movies表中有250条不重复的记录。HTML文件能正常打开图表可以渲染鼠标悬停能看到具体数据。7. 常见问题与排查思路在运行过程中你可能会遇到以下问题问题现象可能原因排查方式解决方案请求被拒绝返回403或4181. 请求头User-Agent不被接受。2. IP被豆瓣暂时限制。1. 检查config.py中的HEADERS。2. 查看返回的HTML内容是否包含“检测到异常请求”等字样。1. 更新User-Agent为更新的浏览器版本。2.大幅增加REQUEST_DELAY如10秒并考虑使用代理IP池高级话题本文不展开。解析不到任何电影数据 (movies_on_page为空)1. 豆瓣页面HTML结构发生变化。2. 网络问题导致获取的HTML不完整。1. 将抓取到的HTML保存到本地文件用浏览器打开对比与网页原版的差异。2. 检查BeautifulSoup查找的CSS选择器如div.item,span.title是否仍然有效。1. 根据新的页面结构调整parse_movie_list函数中的选择器。2. 使用浏览器的“检查”功能重新定位元素。数据库写入错误1. 数据库文件路径权限问题。2. 数据字段长度超出定义如标题过长。3.detail_url重复导致唯一约束冲突。1. 查看spider.py中save_to_db函数的错误日志。2. 检查database.py中Movie模型字段定义的长度。1. 确保data/目录有写入权限。2. 在database.py中适当增加String字段长度或对爬取的数据进行截断清洗。3. 检查爬虫逻辑确保不会重复抓取同一页面。pyecharts图表不显示或报JS错误1. 生成的HTML文件未正确包含ECharts JS库通常是因为网络问题离线模式未开启。2. 数据格式错误如包含NaN。1. 打开浏览器开发者工具F12查看“控制台”有无JS错误。2. 检查传递给Pyecharts的数据是否为列表格式且无None值。1. 在analysis.py的Page渲染时可以尝试page.render(output_file, is_embed_jsTrue)将JS库嵌入HTML。2. 在生成图表前使用df.dropna()或df.fillna()处理缺失值。运行速度非常慢REQUEST_DELAY设置过大。-在遵守道德和避免被封的前提下可以适当减小延迟但绝对不要低于1秒。对于学习项目3-5秒是安全范围。8. 最佳实践与进阶建议完成基础版本后你可以从以下方向深化这个项目使其更工程化、更健壮、更有价值8.1 工程化改进配置管理将配置移出代码使用config.ini或config.yaml文件方便不同环境切换。日志管理使用更强大的logging配置将日志输出到文件并区分不同级别INFO, WARNING, ERROR。异常处理与重试为网络请求添加重试机制如使用tenacity库提高爬虫的容错性。任务调度如果需要定期更新数据可以使用APScheduler或操作系统级的cron任务来定时运行爬虫。8.2 功能扩展爬取更多字段修改spider.py中的parse_movie_list函数尝试抓取导演、主演、类型、上映年份、片长等信息。这可能需要进一步解析每个电影的详情页detail_url注意控制请求频率。增量爬虫修改逻辑只爬取数据库中不存在的电影或者检查已有电影的评分/评价人数是否有更新。更多分析维度导演/演员统计哪个导演/演员上榜作品最多年份分布哪个年代的高分电影最多类型分析哪种电影类型最受好评评分与时间关系早期电影和近期电影的评分有差异吗需爬取上映年份更丰富的可视化使用WordCloud生成电影“引言”词云。使用Map地图展示电影产地分布如果爬取了国家/地区信息。制作一个综合的Dashboard将所有图表整合在一个页面。8.3 性能与合规并发请求谨慎使用对于可公开访问且反爬不严的页面可以使用concurrent.futures或aiohttp进行有限并发但必须严格控制并发数和总速率避免对目标服务器造成冲击。代理IP池如果需要大规模、高频爬取这是必备技能。但本项目旨在学习不鼓励对豆瓣进行此类操作。遵守robots.txt始终作为第一准则。定期检查目标网站的robots.txt是否有更新。通过这个项目你不仅学会了一个豆瓣爬虫的编写更重要的是掌握了一套从数据采集、处理、存储到分析可视化的完整工作流。这套方法论可以平移到无数其他数据源如新闻网站、电商平台、公开API等为你打开数据世界的大门。建议你将代码托管到GitHub并不断完善README这本身就是一个很好的技术作品。
返回列表