Scrapy & CrawlSpider

一、Scrapy

1、概述

Scrapy是用python实现的一个为了爬取网站数据,提取结构性数据而编写的应用框架,使用Twisted高效异步网络框架来处理网络通信。它可以应用在广泛领域:Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。

2、架构

scrapy架构

  • Scrapy Engine(引擎):负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。此组件相当于爬虫的“大脑”,是 整个爬虫的调度中心。
  • Scheduler(调度器):它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,当引擎需要时,交还给引擎。初始爬取url和后续在页面里爬到的待爬取url放入调度器中,等待被爬取。调度器会自动去掉重复的url。
  • Downloader(下载器):负责下载Scrapy Engine(引擎)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理。
  • Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)。
  • Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。
  • Downloader Middlewares(下载中间件):一个可以自定义扩展下载功能的组件。
  • 是在引擎和下载器之间的特定钩子(specific hook),处理它们之间的请求(request)和响应(response)。提供了一个简单的机制,通过插入自定义代码来扩展Scrapy功能。通过设置DownloaderMiddlewares来实现爬虫自动更换user-agent,IP等。
  • Spider Middlewares(Spider中间件):一个可以自定扩展和操作引擎和Spider中间通信的功能组件。是在引擎和Spider之间的特定钩子(specific hook),处理spider的输入(response)和输出(items或requests)。提供了同样简单机制,通过插入自定义代码来扩展Scrapy功能。

Scrapy数据流:

  1. ScrapyEngine打开一个网站,找到处理该网站的Spider,并向该Spider请求第一个(批)要爬取的url(s);
  2. ScrapyEngine向调度器请求第一个要爬取的url,并加入到Schedule作为请求以备调度;
  3. ScrapyEngine向调度器请求下一个要爬取的url;
  4. Schedule返回下一个要爬取的url给ScrapyEngine,ScrapyEngine通过DownloaderMiddlewares将url转发给Downloader;
  5. 页面下载完毕,Downloader生成一个页面的Response,通过DownloaderMiddlewares发送给ScrapyEngine;
  6. ScrapyEngine从Downloader中接收到Response,通过SpiderMiddlewares发送给Spider处理;
  7. Spider处理Response并返回提取到的Item以及新的Request给ScrapyEngine;
  8. ScrapyEngine将Spider返回的Item交给ItemPipeline,将Spider返回的Request交给Schedule进行从第二步开始的重复操作,直到调度器中没有待处理的Request,ScrapyEngine关闭。

3、语法

  • 安装scrapy:pip install **

    Z:\Program Files\Python\Python312\Scripts> pip install scrapy -i https://pypi.douban.com/simple
    
  • 创建项目:scrapy startproject **

    Z:\pwork> scrapy startproject project
    
  • 创建爬虫文件:scrapy genspider 爬虫文件名称 域名 (爬虫文件名称不能跟项目名称一致)

    Z:\pwork> cd project\project\spiders
    Z:\pwork\project\project\spiders> scrapy genspider baidu www.baidu.com
    
  • 运行爬虫代码:scrapy crawl **

    Z:\pwork\project\project\spiders> scrapy crawl baidu
    

    注释掉项目中setting.py文件中ROBOTSTXT_OBEY = True(爬虫协议)

4、项目结构说明

项目名称

​ 项目名称

​ spiders ===> 存储爬虫文件

​ init

​ 自定义爬虫文件 ===> 核心功能文件

​ init

​ items ===> 定义数据结构

​ middleware ===> 中间件(代理)

​ pipelines ===> 管道,用来处理下载的数据

​ settings ===> 配置文件(robots协议、ua定义等)

5、settings.py

# Scrapy settings for scrapy_project project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
#     https://docs.scrapy.org/en/latest/topics/settings.html
#     https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#     https://docs.scrapy.org/en/latest/topics/spider-middleware.html

BOT_NAME = "scrapy_project"

SPIDER_MODULES = ["scrapy_project.spiders"]
NEWSPIDER_MODULE = "scrapy_project.spiders"


# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = "scrapy_project (+http://www.yourdomain.com)"

# Obey robots.txt rules
# 注掉君子协议
# ROBOTSTXT_OBEY = True

# 日志等级修改
LOG_LEVEL = 'WARNING'

LOG_FILE = 'logfile.log'

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32

# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
#DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#CONCURRENT_REQUESTS_PER_IP = 16

# Disable cookies (enabled by default)
#COOKIES_ENABLED = False

# Disable Telnet Console (enabled by default)
#TELNETCONSOLE_ENABLED = False

# Override the default request headers:
#DEFAULT_REQUEST_HEADERS = {
#    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
#    "Accept-Language": "en",
#}

# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
#    "scrapy_project.middlewares.ScrapyProjectSpiderMiddleware": 543,
#}

# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
#DOWNLOADER_MIDDLEWARES = {
#    "scrapy_project.middlewares.ScrapyProjectDownloaderMiddleware": 543,
#}

# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
#EXTENSIONS = {
#    "scrapy.extensions.telnet.TelnetConsole": None,
#}

# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
# 开启管道处理
ITEM_PIPELINES = {
    # 管道优先级范围:1-1000,值越小优先级越高
   "scrapy_project.pipelines.ScrapyProjectPipeline": 300,
}

# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
#AUTOTHROTTLE_ENABLED = True
# The initial download delay
#AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
#AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
#AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
#AUTOTHROTTLE_DEBUG = False

# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
#HTTPCACHE_ENABLED = True
#HTTPCACHE_EXPIRATION_SECS = 0
#HTTPCACHE_DIR = "httpcache"
#HTTPCACHE_IGNORE_HTTP_CODES = []
#HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"

# Set settings whose default value is deprecated to a future-proof value
REQUEST_FINGERPRINTER_IMPLEMENTATION = "2.7"
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
FEED_EXPORT_ENCODING = "utf-8"

6、Scrapy Selector

Selector有四个基本的方法:

  • xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
  • extract(): 序列化该节点为Unicode字符串并返回list
  • css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表,语法同
    BeautifulSoup4
  • re(): 根据传入的正则表达式对数据进行提取,返回Unicode字符串list列表

7、Scrapy shell

scrapy终端,是一个交互终端,供您在未启动spider的情况下尝试及调试您的爬取代码。其本意是用来测试提取数据的代码,不过您可以将其作为正常的python终端,在上面测任何的python代码。该终端是用来测试Xpath或css表达式,查看他们的工作方式及从爬取的网页中提取的数据。

如果安装了 IPython ,Scrapy终端将使用 IPython (替代标准Python终端)。 IPython 终端与其他相比更为强大,提供智能的自动补全,高亮输出,及其他特性。

Z:\Program Files\Python\Python312\Scripts> pip install scrapy -i https://pypi.douban.com/simple

Z:\pwork>scrapy shell www.baidu.com
...
2024-02-20 13:56:29 [asyncio] DEBUG: Using selector: SelectSelector
In [1]:

Scrapy Shell根据下载的页面会自动创建一些方便使用的对象,例如 Response 对象,以及 Selector 对象 (对HTML及XML内容)。

  • 当shell载入后,将得到一个包含response数据的本地 response 变量,输入 response.body 将输出response的包体,输出 response.headers 可以看到response的包头
  • 输入 response.selector 时, 将获取到一个response 初始化的类 Selector 的对象,此时可以通过使用 response.selector.xpath() 或 response.selector.css() 来对 response 进行查询
  • Scrapy也提供了一些快捷方式,例如 response.xpath() 或 response.css() 同样可以生效

8、文件模板

使用 scrapy genspider 生成爬虫文件时,可以使用-t参数指定生成文件的基础模板,默认使用basic模板。

basic模板文件相对路径:Python\Python312\Lib\site-packages\scrapy\templates\spiders\basic.tmpl

basic模板文件路径查询步骤:在pycharm中爬虫文件中导入scrapy包:import scrapy,使用 ctrl + 鼠标左键 进入scrapy源码,basic文件位于其同级目录下templates\spiders文件夹中。

可直接复制一份为mytemplate.tmpl,在其中添加自己的处理逻辑,之后通过命令 scrapy genspider -t mytemplate test test.com创建爬虫文件

可使用命令 scrapy genspider --list 查询模板文件列表

9、运行方式

在终端可通过执行命令scrapy crawl example来运行对应爬虫文件,在pycharm中可通过__main__方法执行

在项目的根目录下(scrapy.cf同级目录下,settings.py的上一层目录下)添加文件main.py(可以是其他名称),main.py文件代码如下:

from scrapy.cmdline import execute
import os
import sys

if __name__ == '__main__':
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    # example即为爬虫文件名称
    execute(['scrapy', 'crawl', 'example'])

10、示例

爬取当当网十页数据

  • spilder example.py

    import scrapy
    from scrapy_project.items import ScrapyProjectItem
    
    
    class BaiduSpider(scrapy.Spider):
        name = "example"
        allowed_domains = ["category.dangdang.com"]
        start_urls = ["https://category.dangdang.com/cid4002145.html"]
    
        base_url = "https://category.dangdang.com/pg"
        page = 1
    
        def parse(self, response, **kwargs):
            targets = response.xpath('//ul[@id="component_47"]/li')
    
            for target in targets:
                src = target.xpath('.//img/@data-original').extract_first()
                if not src:
                    src = target.xpath('.//img/@src').extract_first()
                name = target.xpath('.//img/@alt').extract_first()
                price = target.xpath('.//p[@class="price"]').extract_first()
    
                item = ScrapyProjectItem(src=src, name=name, price=price)
    
                yield item
    
            if self.page < 10:
                self.page = self.page + 1
    
                url = self.base_url + str(self.page) + '-cid4002145.html'
    
                yield scrapy.Request(url=url, callback=self.parse)
    
    
  • items.py

    import scrapy
    
    
    class ScrapyProjectItem(scrapy.Item):
        src = scrapy.Field()
        name = scrapy.Field()
        price = scrapy.Field()
    
    
  • pipeline.py

    import os
    import re
    import urllib.request
    
    
    # 数据保存管道
    class SaveDataPipeline:
    
        def __init__(self):
            self.fp = None
    
        def open_spider(self, spider):
            path = os.getcwd() + '\\scrapy_project\\spiders\\result.json'
            self.fp = open(path, 'a', encoding='utf8')
    
        def process_item(self, item, spider):
            self.fp.write(str(item))
            return item
    
        def close_spider(self, spider):
            self.fp.close()
    
    
    # 图片下载管道
    class DownloadImagesPipeline:
        def __init__(self):
            self.path = None
    
        def open_spider(self, spider):
            # picture文件夹需手动创建
            self.path = os.path.join(os.getcwd(), 'scrapy_project', 'spiders', 'picture')
    
        def process_item(self, item, spider):
            url = 'http:' + item.get('src')
            filename = os.path.join(self.path, re.sub(r'[\\/:*?"<>|]', '', item.get('name'))) + '.jpg'
            urllib.request.urlretrieve(url=url, filename=filename)
    
            return item
    
    
  • settings.py

    # 注释遵循协议
    # ROBOTSTXT_OBEY = True
    
    # 管道
    ITEM_PIPELINES = {
       "scrapy_project.pipelines.SaveDataPipeline": 300,
       "scrapy_project.pipelines.DownloadImagesPipeline": 301,
    }
    

11、推荐阅读

二、CrawlSpider

1、简介

CrawlSpider 是 Scrapy 框架提供的一个特殊的 Spider 类型,除了继承到Spider的特性和功能外,还派生除了其自己独有的更加强大的特性和功能,其中最显著的功能就是”LinkExtractors链接提取器“。在Scrapy中Spider是所有爬虫的基类,其设计原则只是为了爬取start_url列表中网页。CrawlSpider 是基于广度优先算法构建的,可以自动发现并跟踪网页上的链接,并根据预定义的规则提取数据,用于处理那些需要遵循特定规则和链接提取的网站。

CrawlSpider 提供了一种更高级的方法来定义爬取规则,而无需编写大量的重复代码。它基于规则系统工作,其中每个规则由一个或多个链接提取器(LinkExtractor)和一个回调函数(callback)组成。规则定义了要提取的链接和如何处理这些链接的方法。

2、基本运行

适用于先爬取start_url列表中的网页,再从爬取的网页中获取link并继续爬取的工作。

数据流转

  1. 链接提取器(LinkExtractor):链接提取器用于从网页中提取链接。CrawlSpider 提供了几种内置的链接提取器,如基于正则表达式、基于 CSS 选择器、基于 XPath 等,你可以根据需求选择合适的链接提取器。
  2. 规则(Rule):规则定义了要提取的链接和如何处理这些链接的方法。每个规则由一个链接提取器和一个回调函数组成。链接提取器用于提取链接,回调函数定义了如何处理这些链接。可以定义多个规则来处理不同类型的链接。
  3. 回调函数(callback):回调函数是指定规则要调用的方法。当链接提取器提取到链接时,将会调用相应的回调函数来处理提取到的链接。在回调函数中,你可以编写解析页面和提取数据的逻辑。
  4. follow 参数:在规则中,可以设置 follow 参数来决定是否继续跟踪从链接提取器提取的链接。如果设置为 True,则会继续跟踪这些链接并提取数据;如果设置为 False,则不会跟踪这些链接。
  5. allowed_domains 参数:allowed_domains 参数用于限制爬取的域名。只有在 allowed_domains 列表中的域名下的链接才会被跟踪和提取数据,其他域名下的链接将被忽略。

3、核心对象

  • Rule对象:每个Rule对爬取网站的动作定义了特定的操作。如果多个rule匹配了相同的链接,则根据规则在本集合中被定义的顺序,第一个会被使用

    class scrapy.contrib.spiders.Rule(
        link_extractor=None,
        callback=None,
        cb_kwargs=None,
        follow=None,
        process_links=None,
        process_request=None,
        errback=None,
    ) 
    
    • link_extractor为LinkExtractor,用于定义需要提取的链接
    • callback参数:当link_extractor获取到链接时参数所指定的值作为回调函数,回调函数尽量不要用parse方法,crawlspider已使用了parse方法
    • follow:指定了根据该规则从response提取的链接是否需要跟进。当callback为None,默认值为True
    • process_links:主要用来过滤由link_extractor获取到的链接
    • process_request:主要用来过滤在rule中提取到的request
  • LinkExtractors 链接提取器:提取链接。 每个LinkExtractors有唯一的公共方法是extract_links(),他接收一个response对象,并返回一个scrapy.link.Link对象。Linkextractors要实例化一次,并且extract_links方法会根据不同的response调用多次提取链接。

    class scrapy.linkextractors.LinkExtractor(
            allow=(),
            deny=(),
            allow_domains=(),
            deny_domains=(),
            restrict_xpaths=(),
            tags=("a", "area"),
            attrs=("href",),
            canonicalize=False,
            unique=True,
            process_value=None,
            deny_extensions=None,
            restrict_css=(),
            strip=True,
            restrict_text=None,
    )
    
    • allow:满足括号中“正则表达式”的值会被提取,如果为空,则会全部匹配。
    • deny:与这个正则表达式(或正则表达式列表)不匹配的URL一定不提取
    • allow_domains:会被提取的链接domains。
    • deny_domains:一定不会被提取链接的domains
    • restrick_xpaths:使用xpath表达式,和allow共同作用过滤链接(只选到节点,不选到属性)
    • restrict_css:使用css表达式,和allow共同作用过滤链接(只选到节点,不选到属性)

4、示例

爬取读书网业务代码:

from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from crawl_spider_project.items import CrawlSpiderProjectItem


class ExampleSpider(CrawlSpider):
    name = "example"
    allowed_domains = ["www.dushu.com"]
    start_urls = ["https://www.dushu.com/book/1211_1.html"]

    rules = (Rule(LinkExtractor(allow=r"/book/1211_\d+\.html"), callback="parse_item", follow=False),)

    @staticmethod
    def parse_item(response):
        book_list = response.xpath('//div[@class="bookslist"]//img')

        for book in book_list:
            src = book.xpath('./@data-original').extract_first()
            name = book.xpath('./@alt').extract_first()

            item = CrawlSpiderProjectItem(src=src, name=name)

            yield item

5、推荐阅读

  • 23
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值