当前位置: 首页 > news >正文

用Scrapyd爬取豆瓣图书Top250

在数据采集场景中,异步爬虫是提高效率的核心方案,而 Scrapyd 作为 Scrapy 的部署调度工具,能让爬虫实现分布式运行和定时任务管理。本文将基于「豆瓣图书Top250爬取并写入Excel」的实战案例,详细拆解项目搭建、部署流程,以及过程中遇到的6个典型问题和解决方案,适合爬虫新手参考学习。

一、项目需求与技术选型

1. 核心需求

  • 爬取目标:豆瓣图书Top250全量数据(书名、作者、出版社、评分等10个字段)
  • 技术要求:异步爬取、支持部署调度、数据导出为Excel
  • 反爬要求:绕过豆瓣基础反爬机制,保证爬取稳定性

2. 技术选型

工具/框架作用说明核心优势
Scrapy异步爬虫核心天然支持异步并发、中间件生态完善、数据处理流程清晰
Scrapyd爬虫部署与调度支持分布式运行、提供Web管理界面、可通过API调度任务
openpyxlExcel数据写入支持大文件写入、可设置单元格样式、兼容.xlsx格式
fake-useragent反爬辅助随机生成User-Agent,模拟浏览器访问
代理IP突破IP封禁解决豆瓣IP限制问题,提高爬取成功率

二、项目搭建完整流程

1. 环境准备

首先安装依赖包(建议使用虚拟环境,避免版本冲突):

# 创建虚拟环境(Windows)
python -m venv crawler-env
# 激活虚拟环境
crawler-env\Scripts\activate
# 安装核心依赖
pip install scrapy scrapyd scrapyd-client openpyxl fake-useragent

2. 项目结构搭建

# 创建Scrapy项目
scrapy startproject douban_book_spider
# 进入项目目录
cd douban_book_spider
# 创建爬虫
scrapy genspider book_spider book.douban.com

最终项目结构:

douban_book_spider/
├── douban_book_spider/
│   ├── items.py       # 定义爬取字段
│   ├── middlewares.py # 反爬/代理中间件
│   ├── pipelines.py   # Excel写入逻辑
│   ├── settings.py    # 项目核心配置
│   └── spiders/
│       └── book_spider.py # 爬虫核心逻辑
└── scrapy.cfg         # Scrapyd部署配置

3. 核心模块实现

(1)定义爬取字段(items.py)
import scrapyclass DoubanBookSpiderItem(scrapy.Item):book_name = scrapy.Field()       # 书名author = scrapy.Field()          # 作者publisher = scrapy.Field()       # 出版社publish_date = scrapy.Field()    # 出版日期price = scrapy.Field()           # 价格rating = scrapy.Field()          # 评分comment_count = scrapy.Field()   # 评价人数intro = scrapy.Field()           # 简介cover_url = scrapy.Field()       # 封面URLdetail_url = scrapy.Field()      # 详情页链接
(2)爬虫核心逻辑(book_spider.py)

实现分页爬取、详情页提取逻辑,支持异步请求:

import scrapy
from douban_book_spider.items import DoubanBookSpiderItemclass BookSpider(scrapy.Spider):name = 'book_spider'allowed_domains = ['book.douban.com']start_urls = []  # 清空默认URL,通过start方法生成# 异步生成分页URL(Scrapy 2.13+推荐用start()替代start_requests())async def start(self):# 豆瓣Top250共10页,start参数从0开始,每次+25for start in range(0, 250, 25):url = f'https://book.douban.com/top250?start={start}'yield scrapy.Request(url=url,callback=self.parse_book_list,headers={'Referer': 'https://book.douban.com/','User-Agent': self.settings.get('USER_AGENT')},dont_filter=True)# 解析列表页,提取详情页链接def parse_book_list(self, response):book_items = response.xpath('//div[@class="pl2"]')for item in book_items:detail_url = item.xpath('./a/@href').extract_first()if detail_url:yield scrapy.Request(url=detail_url,callback=self.parse_book_detail,headers={'User-Agent': self.settings.get('USER_AGENT')},meta={'detail_url': detail_url})# 解析详情页,提取图书完整信息def parse_book_detail(self, response):item = DoubanBookSpiderItem()detail_url = response.meta['detail_url']# 书名book_name = response.xpath('//span[@property="v:itemreviewed"]/text()').extract_first()item['book_name'] = book_name.strip() if book_name else '未知书名'# 作者、出版社、出版日期、价格(豆瓣详情页格式统一,拆分提取)info_str = ''.join([s.strip() for s in response.xpath('//div[@id="info"]//text()').extract() if s.strip()])item['author'] = info_str.split('作者:')[1].split('出版社:')[0].strip() if '作者:' in info_str else '未知作者'item['publisher'] = info_str.split('出版社:')[1].split('出版年:')[0].strip() if '出版社:' in info_str else '未知出版社'item['publish_date'] = info_str.split('出版年:')[1].split('页数:')[0].strip() if '出版年:' in info_str else '未知日期'item['price'] = info_str.split('定价:')[1].split('装帧:')[0].strip() if '定价:' in info_str else '未知价格'# 评分、评价人数、简介、封面URLitem['rating'] = response.xpath('//strong[@property="v:average"]/text()').extract_first() or '0.0'item['comment_count'] = response.xpath('//span[@property="v:votes"]/text()').extract_first() or '0'item['intro'] = '\n'.join([line.strip() for line in response.xpath('//div[@class="intro"]//p/text()').extract() if line.strip()]) or '无简介'item['cover_url'] = response.xpath('//img[@rel="v:image"]/@src').extract_first() or '无封面'item['detail_url'] = detail_urlyield item
(3)Excel写入Pipeline(pipelines.py)

使用线程锁避免异步写入冲突,设置Excel样式:

from openpyxl import Workbook
from openpyxl.styles import Font, Alignment
import threadingclass DoubanBookExcelPipeline:wb = Nonews = Nonelock = threading.Lock()  # 解决异步写入冲突def open_spider(self, spider):# 初始化Excel工作簿self.wb = Workbook()self.ws = self.wb.activeself.ws.title = '豆瓣图书数据'# 表头与样式设置headers = ['书名', '作者', '出版社', '出版日期', '价格', '评分', '评价人数', '简介', '封面URL', '详情页链接']for col, header in enumerate(headers, start=1):cell = self.ws.cell(row=1, column=col, value=header)cell.font = Font(bold=True)cell.alignment = Alignment(horizontal='center')# 列宽适配column_widths = [20, 25, 20, 15, 10, 8, 12, 50, 40, 40]for col, width in enumerate(column_widths, start=1):self.ws.column_dimensions[chr(64+col)].width = widthdef process_item(self, item, spider):# 加锁写入数据with self.lock:row = self.ws.max_row + 1data = [item['book_name'], item['author'], item['publisher'], item['publish_date'], item['price'],item['rating'], item['comment_count'], item['intro'], item['cover_url'], item['detail_url']]for col, value in enumerate(data, start=1):self.ws.cell(row=row, column=col, value=value)return itemdef close_spider(self, spider):# 保存Excel文件self.wb.save('豆瓣图书全量数据.xlsx')print('数据已保存到:豆瓣图书全量数据.xlsx')
(4)项目核心配置(settings.py)

重点配置反爬策略、Pipeline、中间件:

# 反爬配置
ROBOTSTXT_OBEY = False  # 禁用robots协议
COOKIES_ENABLED = False  # 禁用Cookie(豆瓣反爬关键)
DOWNLOAD_DELAY = 5  # 访问延迟5秒
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36'# 默认请求头(模拟浏览器访问)
DEFAULT_REQUEST_HEADERS = {'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','Referer': 'https://book.douban.com/'
}# 启用Pipeline(Excel写入)
ITEM_PIPELINES = {'douban_book_spider.pipelines.DoubanBookExcelPipeline': 300,
}# 启用代理中间件
DOWNLOADER_MIDDLEWARES = {'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,'douban_book_spider.middlewares.ProxyMiddleware': 544,
}# 日志配置
LOG_LEVEL = 'INFO'
LOG_FILE = 'douban_book_spider.log'# 其他基础配置
BOT_NAME = 'douban_book_spider'
SPIDER_MODULES = ['douban_book_spider.spiders']
NEWSPIDER_MODULE = 'douban_book_spider.spiders'
FEED_EXPORT_ENCODING = 'utf-8-sig'  # 解决Excel中文乱码
(5)代理中间件(middlewares.py)
class ProxyMiddleware:def process_request(self, request, spider):request.meta['proxy'] = 'http://127.0.0.1:7890'# 代理失效时重试def process_exception(self, request, exception, spider):return scrapy.Request(url=request.url,callback=request.callback,headers=request.headers,meta=request.meta,dont_filter=True)

4. Scrapyd部署与调度

(1)启动Scrapyd服务
# 在项目根目录执行
scrapyd

启动成功后访问 http://localhost:6800 可看到Scrapyd管理界面。

(2)部署爬虫到Scrapyd
# 项目根目录执行(确保scrapy.cfg配置正确)
scrapyd-deploy localhost -p douban_book_spider

部署成功提示:Deployed project "douban_book_spider" version 1

(3)调度爬虫运行
# 命令行调度(推荐)
curl http://localhost:6800/schedule.json -d project=douban_book_spider -d spider=book_spider

成功响应:{"status": "ok", "jobid": "xxx"}

三、实战踩坑实录(6个核心问题+解决方案)

坑1:Scrapyd界面看不到部署的项目

现象

执行 scrapyd-deploy 后,访问 http://localhost:6800 未找到 douban_book_spider 项目。

原因

部署命令未在 Scrapy项目根目录 执行(根目录需包含 scrapy.cfg 文件)。

解决方案
  1. 切换到项目根目录(通过 cd 命令):
    cd D:\Desktop\js\douban_book_spider
    
  2. 重新执行部署命令,确保终端显示 Deployed project 成功提示。

坑2:爬虫请求豆瓣返回403 Forbidden

现象

日志显示 Crawled (403) <GET https://book.douban.com/top250?start=0>,爬虫直接终止。

原因

豆瓣反爬机制识别出爬虫:① 遵守robots协议;② 缺少必要请求头;③ 未禁用Cookie;④ IP被限制。

解决方案(逐步递进)
  1. 禁用robots协议:ROBOTSTXT_OBEY = False
  2. 禁用Cookie:COOKIES_ENABLED = False
  3. 添加完整请求头(DEFAULT_REQUEST_HEADERS
  4. 增大访问延迟:DOWNLOAD_DELAY = 5

坑3:Pipeline类未找到(NameError)

现象

日志报错:Module 'douban_book_spider.pipelines' has no attribute 'DoubanBookExcelPipeline'

原因

settings.py 中启用了Pipeline,但 pipelines.py 中未定义对应的类(或类名拼写错误)。

解决方案
  1. 确保 pipelines.py 中存在 DoubanBookExcelPipeline 类(复制完整代码,避免遗漏);
  2. 检查 settings.py 中Pipeline类名与文件中完全一致(大小写、拼写无差异):
    ITEM_PIPELINES = {'douban_book_spider.pipelines.DoubanBookExcelPipeline': 300,
    }
    

坑4:openpyxl模块缺失(ModuleNotFoundError)

现象

日志报错:ModuleNotFoundError: No module named 'openpyxl'

原因

openpyxl 安装到了系统Python环境,而非爬虫使用的虚拟环境。

解决方案
  1. 激活虚拟环境(终端前缀显示虚拟环境名称):
    d:\desktop\js\crawler\Scripts\activate
    
  2. 在虚拟环境中重新安装:
    pip install openpyxl==3.1.2
    
  3. 验证安装:pip list | findstr openpyxl(显示版本即成功)。

坑5:Windows终端编码错误(UnicodeEncodeError)

现象

日志报错:UnicodeEncodeError: 'gbk' codec can't encode character '\u2705'

原因

Windows终端默认编码为GBK,无法解析特殊符号(如 )和部分Unicode字符。

解决方案
  1. 删除代码中特殊符号(如Pipeline的打印语句中的 );
  2. 简化打印语句为纯中文:
    # 原代码(报错)
    print(f'✅ 数据已保存到:豆瓣图书全量数据.xlsx')
    # 修改后
    print('数据已保存到:豆瓣图书全量数据.xlsx')
    

坑6:start_requests方法废弃警告

现象

日志警告:ScrapyDeprecationWarning: douban_book_spider.spiders.book_spider.BookSpider defines the deprecated start_requests() method

原因

Scrapy 2.13+ 推荐用 start() 方法替代旧的 start_requests() 方法(旧方法仍可使用,但未来会废弃)。

解决方案

start_requests() 改为异步协程 start()

# 替换前
def start_requests(self):# 分页URL生成逻辑pass# 替换后
async def start(self):# 分页URL生成逻辑(代码不变)pass

四、项目优化建议

1. 断点续爬

启用Scrapy的 JOBDIR 配置,支持爬虫中断后继续爬取:

# settings.py中添加
JOBDIR = 'job_info'  # 断点信息保存目录

2. 数据去重

通过图书名称+作者去重,避免重复数据:

# Pipeline中添加去重逻辑
def __init__(self):self.book_set = set()  # 存储已爬取的(书名+作者)组合def process_item(self, item, spider):key = (item['book_name'], item['author'])if key not in self.book_set:self.book_set.add(key)# 写入Excel逻辑return itemreturn DropItem(f'Duplicate item: {key}')

3. 分布式爬取

在多台服务器部署Scrapyd,通过共享Redis队列分发任务,提高爬取效率(需结合 scrapy-redis 扩展)。

五、总结

本项目通过 Scrapyd+Scrapy 实现了豆瓣图书数据的异步爬取与部署调度,核心难点在于突破豆瓣的反爬机制和解决环境配置问题。通过实战我们总结出以下关键经验:

  1. 环境一致性是基础:始终在虚拟环境中操作,避免依赖包版本冲突和路径问题;
  2. 反爬策略循序渐进:从禁用Cookie、添加请求头,逐步提高爬取成功率;
  3. 日志是排坑关键:Scrapy和Scrapyd的日志会详细记录错误原因,重点关注 ERRORCRITICAL 级别日志;
  4. 代码规范避坑:类名、配置项拼写一致,避免因细节错误导致项目运行失败。

最终实现的爬虫可稳定爬取豆瓣图书Top250全量数据,生成结构化Excel文件,可直接用于数据分析或二次开发。如果需要扩展爬取范围(如豆瓣全分类图书),只需修改分页URL生成逻辑,即可实现快速扩展。

http://www.dtcms.com/a/610024.html

相关文章:

  • 数据分析笔记06:假设检验
  • 【论文阅读17】-LLM-TSFD:一种基于大型语言模型的工业时间序列人机回路故障诊断方法
  • Elasticsearch 面试题精编(26题|含答案|分类整理)
  • 专业格泰网站建设宝塔 怎么做网站
  • app做好了网站怎么做1千万人网站维护成本
  • 网站设计价格大概多少宁波seo关键词优化服务
  • AIGC总结二:Stable Diffusion 的训练方式、使用流程、硬件要求、实际应用场景
  • 大疆Action 6 ,pocket3及 action 5 Pro 该如何选择?
  • 银川网站开发培训案例分析网站
  • 谷歌云数据库服务概览:关系型与 NoSQL 的多元选择与应用场景解析
  • 自动驾驶环境下的多目标检测与识别_YOLOv8改进实践
  • 运动学模型推导 + 离散化 + 工程化版本(适用于前方单舵轮 AGV / 自动驾驶 / MPC)
  • 微信小程序中 WebView 组件的使用与应用场景
  • UE5导入的CAD文件零件如何被Merge?
  • 从无形IP到AI万象,安谋科技Arm China“周易”X3 NPU 发布!
  • 微信小程序可以做视频网站吗滑坡毕业设计代做网站
  • Windows 下 Eclipse + MinGW 写 C++ 环境
  • 美国税务表格W-2/1099/W-9/W-4/I-9详解:中国投资者跨境经营合规与战略指南
  • 外贸网站如何推广优化网站备案号取消原因
  • MySQL 查看有哪些表
  • 衡水做网站推广找谁wordpress 图片托管
  • 第一章 函数与极限 7.无穷小的比较
  • CMake 中 install 的使用原因和使用方法
  • 网站宝 添加二级域名怎样在工商局网站做申请登记
  • langchain langGraph 中streaming 流式输出 stream_mode
  • C语言在线编译 | 提供便捷高效的编程体验
  • 自建开发工具IDE(三)仙盟在线文件格式功能——东方仙盟炼气期
  • Vue 3 + Vite 集成 Spring Boot 完整部署指南 - 前后端一体化打包方案
  • 自己的网站网站项目ppt怎么做
  • 保健品网站建设策划书太原广告公司