scrapy-使用

本文详述了Scrapy的使用,包括安装、错误处理、项目创建与配置、启动项目、命令行操作、Item Pipeline的实现、Logging、Telnet终端、下载中间件设置、Spider中间件、图片下载、自动限速、分布式爬虫等。Scrapy通过组件如Engine、Scheduler、Downloader、Spiders和Item Pipeline协同工作,实现高效的数据抓取和处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

该文章涉及方面比较多,后面会将该文章拆开,每个方面都会进行详细的说明和使用,但是该文件的内容不变。

Scrapy 爬虫框架的使用 手册

基础介绍

安装

pip install Twisted-18.9.0-cp36-cp36m-win_amd64.whl
pip install Scrapy

错误

ModuleNotFoundError: No module named ‘win32api’

pip install pypiwin32

创建项目

scrapy startproject myscrapy

目录介绍

  • scrapy.cfg : 项目的配置文件
  • myscrapy:项目
  • myscrapy/items.py:项目使用的item文件
  • myscrapy/pipelines.py: 项目中的pipelines文件.
  • myscrapy/settings.py: 项目的设置文件.
  • myscrapy/spiders/: 放置spider代码的目录.

创建一个爬虫的应用

# 创建一个名称叫danke 网址后缀为danke.com的爬虫应用
scrapy genspider danke danke.com
# 此时在myscrapy/spiders目录下面有了一个danke.py的文件 打开

修改items.py

import scrapy
class CrawlsHouseItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    pass
class DanKe(CrawlsHouseItem):
     # 房子名称
    host_name = scrapy.Field()
    # 租金
    price = scrapy.Field()
    # room list
    room_list = scrapy.Field()

修改danke.py

# -*- coding: utf-8 -*-
import scrapy
from crawls_house.items import DanKe


class DankeSpider(scrapy.Spider):
    # 应用的名称
    name = 'danke'
    # 只有是该域名的连接才会被调度
    allowed_domains = ['www.danke.com']
    # 起始页面的url
    start_urls = ['https://www.danke.com/room/bj']

    def parse(self, response):
        # 获取需要继续跟进的url
        for href in response.xpath("//div[@class='r_lbx_cena']//a//@href").extract():
            # 将需要跟进的url交给调度器处理并指定回调方法为parse_item
            yield scrapy.Request(href, self.parse_item)
        # 获取下一页的url 自动会进行去重
        for next_href in response.xpath("//div[@class='page']//a//@href").extract():
            yield scrapy.Request(next_href)

    def parse_item(self, response):
        # 处理详情页面的信息
        danke = DanKe()
        # 房子的名称
        danke['host_name'] = response.xpath('//h1//text()').extract()[0]
        # 租金
        price = response.xpath('//div[@class="room-price-sale"]//text()').extract()[0]
        danke['price'] = price.replace(" ", "").replace("\n", "")
        # room list
        room_list = []
        for room in response.xpath('//div[@class="room-list"]//text()').extract():
            room = room.replace(" ", "").replace("\n", "")
            if len(room) != 0:
                room_list.append(room)
        danke["room_list"] = ",".join(room_list)
        # 返回 Item 交给 pipline 处理
        return danke

修改 settings.py

# 设置 User-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
# 是否遵循 robots.txt 协议
ROBOTSTXT_OBEY = False
# 是否关闭 cookies (默认启用)
COOKIES_ENABLED = False
# 使用 request headers:
DEFAULT_REQUEST_HEADERS = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
}
#item piplines
ITEM_PIPELINES = {
    'crawls_house.pipelines.CrawlsHousePipeline': 300,
}

修改pipelines.py

class CrawlsHousePipeline(object):
    def __init__(self):
        super().__init__()
        self.file = open("danke.json", "a", encoding="utf-8")

    def __del__(self):
        self.file.close()

    def process_item(self, item, spider):
        print(item, spider)
        self.file.write("{}|{}|{}\n".format(item["host_name"], item["price"], item["room_list"]))
        return item

启动项目

scrapy crawl danke

命令行使用

创建项目

scrapy startproject myproject

创建一个spider

# scrapy genspider [-t template] <name> <domain>
scrapy genspider myspider myspider.com
# 查看可以使用的template
scrapy genspider -l
# 查看template 的内容
scrapy genspider -d basic

运行项目

scrapy crawl myspider

检查spider

scrapy check -l
scrapy check

列出当前可用的spider

scrapy list

查看页面返回结果

scrapy fetch --nolog --headers https://www.danke.com/room/bj

用浏览器打开页面

scrapy view https://www.danke.com/room/bj

命令行执行scrapy

scrapy shell https://www.danke.com/room/bj

对url进行分析

scrapy parse <url> [options]
  • --spider=SPIDER: 跳过自动检测spider并强制使用特定的spider

  • --a NAME=VALUE: 设置spider的参数(可能被重复)

  • --callback or -c: spider中用于解析返回(response)的回调函数

  • --pipelines: 在pipeline中处理item

  • --rules or -r: 使用 CrawlSpider 规则来发现用来解析返回(response)的回调函数

  • --noitems: 不显示爬取到的item

  • --nolinks: 不显示提取到的链接

  • --nocolour: 避免使用pygments对输出着色

  • --depth or -d: 指定跟进链接请求的层次数(默认: 1)

  • --verbose or -v: 显示每个请求的详细信息

Item Pipline

当Item在Spider中被收集之后,它将会被传递到Item Pipeline,一些组件会按照一定的顺序执行对Item的处理。

每个item pipeline组件(有时称之为“Item Pipeline”)是实现了简单方法的Python类。他们接收到Item并通过它执行一些行为,同时也决定此Item是否继续通过pipeline,或是被丢弃而不再进行处理。

以下是item pipeline的一些典型应用:

  • 清理HTML数据
  • 验证爬取的数据(检查item包含某些字段)
  • 查重(并丢弃)
  • 将爬取结果保存到数据库中

编写自己的item pipline

  1. 重写以下方法
import json
class MyItemPipline(object):
    def __init__(self, file_name):
        self.filename = file_name 
    def open_spider(self, spider):
        # spider 开始时被调用
        sefl.file = open(self.filename, "a", encoding="utf-8")
        pass
    def close_spider(self, spider):
        # spider 结束时被调用
        self.file.close()
        pass
    def process_item(self, item, spider)
        # 每个item pipeline组件都需要调用该方法,这个方法必须返回一个 Item (或任何继承类)对象, 或是抛出 DropItem 异常,被丢弃的item将不会被之后的pipeline组件所处理。
        if item['id'] == 1:
            raise DropItem("item is get")
        else:
           self.file.write("%s\n" % json.dumps(dict(item))) 
            return item
   @classmethod
   def from_crawler(cls, crawler):
        # 从crawler的配置文件中获取配置信息 需要设置其为类方法
       return cls(
            file_name = crawler.settings.get("file_name")
       )
  1. 在配置文件最后那个添加该组件

    分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。

    ITEM_PIPELINES = {
        'crawls_house.pipelines.CrawlsHousePipeline': 300,
        'crawls_house.pipelines.CrawlsHousePipeline2': 400,
    }
    

Logging

  1. 使用日志

    from scrapy import log
    log.msg("logging warning", level=log.WARNING)
    
  2. 将日志导出到文件

    scrapy crawl danke --logfile=danke.log
    

Telent

使用telent终端访问scrapy

# 默认监听本地的6023端口
telnet localhost 6023
快捷名称描述
crawler()Scrapy Crawler (scrapy.crawler.Crawler 对象)
engine()Crawler.engine属性
spider()当前激活的爬虫(spider)
slot()the engine slot
extensions()扩展管理器(manager) (Crawler.extensions属性)
stats()状态收集器 (Crawler.stats属性)
settings()Scrapy设置(setting)对象 (Crawler.settings属性)
est()打印引擎状态的报告
prefs()针对内存调试 (参考 调试内存溢出)
p()pprint.pprint 函数的简写
hpy()针对内存调试
# 暂停爬虫
telnet localhost 6023
>>> engine.pause()
# 恢复爬虫
>>> engine.unpause()
# 停止爬虫
>>> engine.stop()

Setting 配置

# 设置 telnet 的端口
TELNETCONSOLE_PORT = [6023, 6073]
# 监听的地址
TELNETCONSOLE_HOST = '127.0.0.1'

下载中间件

动态随机User-Agent和使用代理

  1. settings.py设置

    DOWNLOADER_MIDDLEWARES = {
        'crawls_house.middlewares.CrawlsHouseDownloaderMiddleware': 543,
    }
    
  2. middlewares.pyCrawlsHouseDownloaderMiddleware类中修改

    其他的方法不要改变

    import random
    from crawls_house.settings import USER_AGENTS, PROXIES
    class CrawlsHouseDownloaderMiddleware(object):
        def process_response(self, request, response, spider):
            # 设置 user-agent
            request.headers["User-Agent"] = random.choice(USER_AGENTS)
            # 设置 proxy
            request.meta["proxy"] = "http://%s" % random.choice(PROXIES)
            return response
    

Spider中间件

图片下载

pip install Pillow
  1. 在item.py文件的需要进行图片下载的类中添加属性

    image_urls = scrapy.Field()
    image_paths = scrapy.Field()
    
  2. 在settings.py文件中添加

    ITEM_PIPELINES = {
        'crawls_house.pipelines.XMLExportItem': 300,
        # 这是自定义的
        "crawls_house.pipelines.MyImagesPipeline": 299,
        # 这是基本的 不可和自定义的同时设置
        "scrapy.contrib.pipeline.images.ImagesPipeline", 299
    }
    # 图片的下载地址
    IMAGES_STORE = r'D:\workspace\crawls_house\imgs'
    # 图片的有效时间
    IMAGES_EXPIRES = 90
    # 图片缩略图生成
    IMAGES_THUMBS = {
        'small': (50, 50),
        'big': (270, 270),
    }
    
  3. 如果使用了自定义 则需要在pipeline.py文件中添加

    from scrapy.contrib.pipeline.images import ImagesPipeline
    from scrapy.exceptions import DropItem
    class MyImagesPipeline(ImagesPipeline):
        def item_completed(self, results, item, info):
            image_paths = [x['path'] for ok, x in results if ok]
            if not image_paths:
                raise DropItem("Item contains no images")
            item['image_paths'] = image_paths
            return item
    

自动限速

# 是否启用限速扩展
AUTOTHROTTLE_ENABLED = True
# 初始限速速度(单位秒)。
AUTOTHROTTLE_START_DELAY = 5.0
# 在高延迟情况下最大的下载延迟(单位秒)。
AUTOTHROTTLE_MAX_DELAY = 60.0
# 起用AutoThrottle调试(debug)模式,展示每个接收到的response。 您可以通过此来查看限速参数是如何实时被调整的。
AUTOTHROTTLE_DEBUG = True

Jobs 暂停和恢复爬虫

  1. settings.py文件中指定

    # 爬虫的job恢复路径
    JOBDIR = "crawl_job/crawls-1"
    
  2. 在命令行指定

    scrapy crawl danke -s JOBDIR=crawls_job/danke-1
    

同一时间启动多个Spider

  1. crawls_house下创建crawlall.py文件

    from scrapy.commands import ScrapyCommand
    from scrapy.utils.project import get_project_settings
    
    class Command(ScrapyCommand):
        requires_project = True
    
        def syntax(self):
            return '[options]'
    
        def short_desc(self):
            return 'Runs all of the spiders'
    
        def run(self, args, opts):
            spider_list = self.crawler_process.spiders.list()
            for name in spider_list:
                self.crawler_process.crawl(name, **opts.__dict__)
            self.crawler_process.start()
    
    
  2. settings.py文件中添加

    COMMANDS_MODULE = 'crawls_house'
    
  3. 启动

    scrapy crawlall
    

分布式爬虫

  1. 安装scrapy-redis

    pip install scrapy-redis
    
  2. 修改settings.py文件

    # 启用Redis调度存储请求队列.
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    # 确保所有的爬虫通过Redis去重
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    # Default requests serializer is pickle, but it can be changed to any module
    # with loads and dumps functions. Note that pickle is not compatible between
    # python versions.
    # Caveat: In python 3.x, the serializer must return strings keys and support
    # bytes as values. Because of this reason the json or msgpack module will not
    # work by default. In python 2.x there is no such issue and you can use
    # 'json' or 'msgpack' as serializers.
    # 默认请求序列化使用的是pickle 但是我们可以更改为其他类似的。PS:这玩意儿2.X的可以用。3.X的不能用
    # SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"
    
    # 不清除Redis队列、这样可以暂停/恢复 爬取
    # SCHEDULER_PERSIST = True
    
    # 使用优先级调度请求队列 (默认使用)
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
    
    # 可选用的其它队列
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
    # SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
    
    # 最大空闲时间防止分布式爬虫因为等待而关闭
    # 这只有当上面设置的队列类是SpiderQueue或SpiderStack时才有效
    # 并且当您的蜘蛛首次启动时,也可能会阻止同一时间启动(由于队列为空)
    # SCHEDULER_IDLE_BEFORE_CLOSE = 10
    
    # 将清除的项目在redis进行处理
    ITEM_PIPELINES = {
        'scrapy_redis.pipelines.RedisPipeline': 1,
        'crawls_house.pipelines.XMLExportItem': 300,
        "crawls_house.pipelines.MyImagesPipeline": 299
    }
    
    # 序列化项目管道作为redis Key存储
    # REDIS_ITEMS_KEY = '%(spider)s:items'
    
    # 默认使用ScrapyJSONEncoder进行项目序列化
    # REDIS_ITEMS_SERIALIZER = 'json.dumps'
    
    # 指定连接到redis时使用的端口和地址(可选)
    REDIS_HOST = '192.168.1.203'
    REDIS_PORT = 60790
    
    # 指定用于连接redis的URL(可选)
    # 如果设置此项,则此项优先级高于设置的REDIS_HOST 和 REDIS_PORT
    # REDIS_URL = 'redis://user:pass@hostname:9001'
    
    # 自定义的redis参数(连接超时之类的)
    # REDIS_PARAMS  = {}
    # 自定义redis客户端类
    # REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient'
    
    # 如果为True,则使用redis的'spop'进行操作。
    # #如果需要避免起始网址列表出现重复,这个选项非常有用。开启此选项urls必须通过sadd添加,否则会出现类型错误。
    # REDIS_START_URLS_AS_SET = False
    
    # RedisSpider和RedisCrawlSpider默认 start_usls 键
    #REDIS_START_URLS_KEY = '%(name)s:start_urls'
    
    # 设置redis使用utf-8之外的编码
    # REDIS_ENCODING = 'latin1'
    
  3. 修改***tongcheng.py***文件
    只修改必要部分,其余部分不变

     # 引入 redisSpider类
     from scrapy_redis.spiders import RedisSpider
     # 继承该类
     class TongchengSpider(RedisSpider):
         name = 'tongcheng'
         allowed_domains = ['58.com']
         # redis中是的key
         redis_key = "tongcheng:start_urls"
    
  4. 启动爬虫,此时爬虫并没有开始爬取

    scrapy runspider spider/tongcheng.py
    
  5. 向redis中添加键,爬虫开始运行

    lpush tongcheng:start_urls https://bj.58.com/chuzu/?PGTID=0d3090a7-0000-126a-c26e-b315d60fe251&ClickID=1
    

架构概览

scrapy_architecture

组件

Scrapy Engine

引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。 详细内容查看下面的数据流(Data Flow)部分。

调度器(Scheduler)

调度器从引擎接受request并将他们入队,以便之后引擎请求他们时提供给引擎。

下载器(Downloader)

下载器负责获取页面数据并提供给引擎,而后提供给spider。

Spiders

Spider是Scrapy用户编写用于分析response并提取item(即获取到的item)或额外跟进的URL的类。 每个spider负责处理一个特定(或一些)网站。 更多内容请看 Spiders

Item Pipeline

Item Pipeline负责处理被spider提取出来的item。典型的处理有清理、 验证及持久化(例如存取到数据库中)。 更多内容查看 Item Pipeline

下载器中间件(Downloader middlewares)

下载器中间件是在引擎及下载器之间的特定钩子(specific hook),处理Downloader传递给引擎的response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。更多内容请看 下载器中间件(Downloader Middleware)

Spider中间件(Spider middlewares)

Spider中间件是在引擎及Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items及requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。更多内容请看 Spider中间件(Middleware)

数据流(Data flow)

Scrapy中的数据流由执行引擎控制,其过程如下:

  1. 引擎打开一个网站(open a domain),找到处理该网站的Spider并向该spider请求第一个要爬取的URL(s)。
  2. 引擎从Spider中获取到第一个要爬取的URL并在调度器(Scheduler)以Request调度。
  3. 引擎向调度器请求下一个要爬取的URL。
  4. 调度器返回下一个要爬取的URL给引擎,引擎将URL通过下载中间件(请求(request)方向)转发给下载器(Downloader)。
  5. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过下载中间件(返回(response)方向)发送给引擎。
  6. 引擎从下载器中接收到Response并通过Spider中间件(输入方向)发送给Spider处理。
  7. Spider处理Response并返回爬取到的Item及(跟进的)新的Request给引擎。
  8. 引擎将(Spider返回的)爬取到的Item给Item Pipeline,将(Spider返回的)Request给调度器。
  9. (从第二步)重复直到调度器中没有更多地request,引擎关闭该网站。

事件驱动网络(Event-driven networking)

Scrapy基于事件驱动网络框架 Twisted 编写。因此,Scrapy基于并发性考虑由非阻塞(即异步)的实现。

关于异步编程及Twisted更多的内容请查看下列链接:

### 关于 Scrapy-Client 的使用 尽管当前提供的引用并未直接提及 `Scrapy-Client`,但从上下文中可以推测您可能希望了解如何基于 Scrapy 和其扩展工具(如 Scrapy-Redis)构建客户端应用或执行特定操作。以下是对 Scrapy 及其相关组件的深入分析。 #### 什么是 Scrapy-Client? 严格来说,“Scrapy-Client” 并不是一个官方术语或模块名称。通常情况下,开发者会通过编写脚本调用 Scrapy 命令行接口或者利用 API 来启动爬虫实例并获取数据。这种行为可被理解为一种 “客户端模式”。因此,在此场景下讨论的是如何通过命令行或其他方式运行 Scrapy 爬虫项目[^1]。 #### 如何运行一个简单的 Scrapy 脚本 假设您的目录结构类似于引用中的描述[^2]: ```plaintext example/ ├── scrapy.cfg └── example ├── __init__.py ├── items.py ├── middlewares.py ├── pipelines.py ├── settings.py └── spiders ├── __init__.py └── my_crawlspider.py/my_redisspider.py/my_rediscrawlspider.py ``` 可以通过以下命令来运行指定的 Spider 文件: ```bash scrapy runspider example/spiders/myspider_redis.py ``` 上述命令适用于单文件形式的 Spider 实现。如果已经创建了一个完整的 Scrapy 项目,则需切换到项目根目录后执行如下指令: ```bash scrapy crawl your_spider_name ``` 其中 `your_spider_name` 应替换为您实际定义的爬虫名字。 #### 配合 Scrapy-Redis 使用 当涉及到分布式爬取时,推荐采用 **Scrapy-Redis** 插件完成任务共享与去重等功能[^3]。为了启用这些特性,需要调整配置项至 `settings.py` 中[^5]: ```python # 启用 Redis 调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 设置请求指纹存储在 Redis 中 DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter' # 将所有爬过的 URL 存入 Redis 数据库中 REDIS_URL = None # 或者提供具体的连接字符串,例如 redis://localhost:6379 ``` 以上设置允许多个节点访问同一个 Redis 实例从而协同工作。 #### 自定义中间件支持 对于某些特殊需求,比如动态代理、Cookie 处理等,可通过开发自定义 Downloader Middleware 达成目的[^4]。下面展示一段基础模板代码供参考: ```python from scrapy import signals import random class RandomUserAgentMiddleware(object): def process_request(self, request, spider): ua = random.choice(['Mozilla/5.0', 'Chrome/80']) request.headers.setdefault('User-Agent', ua) def process_response(self, request, response, spider): return response def process_exception(self, request, exception, spider): pass ``` 最后记得更新 `settings.py` 添加新类路径名: ```python DOWNLOADER_MIDDLEWARES = { 'myproject.middlewares.RandomUserAgentMiddleware': 400, } ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值