资讯详情

资讯详情

基于Scrapy框架构建豆瓣电影数据采集系统:从爬虫到数据库存储的完整实践

简介这是一套基于Python Scrapy框架实现的豆瓣电影TOP250数据采集系统面向Python初学者与网络爬虫入门学习者解决结构化影视数据获取、Scrapy项目工程化搭建及MySQL数据持久化等典型实践问题。资源包共26个文件含8个核心Python源码如Spider主爬虫、Items定义、Pipelines管道、Settings配置、5个编译字节码文件pyc、8个IDE配置与项目元数据XML文件以及关键的Movie.sql建表语句和scrapy.cfg项目配置整体仅14KB轻量易读。已有1757人学习下载适合快速理解Scrapy请求调度、XPath解析、分页翻页逻辑、数据清洗与MySQL入库全流程。读者可直接运行完整项目获得包含片名、评分、简介、经典台词等字段的结构化电影数据并通过SQL脚本一键初始化数据库是掌握工业级爬虫开发范式的优质入门范例。1. 项目概述从零构建一个健壮的豆瓣电影数据采集系统最近在整理个人项目库翻到了一个几年前写的豆瓣电影爬虫当时是为了做电影推荐系统的数据准备。重新审视和优化后我觉得这个基于Scrapy框架的完整爬虫系统对于想学习Python爬虫、数据采集以及后端数据处理的开发者来说是一个非常好的练手项目。它不仅仅是一个简单的脚本而是一个包含完整数据流采集、清洗、存储和工程化考虑反爬策略、异常处理、代码结构的系统。很多新手在学爬虫时往往只停留在获取网页内容的层面对于如何将零散的数据结构化、持久化以及如何让爬虫长期稳定运行缺乏经验。这个项目正好可以填补这块空白。这个系统能做什么简单说它能自动化地从豆瓣电影Top250、正在热映、即将上映等页面抓取电影的名称、导演、演员、评分、评价人数、简介、标签等核心信息并将这些数据清洗后存入你指定的数据库如MySQL、SQLite。你拿到的不再是一堆杂乱的文本文件而是一个可以直接用于数据分析、可视化或推荐算法训练的、结构良好的数据库。无论你是想学习Scrapy框架的实战应用还是需要一个电影数据集来做数据分析项目或者想了解如何设计一个可维护的爬虫系统这个项目都能提供直接的参考。2. 核心需求与系统设计思路拆解2.1 为什么选择Scrapy而不是Requests很多Python爬虫入门教程都是从Requests库开始的它简单直接对于小规模、简单的页面抓取非常有效。但当我们面对像豆瓣电影这样具有一定反爬措施、需要采集多个关联页面、并且希望代码易于维护和扩展的场景时Scrapy框架的优势就凸显出来了。Scrapy是一个为爬取网站数据、提取结构化数据而设计的应用程序框架。它内置了异步处理、请求调度、数据管道Item Pipeline、中间件等组件。这意味着你不用从零开始写网络请求队列、不用自己处理复杂的异常重试逻辑、也不用手动管理数据的清洗和存储流程。框架已经为你搭好了舞台你只需要专注于编写爬取规则Spider和数据提取逻辑Selectors。对于豆瓣电影这种需要翻页、需要进入详情页抓取更多信息的任务Scrapy的CrawlSpider配合LinkExtractor可以非常优雅地实现代码简洁且高效。另一个关键点是工程化。用Requests写脚本通常所有逻辑都堆在一个文件里随着功能增加代码会变得难以阅读和维护。而Scrapy的项目结构是标准化的spiders,items.py,pipelines.py,middlewares.py,settings.py天然支持模块化开发。数据定义、爬虫逻辑、数据处理、配置相互分离这不仅让代码更清晰也便于团队协作和后期功能扩展。2.2 系统架构与数据流设计一个健壮的爬虫系统其核心设计思想应该是“高内聚、低耦合”。我们将整个数据采集流程分解为几个独立的阶段每个阶段负责单一职责。调度与下载器Scrapy Engine Downloader这是Scrapy框架的核心。引擎负责控制所有模块的数据流根据调度器的请求调用下载器去互联网抓取网页。我们需要在settings.py中配置下载延迟、并发数、User-Agent池等参数以模拟人类行为应对反爬。爬虫模块Spiders这是我们的业务逻辑核心。在这里我们定义起始URL如豆瓣电影Top250列表页并编写解析函数。解析函数有两个主要任务一是使用XPath或CSS选择器从当前页面提取我们需要的数据即生成Item二是发现新的链接如“下一页”的链接或每个电影的详情页链接并生成新的Request对象交给引擎去调度。对于豆瓣电影我们通常需要两级抓取先抓列表页获取电影基本信息和详情页链接再进入详情页抓取更丰富的信息如剧情简介、更多演员、标签等。数据管道Item Pipeline爬虫提取出的原始数据Item会依次通过多个配置好的管道进行处理。这是进行数据清洗和持久化的关键环节。典型的管道包括数据清洗管道检查字段是否为空、去除字符串首尾空格、统一日期格式、处理特殊字符等。去重管道基于电影ID或URL确保同一条数据不会被重复存储。我们可以利用Scrapy内置的dupefilter也可以在管道中自己实现基于数据库的判重逻辑。数据库存储管道这是本项目的重点。我们将清洗后的数据通过pymysql或sqlite3等库写入到MySQL或SQLite数据库中。这里需要设计合理的数据库表结构。数据存储层Database我们选择关系型数据库如MySQL来存储数据主要是因为电影数据是高度结构化的并且后续可能需要进行复杂的查询和分析例如“找出评分高于8.5分的喜剧片”。在项目附带的SQL文件中会包含创建数据库和表的语句。一个基本的movies表可能包含以下字段id主键可使用豆瓣电影ID、title、directors、actors、rating、rating_count、genres、pubdate、summary等。整个数据流就像一条流水线URL经过调度进入下载器下载的网页交给爬虫解析解析出的数据项流入管道进行清洗和存储最终整齐地码放在数据库的货架上。这种设计使得任何一个环节出问题如下载失败、页面结构变化都容易定位和修复而不会影响其他环节。3. 环境准备与项目初始化3.1 Python与依赖库安装首先确保你安装了Python 3.6或更高版本。可以通过命令行输入python --version来检查。接下来我们需要安装Scrapy框架和数据库驱动。强烈建议使用虚拟环境来管理项目依赖这可以避免不同项目间的库版本冲突。使用venv创建虚拟环境# 在项目目录下 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Linux/Mac: source venv/bin/activate激活虚拟环境后命令行提示符前通常会显示(venv)。接下来安装核心依赖pip install scrapyScrapy是核心框架。根据你选择的数据库安装相应的驱动# 如果使用MySQL pip install pymysql # 如果使用SQLitePython标准库自带无需额外安装 # 如果需要更强大的连接池等功能也可以安装 # pip install scrapy-redis # 用于分布式爬虫非必需注意事项安装pymysql时如果遇到关于Microsoft Visual C的错误通常是因为缺少编译环境。最简单的解决办法是访问[一个非官方的Windows二进制包网站]下载对应Python版本和系统位数的pymysql的.whl文件然后通过pip install 文件名.whl进行安装。对于初学者如果觉得MySQL配置麻烦可以优先使用SQLite它无需安装服务器所有数据存储在一个本地文件中非常适合学习和测试。3.2 创建Scrapy项目与基础结构在项目目录下使用Scrapy命令行工具创建项目骨架scrapy startproject douban_movie cd douban_movie这会生成一个标准的Scrapy项目目录结构douban_movie/ ├── scrapy.cfg # 项目部署配置文件 └── douban_movie/ # 项目Python模块 ├── __init__.py ├── items.py # 定义数据模型Item ├── middlewares.py # 自定义中间件如代理、User-Agent ├── pipelines.py # 数据管道用于清洗和存储 ├── settings.py # 项目全局设置 └── spiders/ # 爬虫文件存放目录 └── __init__.py这个结构是我们所有代码的容器。items.py定义了我们要抓取的数据字段pipelines.py是数据处理的流水线settings.py是控制爬虫行为的中枢而spiders目录下则是我们编写具体抓取逻辑的地方。3.3 数据库设计与SQL文件解析在开始写爬虫代码前我们先设计好数据的归宿。这里以MySQL为例提供一个简单的表结构设计。项目附带的SQL文件init_database.sql通常包含如下内容-- 创建数据库 CREATE DATABASE IF NOT EXISTS douban_movie DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE douban_movie; -- 创建电影信息表 CREATE TABLE IF NOT EXISTS movies ( id int(11) NOT NULL COMMENT 豆瓣电影ID作为主键, title varchar(255) NOT NULL COMMENT 电影标题, directors varchar(500) DEFAULT NULL COMMENT 导演多个用/分隔, actors text COMMENT 主演多个用/分隔, rating decimal(3,1) DEFAULT NULL COMMENT 评分如9.2, rating_count int(11) DEFAULT NULL COMMENT 评分人数, genres varchar(255) DEFAULT NULL COMMENT 类型多个用/分隔, pubdate varchar(100) DEFAULT NULL COMMENT 上映日期可能包含多个地区, summary text COMMENT 剧情简介, tags varchar(500) DEFAULT NULL COMMENT 用户标签多个用/分隔, cover_url varchar(500) DEFAULT NULL COMMENT 封面图片URL, detail_url varchar(500) DEFAULT NULL COMMENT 豆瓣详情页URL, create_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 记录创建时间, PRIMARY KEY (id), KEY idx_rating (rating), KEY idx_genres (genres(191)) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci COMMENT电影信息表;设计思路解析id使用豆瓣电影自身的ID如1292052对应《肖申克的救赎》具有唯一性适合作为主键。这比自增ID更有业务意义也便于后续根据ID更新数据。directors,actors,genres,tags这些字段在豆瓣页面上通常是多个值。我们选择用特定分隔符如/将它们拼接成一个字符串存储而不是设计多个关联表。这样做的好处是简单、查询方便例如用LIKE %喜剧%查找喜剧片缺点是违反了数据库第一范式且无法高效地进行多值查询如“找出同时是喜剧和爱情的电影”。对于这个爬虫项目的目标——数据采集和初步分析这种设计是权衡之下的合理选择。如果后续需要做复杂的关联分析可以再将其规范化。rating: 使用DECIMAL(3,1)类型可以精确存储一位小数的评分。pubdate存储为字符串因为豆瓣的上映日期格式不统一可能包含“2023-05-01(中国大陆)”这样的信息。create_time记录数据插入数据库的时间用于区分不同批次抓取的数据。你可以使用MySQL客户端如MySQL Workbench, Navicat或命令行执行这个SQL文件来创建数据库和表。对于SQLite语法略有不同但逻辑相似项目源代码中通常会提供适配SQLite的版本或配置。4. 核心爬虫逻辑实现详解4.1 定义数据模型Items在items.py中我们定义爬虫要抓取的数据结构。这相当于为我们的数据定义一个强类型的容器。import scrapy class DoubanMovieItem(scrapy.Item): # 定义电影数据项的各字段 douban_id scrapy.Field() # 豆瓣ID title scrapy.Field() # 标题 directors scrapy.Field() # 导演 actors scrapy.Field() # 演员 rating scrapy.Field() # 评分 rating_count scrapy.Field() # 评分人数 genres scrapy.Field() # 类型 pubdate scrapy.Field() # 上映日期 summary scrapy.Field() # 简介 tags scrapy.Field() # 标签 cover_url scrapy.Field() # 封面图 detail_url scrapy.Field() # 详情页链接scrapy.Field()对象可以接受元数据但在这个简单模型中我们只需声明字段即可。在爬虫的解析函数中我们会实例化这个Item类并为每个字段赋值。4.2 编写爬虫主体Spider我们在spiders目录下创建一个新文件比如top250_spider.py。这里我们以抓取豆瓣电影Top250为例因为它结构清晰适合教学。import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from douban_movie.items import DoubanMovieItem import re class Top250Spider(CrawlSpider): name top250 # 爬虫的唯一标识运行爬虫时使用 allowed_domains [movie.douban.com] # 限制爬取的域名 start_urls [https://movie.douban.com/top250] # 起始URL # 定义规则从列表页提取电影详情页链接并指定回调函数 rules ( Rule(LinkExtractor(restrict_cssdiv.hd a), callbackparse_movie_detail, followFalse), Rule(LinkExtractor(restrict_cssspan.next a), followTrue), # 处理“下一页” ) def parse_movie_detail(self, response): 解析电影详情页提取具体信息。 item DoubanMovieItem() item[detail_url] response.url # 提取豆瓣ID从URL中正则匹配 match re.search(rsubject/(\d)/, response.url) if match: item[douban_id] match.group(1) # 使用CSS选择器提取信息 item[title] response.css(h1 span::text).get() # 电影标题 item[directors] /.join(response.css(a[relv:directedBy]::text).getall()) # 导演 item[actors] /.join(response.css(.actor a[relv:starring]::text).getall()[:5]) # 主要演员取前5个 item[rating] response.css(strong.ll.rating_num::text).get() # 评分 # 评分人数需要处理掉“人评价”这几个字 rating_count_text response.css(div.rating_sum span::text).get() if rating_count_text: item[rating_count] int(rating_count_text.replace(人评价, ).strip()) item[genres] /.join(response.css(span[propertyv:genre]::text).getall()) # 类型 item[pubdate] /.join(response.css(span[propertyv:initialReleaseDate]::text).getall()) # 上映日期 item[summary] response.css(span[propertyv:summary]::text).get().strip() # 简介 # 标签可能在另一个区块 item[tags] /.join(response.css(.tags-body .tag::text).getall()) item[cover_url] response.css(#mainpic img::attr(src)).get() # 封面图URL yield item # 将Item返回给引擎进入Pipeline处理代码逻辑解析继承CrawlSpider这是Scrapy中用于处理规则化链接爬取的强大Spider。我们通过rules元组来定义链接提取和跟踪规则。LinkExtractor用于从页面中自动提取符合特定条件的链接。第一条规则使用CSS选择器div.hd a提取每个电影条目的详情页链接。callbackparse_movie_detail指定了处理这些链接的回调函数。followFalse表示不继续跟踪从这些详情页里提取的链接避免无限深入。第二条规则提取“下一页”的链接span.next a。followTrue表示Scrapy会跟进这些链接并在新页面上继续应用所有规则。这样就实现了自动翻页。parse_movie_detail函数这是数据提取的核心。我们使用CSS选择器也可以用XPath来定位页面元素并提取文本。选择器的编写需要分析豆瓣电影详情页的HTML结构。字段提取例如response.css(h1 span::text).get()获取h1标签内span的文本内容即电影标题。.getall()返回所有匹配结果的列表我们用/.join(...)将其合并为一个字符串。数据清洗在提取过程中直接进行简单清洗。例如评分人数文本是“2000000人评价”我们用.replace()和.strip()去掉“人评价”并转换为整数。正则匹配豆瓣ID从URL中提取更可靠使用re.search进行匹配。yield item这是关键。它将填充好的Item对象抛给Scrapy引擎引擎会将其送入配置好的Item Pipeline进行处理。使用yield而非return使得这个函数成为一个生成器可以逐条产出数据节省内存。实操心得编写选择器时浏览器的开发者工具F12是你的最佳伙伴。使用“检查元素”功能可以直观地看到目标元素的HTML结构和可能的CSS路径。尽量选择具有唯一性、稳定性的属性如id,class,property来定位元素避免使用可能变化的索引位置。豆瓣页面的propertyv:xxx这类属性是遵循微格式的通常比较稳定。4.3 反爬策略与中间件配置豆瓣有基本的反爬机制过于频繁的访问会导致IP被暂时封锁或返回验证码。我们需要在settings.py中配置一些参数来模拟人类行为并可能编写自定义中间件。基础反爬配置settings.py# 遵守robots.txt协议对于学习项目可以设为False以抓取更多内容但生产环境建议遵守 ROBOTSTXT_OBEY False # 配置默认的请求头特别是User-Agent DEFAULT_REQUEST_HEADERS { Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, 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 } # 下载延迟单位秒。设置一个合理的值如1-3秒避免请求过快 DOWNLOAD_DELAY 2 # 自动限速扩展根据服务器响应动态调整延迟 AUTOTHROTTLE_ENABLED True AUTOTHROTTLE_START_DELAY 2 AUTOTHROTTLE_MAX_DELAY 10 AUTOTHROTTLE_TARGET_CONCURRENCY 1.0 # 启用Cookies有时需要维持会话 COOKIES_ENABLED True # 并发请求数不要设置太高 CONCURRENT_REQUESTS 4 # 对单个网站的最大并发数 CONCURRENT_REQUESTS_PER_DOMAIN 2自定义User-Agent中间件middlewares.py 单一的User-Agent容易被识别。我们可以创建一个中间件每次请求时随机从列表中选一个。import random from douban_movie.settings import USER_AGENT_LIST # 需要在settings.py中定义这个列表 class RandomUserAgentMiddleware: 随机User-Agent中间件 def process_request(self, request, spider): ua random.choice(USER_AGENT_LIST) if ua: request.headers[User-Agent] ua # 不要return任何值让请求继续处理然后在settings.py中启用这个中间件并定义USER_AGENT_LISTUSER_AGENT_LIST [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ..., Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 ..., # ... 添加更多常见的浏览器User-Agent字符串 ] DOWNLOADER_MIDDLEWARES { douban_movie.middlewares.RandomUserAgentMiddleware: 543, # 数值越小优先级越高 }注意事项对于更严格的反爬可能需要使用IP代理池。但搭建和维护代理池成本较高对于豆瓣这类对个人学习相对友好的网站通过控制请求频率、使用随机User-Agent、合理设置DOWNLOAD_DELAY和CONCURRENT_REQUESTS通常就能稳定运行。如果遇到封禁可以暂停一段时间如半小时再继续。切勿在短时间内发起海量请求这不仅不道德也容易导致你的IP被永久封禁。5. 数据清洗与数据库存储管道实现爬虫抓取到的原始数据往往存在缺失、格式不一致、包含无关字符等问题。数据管道Pipeline就是负责在数据入库前进行最后一道清洗和格式化并处理存储逻辑。5.1 数据清洗管道我们在pipelines.py中创建一个清洗管道。Scrapy的管道是按settings.py中定义的顺序执行的。class DataCleanPipeline: 数据清洗管道负责清洗和格式化Item中的字段。 def process_item(self, item, spider): # 1. 处理空值和默认值 for field in item.fields: if field not in item or item[field] is None: item[field] # 或者根据字段类型设置默认值如0.0 for rating # 2. 清洗特定字段 # 清洗评分确保是数字并保留一位小数 if item[rating]: try: item[rating] float(item[rating]) except ValueError: item[rating] 0.0 # 清洗评分人数转换为整数 if item[rating_count]: if isinstance(item[rating_count], str): # 移除逗号等千位分隔符 item[rating_count] int(item[rating_count].replace(,, )) # 如果已经是int则保持 # 3. 字符串字段去除首尾空格 string_fields [title, directors, actors, genres, pubdate, summary, tags] for field in string_fields: if item.get(field): item[field] item[field].strip() # 4. 处理简介中的换行和多余空格 if item[summary]: # 将多个换行和空格合并为一个空格 import re item[summary] re.sub(r\s, , item[summary]) return item # 必须返回item传递给下一个管道这个管道做了几件事确保所有字段都有值即使是空字符串、将评分和评分人数转换为正确的数值类型、去除字符串字段的无用空格、清理简介文本的格式。这些操作能极大提高后续数据分析和使用的便利性。5.2 数据库存储管道清洗后的数据需要存入数据库。我们创建一个专门的存储管道。这里以MySQL为例使用pymysql库。import pymysql from itemadapter import ItemAdapter class MySQLPipeline: MySQL存储管道将Item存入MySQL数据库。 def __init__(self, mysql_host, mysql_db, mysql_user, mysql_password): self.mysql_host mysql_host self.mysql_db mysql_db self.mysql_user mysql_user self.mysql_password mysql_password self.connection None self.cursor None classmethod def from_crawler(cls, crawler): # 从settings.py读取数据库配置 return cls( mysql_hostcrawler.settings.get(MYSQL_HOST, localhost), mysql_dbcrawler.settings.get(MYSQL_DATABASE, douban_movie), mysql_usercrawler.settings.get(MYSQL_USER, root), mysql_passwordcrawler.settings.get(MYSQL_PASSWORD, ), ) def open_spider(self, spider): 爬虫启动时建立数据库连接。 self.connection pymysql.connect( hostself.mysql_host, userself.mysql_user, passwordself.mysql_password, dbself.mysql_db, charsetutf8mb4, # 重要支持存储Emoji等四字节字符 cursorclasspymysql.cursors.DictCursor ) self.cursor self.connection.cursor() spider.logger.info(f成功连接到MySQL数据库: {self.mysql_db}) def close_spider(self, spider): 爬虫关闭时关闭数据库连接。 if self.connection: self.connection.close() spider.logger.info(MySQL数据库连接已关闭。) def process_item(self, item, spider): # 使用ItemAdapter获取字典形式的item数据 adapter ItemAdapter(item) data adapter.asdict() # 构建SQL插入语句使用ON DUPLICATE KEY UPDATE实现“存在则更新” # 假设豆瓣ID是主键或唯一索引 placeholders , .join([%s] * len(data)) columns , .join(data.keys()) updates , .join([f{k}VALUES({k}) for k in data.keys() if k ! douban_id]) # 主键不更新 sql f INSERT INTO movies ({columns}) VALUES ({placeholders}) ON DUPLICATE KEY UPDATE {updates} try: self.cursor.execute(sql, list(data.values())) self.connection.commit() spider.logger.debug(f成功插入/更新电影: {item.get(title)} (ID: {item.get(douban_id)})) except pymysql.Error as e: spider.logger.error(f插入数据失败: {e}, SQL: {sql}, Data: {data}) self.connection.rollback() # 发生错误时回滚 # 可以选择抛出DropItem异常来丢弃这个item或者不抛出让爬虫继续 # raise DropItem(fMySQL error: {e}) return item关键点解析from_crawler类方法这是Scrapy管道的标准初始化方式用于从项目设置settings.py中读取配置信息如数据库连接参数。这样可以将配置与代码分离更灵活。open_spider和close_spider这两个方法分别在爬虫开始和结束时被Scrapy自动调用。我们在open_spider中建立数据库连接在close_spider中关闭连接确保资源正确管理。ON DUPLICATE KEY UPDATE这是MySQL的一个强大特性。当插入的数据与表中现有数据的主键或唯一索引冲突时执行更新操作而不是报错。这对于爬虫非常有用因为我们可以定期重新爬取同一部电影来更新它的评分、评价人数等信息而不会产生重复记录。VALUES(column_name)函数引用的是INSERT语句中试图插入的值。异常处理与日志数据库操作可能失败网络问题、重复键、字段超长等。我们使用try...except捕获异常记录错误日志并执行回滚。spider.logger是Scrapy提供的日志工具非常好用。字符集utf8mb4这是必须的。标准的utf8在MySQL中只支持最多三个字节的字符基本多文种平面而utf8mb4支持四字节字符如Emoji表情、某些生僻汉字。豆瓣的评论或简介里有可能出现Emoji。对应的settings.py配置# 启用Item Pipeline并设置执行顺序。数值越小优先级越高。 ITEM_PIPELINES { douban_movie.pipelines.DataCleanPipeline: 300, # 先清洗 douban_movie.pipelines.MySQLPipeline: 800, # 后存储 } # MySQL数据库配置 MYSQL_HOST localhost MYSQL_DATABASE douban_movie MYSQL_USER your_username MYSQL_PASSWORD your_password对于SQLite管道逻辑类似只是连接库换成sqlite3SQL语句略有不同SQLite使用INSERT OR REPLACE来实现类似ON DUPLICATE KEY UPDATE的功能。6. 项目运行、监控与数据验证6.1 运行爬虫与常用命令项目代码编写完成后就可以运行了。在项目根目录有scrapy.cfg的目录下使用命令行# 运行名为 top250 的爬虫并将日志输出到文件 scrapy crawl top250 -s LOG_FILEtop250.log # 更详细的日志输出 scrapy crawl top250 --loglevelINFO # 以安静模式运行只输出错误信息 scrapy crawl top250 --loglevelERROR # 将爬取到的数据导出为JSON文件用于调试不经过Pipeline scrapy crawl top250 -o movies.json # 将爬取到的数据导出为CSV文件 scrapy crawl top250 -o movies.csv使用-o选项导出文件时数据不会经过你在settings.py中配置的ITEM_PIPELINES。这通常用于快速检查爬虫解析是否正确。正式运行采集数据到数据库时不要加-o参数。实操心得在开发调试阶段可以先使用scrapy shell https://movie.douban.com/subject/1292052/命令进入交互式shell。这是一个极其强大的工具你可以快速测试CSS或XPath选择器是否正确而无需反复运行整个爬虫。例如在shell中输入response.css(h1 span::text).get()立刻就能看到提取结果。6.2 监控爬虫状态与日志分析爬虫运行时控制台会输出大量日志。关注以下几点DEBUG级别日志可以看到每个请求的发出和响应以及Item的生成信息量最大但也很冗长。INFO级别日志会显示爬取的页面数量、Item数量、请求统计等适合监控运行概况。ERROR/WARNING级别日志必须重点关注。常见的错误包括Ignoring response xxx: HTTP status code is not handled or not allowed请求被服务器拒绝如403、404、503。可能是触发了反爬。Error downloading request url网络错误或超时。在管道中记录的数据库插入错误。将日志输出到文件-s LOG_FILE...便于事后分析。如果爬虫中途因错误停止可以查看日志文件定位问题。6.3 数据验证与简单分析爬虫运行结束后我们需要验证数据是否正确地存入了数据库。连接到你的MySQL数据库执行一些查询-- 查看总共爬取了多少条电影记录 SELECT COUNT(*) FROM movies; -- 查看评分最高的10部电影 SELECT title, rating, rating_count FROM movies ORDER BY rating DESC LIMIT 10; -- 查看哪种类型的电影最多 SELECT genres, COUNT(*) as count FROM movies GROUP BY genres ORDER BY count DESC LIMIT 10; -- 检查是否有数据缺失例如评分为空 SELECT * FROM movies WHERE rating IS NULL OR rating ;通过这些查询可以直观地感受数据的质量。你可能会发现一些问题比如某些电影的“上映日期”字段格式很奇怪或者“标签”字段为空。这可能需要你回头调整爬虫的解析逻辑或数据清洗管道。7. 常见问题排查与优化技巧在实际运行中你几乎一定会遇到各种问题。这里记录了一些典型问题的排查思路和解决技巧。7.1 爬虫被屏蔽或返回403错误这是最常见的问题。症状日志中出现大量403 Forbidden或Ignoring response爬取速度变慢直至停止或者返回的HTML内容是验证码页面。排查与解决检查请求头确保User-Agent是有效的浏览器标识。使用我们上面编写的随机User-Agent中间件。降低请求频率大幅增加DOWNLOAD_DELAY比如到5秒或10秒并减少CONCURRENT_REQUESTS_PER_DOMAIN比如到1。让爬虫“慢下来”。检查Cookies有些页面需要登录或简单的会话状态。确保COOKIES_ENABLED True。对于更复杂的情况可能需要先在浏览器中登录然后将Cookies复制到爬虫的start_requests方法中。模拟浏览器行为有些网站会检查JavaScript执行环境。对于这类动态加载的网站Scrapy本身可能不够。这时可以考虑使用scrapy-splash或scrapy-playwright中间件来渲染JavaScript。直接分析网站的API接口。通过浏览器开发者工具的“网络”选项卡查看数据是如何通过XHR或Fetch请求加载的然后让爬虫直接去请求这些API接口效率更高且不易被反爬。豆瓣的部分数据就有API接口。使用代理IP如果IP被封锁这是终极方案。可以在middlewares.py中编写代理中间件并从代理IP池中随机选取IP。但免费代理质量不稳定高质量代理需要付费。7.2 数据提取失败或为空症状Item中的某些字段总是为空或者提取到了错误的内容。排查与解决使用scrapy shell调试这是最有效的工具。在shell中加载有问题的页面URL然后逐一测试你的CSS/XPath选择器看是否能准确提取到目标数据。检查页面结构变化网站前端可能会改版导致你之前写的选择器失效。定期检查并更新选择器。处理动态加载内容如果数据是通过JavaScript异步加载的在初始HTML中不存在。解决方法同上一点分析API或使用渲染中间件。注意默认值和异常处理在解析函数中对每个字段的提取都使用.get()并提供默认值如.get()避免因为某个元素缺失导致整个Item解析失败。7.3 数据库相关错误症状MySQLPipeline中抛出pymysql.Error数据无法插入。排查与解决连接失败检查settings.py中的数据库主机、端口、用户名、密码、数据库名是否正确。检查MySQL服务是否启动以及用户是否有远程连接的权限如果主机不是localhost。字符编码错误确保数据库、表和连接都使用utf8mb4字符集。中文字符或Emoji插入失败多半是这个问题。数据字段超长VARCHAR(255)字段可能存不下过长的字符串比如演员列表非常长。根据实际情况调整表结构增加字段长度或者将超长文本字段改为TEXT类型。主键或唯一键冲突如果你没有使用ON DUPLICATE KEY UPDATE并且试图插入重复的豆瓣ID就会报错。确保你的SQL语句能正确处理重复数据。网络超时数据库服务器响应慢或网络不稳定。可以在连接数据库时设置connect_timeout参数。7.4 性能优化与代码健壮性启用缓存在settings.py中设置HTTPCACHE_ENABLED True。这在开发调试时非常有用可以避免重复下载相同的页面加快测试速度。合理设置并发不要盲目提高CONCURRENT_REQUESTS。过高的并发会给目标服务器带来压力也更容易触发反爬。根据网络条件和目标服务器的承受能力从低并发开始测试。实现断点续爬Scrapy本身不支持真正的断点续爬即从上次停止的URL继续。一种简单的实现方式是使用JOBDIR设置scrapy crawl top250 -s JOBDIRjobs/top250。这会将请求队列和去重指纹保存到磁盘下次用相同命令启动时会尝试恢复。更复杂的需求可以考虑使用scrapy-redis将队列和去重中心化到Redis实现分布式和持久化。日志分级生产环境运行时将日志级别设为INFO或WARNING避免DEBUG日志产生的巨大IO开销。编写单元测试为你的Spider编写测试确保解析逻辑正确。Scrapy提供了scrapy.utils.test模块来帮助测试。这个基于Scrapy的豆瓣电影爬虫项目从环境搭建、爬虫编写、反爬应对、数据清洗到数据库存储覆盖了一个数据采集系统的主要环节。它提供的不仅仅是一段可运行的代码更是一种工程化的思考和解决问题的方法。你可以以此为基础尝试爬取豆瓣的其他板块如书籍、音乐或者将数据应用于更复杂的分析、推荐系统甚至结合Django或Flask搭建一个简单的电影展示网站。爬虫技术的核心在于对目标网站结构和网络协议的理解以及应对各种异常情况的耐心与技巧希望这个项目能为你提供一个扎实的起点。本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →