Scrapy 框架实战:构建高效的快看漫画分布式爬虫

一、Scrapy框架概述

Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,它提供了强大的数据提取能力、灵活的扩展机制以及高效的异步处理性能。其核心架构包括:

  • Engine:控制所有组件之间的数据流,当某个动作发生时触发事件
  • Scheduler:接收Engine发送的请求并入队,当Engine请求时返回给Engine
  • Downloader:负责下载网页内容并将结果返回给Spider
  • Spider:用户编写的用于分析响应、提取项目和额外URL的类
  • Item Pipeline:负责处理Spider提取的项目,进行数据清洗、验证和存储

二、项目环境搭建

首先,我们需要安装Scrapy和相关的依赖库:

对于分布式爬虫,我们还需要安装和配置Redis服务器作为调度队列。

三、创建Scrapy项目

使用Scrapy命令行工具创建项目:

plain 复制代码
scrapy startproject kuaikan_crawler
cd kuaikan_crawler
scrapy genspider kuaikan www.kuaikanmanhua.com

四、定义数据模型

在items.py中定义我们需要抓取的数据结构:

plain 复制代码
import scrapy

class ComicItem(scrapy.Item):
    title = scrapy.Field()          # 漫画标题
    author = scrapy.Field()         # 作者
    description = scrapy.Field()    # 描述
    cover_url = scrapy.Field()      # 封面URL
    tags = scrapy.Field()           # 标签
    likes = scrapy.Field()          # 喜欢数
    comments = scrapy.Field()       # 评论数
    chapters = scrapy.Field()       # 章节列表
    source_url = scrapy.Field()     # 源URL
    crawl_time = scrapy.Field()     # 爬取时间

五、编写爬虫核心逻辑

在spiders/kuaikan.py中编写爬虫的主要逻辑:

plain 复制代码
import scrapy
import json
from kuaikan_crawler.items import ComicItem
from urllib.parse import urljoin

class KuaikanSpider(scrapy.Spider):
    name = 'kuaikan'
    allowed_domains = ['www.kuaikanmanhua.com']
    start_urls = ['https://www.kuaikanmanhua.com/web/topic/all/']
    
    def parse(self, response):
        # 解析漫画列表页
        comics = response.css('.TopicList .topic-item')
        for comic in comics:
            detail_url = comic.css('a::attr(href)').get()
            if detail_url:
                yield scrapy.Request(
                    url=urljoin(response.url, detail_url),
                    callback=self.parse_comic_detail
                )
        
        # 分页处理
        next_page = response.css('.next-page::attr(href)').get()
        if next_page:
            yield scrapy.Request(
                url=urljoin(response.url, next_page),
                callback=self.parse
            )
    
    def parse_comic_detail(self, response):
        # 解析漫画详情页
        item = ComicItem()
        
        # 提取基本信息
        item['title'] = response.css('.comic-title::text').get()
        item['author'] = response.css('.author-name::text').get()
        item['description'] = response.css('.comic-description::text').get()
        item['cover_url'] = response.css('.cover img::attr(src)').get()
        item['tags'] = response.css('.tags .tag::text').getall()
        item['likes'] = response.css('.like-count::text').get()
        item['comments'] = response.css('.comment-count::text').get()
        item['source_url'] = response.url
        item['crawl_time'] = datetime.now().isoformat()
        
        # 提取章节信息
        chapters = []
        for chapter in response.css('.chapter-list li'):
            chapter_info = {
                'title': chapter.css('.chapter-title::text').get(),
                'url': urljoin(response.url, chapter.css('a::attr(href)').get()),
                'update_time': chapter.css('.update-time::text').get()
            }
            chapters.append(chapter_info)
        
        item['chapters'] = chapters
        
        yield item

六、实现分布式爬虫

为了将爬虫转换为分布式模式,我们需要使用scrapy-redis组件:

  1. 修改settings.py配置文件:
plain 复制代码
# 启用scrapy-redis调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

# 启用去重过滤器
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

# 设置Redis连接
REDIS_URL = 'redis://localhost:6379/0'

# 保持Redis队列不清空,允许暂停/恢复爬取
SCHEDULER_PERSIST = True

# 设置Item Pipeline
ITEM_PIPELINES = {
    'scrapy_redis.pipelines.RedisPipeline': 300,
    'kuaikan_crawler.pipelines.MongoPipeline': 400,
}
  1. 修改爬虫代码,继承RedisSpider:
plain 复制代码
from scrapy_redis.spiders import RedisSpider

class DistributedKuaikanSpider(RedisSpider):
    name = 'distributed_kuaikan'
    redis_key = 'kuaikan:start_urls'
    
    def __init__(self, *args, **kwargs):
        super(DistributedKuaikanSpider, self).__init__(*args, **kwargs)
        self.allowed_domains = ['www.kuaikanmanhua.com']
    
    def parse(self, response):
        # 解析逻辑与之前相同
        pass

七、数据存储管道

创建MongoDB存储管道,在pipelines.py中:

plain 复制代码
import pymongo
from scrapy import settings

class MongoPipeline:
    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db
    
    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'scrapy')
        )
    
    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]
    
    def close_spider(self, spider):
        self.client.close()
    
    def process_item(self, item, spider):
        collection_name = item.__class__.__name__
        self.db[collection_name].insert_one(dict(item))
        return item

在settings.py中添加MongoDB配置:

plain 复制代码
MONGO_URI = 'mongodb://localhost:27017'
MONGO_DATABASE = 'kuaikan_comics'

八、中间件与反爬虫策略

为了应对网站的反爬虫机制,我们需要添加一些中间件:

plain 复制代码
# 在middlewares.py中添加随机User-Agent中间件
import random
from scrapy import signals

class RandomUserAgentMiddleware:
    def __init__(self, user_agents):
        self.user_agents = user_agents
    
    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            user_agents=crawler.settings.get('USER_AGENTS', [])
        )
    
    def process_request(self, request, spider):
        request.headers['User-Agent'] = random.choice(self.user_agents)

# 在settings.py中配置用户代理列表
USER_AGENTS = [
    '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',
    # 添加更多用户代理...
]

总结

本文详细介绍了如何使用Scrapy框架构建一个高效的分布式漫画爬虫。通过结合Scrapy-Redis实现分布式抓取,使用MongoDB进行数据存储,以及实施多种反反爬虫策略,我们能够构建一个稳定高效的爬虫系统。这种架构不仅可以应用于漫画网站,经过适当修改后也可以用于其他各种类型的网站数据抓取任务。

相关推荐
计算机程序员小杨5 分钟前
计算机毕设选题:电子商务供应链大数据分析系统Python+Django技术实现详解|毕设|计算机毕设|程序开发|项目实战
java·vue.js·python
moxiaoran57536 分钟前
Django Admin 管理工具
python·django
小先生001011 小时前
GraphRAG 知识图谱核心升级:集成 langextract 与 Gemini ----实现高精度实体与关系抽取
人工智能·python·开源·prompt·github·bert·知识图谱
拾忆,想起1 小时前
Redis红锁(RedLock)解密:分布式锁的高可用终极方案
java·数据库·redis·分布式·缓存·性能优化·wpf
衍生星球1 小时前
JSP程序设计之JSP指令
java·开发语言·jsp
007php0072 小时前
Go Vendor 和 Go Modules:管理和扩展依赖的最佳实践
java·开发语言·docker·微服务·golang·自动化·jenkins
郝学胜-神的一滴2 小时前
C++组合模式:构建灵活的层次结构
开发语言·c++·程序人生·设计模式·组合模式
封奚泽优2 小时前
数学七夕花礼(MATLAB版)
开发语言·数学·matlab·七夕·鲜花
跟橙姐学代码2 小时前
写Python的人,都应该掌握的高效写法(用了真的爽!)
前端·python·ipython
阿豪在学习2 小时前
win环境使用pixi,安装vnpy(python3.13.5)
python