CodeRoadMap
路线图学习路径文章题库资源社区

浏览

首页路线图学习路径知识库题库文章资源社区我的学习
CodeRoadMap

中文编程学习导航:路线图、讲义与题库,进度可同步。

路线图学习路径文章题库社区

© 2026 CodeRoadMap

津ICP备2026012044号-1|coderoadmap@126.com
首页/Python 100 天/65.爬虫框架Scrapy简介

课程目录

  1. 0101.初识Python
  2. 0202.第一个Python程序
  3. 0303.Python语言中的变量
  4. 0404.Python语言中的运算符
  5. 0505.分支结构
  6. 0606.循环结构
  7. 0707.分支和循环结构实战
  8. 0808.常用数据结构之列表-1
  9. 0909.常用数据结构之列表-2
  10. 1010.常用数据结构之元组
  11. 1111.常用数据结构之字符串
  12. 1212.常用数据结构之集合
  13. 1313.常用数据结构之字典
  14. 1414.函数和模块
  15. 1515.函数应用实战
  16. 1616.函数使用进阶
  17. 1717.函数高级应用
  18. 1818.面向对象编程入门
  19. 1919.面向对象编程进阶
  20. 2020.面向对象编程应用
  21. 2121.文件读写和异常处理
  22. 2222.对象的序列化和反序列化
  23. 2323.Python读写CSV文件
  24. 2424.Python读写Excel文件-1
  25. 2525.Python读写Excel文件-2
  26. 2626.Python操作Word和PowerPoint文件
  27. 2727.Python操作PDF文件
  28. 2828.Python处理图像
  29. 2929.Python发送邮件和短信
  30. 3030.正则表达式的应用
  31. 3131.Python语言进阶
  32. 3232-33.Web前端入门
  33. 3334-35.玩转Linux操作系统
  34. 3436.关系型数据库和MySQL概述
  35. 3537.SQL详解之DDL
  36. 3638.SQL详解之DML
  37. 3739.SQL详解之DQL
  38. 3840.SQL详解之DCL
  39. 3941.MySQL新特性
  40. 4042.视图、函数和过程
  41. 4143.索引
  42. 4244.Python接入MySQL数据库
  43. 4345.Hive实战
  44. 4446.Django快速上手
  45. 4547.深入模型
  46. 4648.静态资源和Ajax请求
  47. 4749.Cookie和Session
  48. 4850.制作报表
  49. 4951.日志和调试工具栏
  50. 5052.中间件的应用
  51. 5153.前后端分离开发入门
  52. 5254.RESTful架构和DRF入门
  53. 5355.RESTful架构和DRF进阶
  54. 5456.使用缓存
  55. 5557.接入三方平台
  56. 5658.异步任务和定时任务
  57. 5759.单元测试
  58. 5860.项目上线
  59. 5961.网络数据采集概述
  60. 6062.用Python获取网络资源-1
  61. 6162.用Python解析HTML页面-2
  62. 6263.并发编程在爬虫中的应用
  63. 6363.Python中的并发编程-1
  64. 6463.Python中的并发编程-2
  65. 6563.Python中的并发编程-3
  66. 6664.使用Selenium抓取网页动态内容
  67. 6765.爬虫框架Scrapy简介
  68. 6866.数据分析概述
  69. 6967.环境准备
  70. 7068.NumPy的应用-1
  71. 7169.NumPy的应用-2
  72. 7270.NumPy的应用-3
  73. 7371.NumPy的应用-4
  74. 7472.深入浅出pandas-1
  75. 7573.深入浅出pandas-2
  76. 7674.深入浅出pandas-3
  77. 7775.深入浅出pandas-4
  78. 7876.深入浅出pandas-5
  79. 7977.深入浅出pandas-6
  80. 8078.数据可视化-1
  81. 8179.数据可视化-2
  82. 8280.数据可视化-3
  83. 8381.浅谈机器学习
  84. 8482.k最近邻算法
  85. 8583.决策树和随机森林
  86. 8684.朴素贝叶斯算法
  87. 8785.回归模型
  88. 8886.K-Means聚类算法
  89. 8987.集成学习算法
  90. 9088.神经网络模型
  91. 9189.自然语言处理入门
  92. 9290.机器学习实战
  93. 9391.团队项目开发的问题和解决方案
  94. 9492.Docker容器技术详解
  95. 9593.MySQL性能优化
  96. 9694.网络API接口设计
  97. 9795.使用Django开发商业项目
  98. 9896.软件测试和自动化测试
  99. 9997.电商网站技术要点剖析
  100. 10098.项目部署上线和性能调优
  101. 10199.面试中的公共问题
  102. 102100.补充内容
第 67 课1 天

65.爬虫框架Scrapy简介

当你写了很多个爬虫程序之后,你会发现每次写爬虫程序时,都需要将页面获取、页面解析、爬虫调度、异常处理、反爬应对这些代码从头至尾实现一遍,这里面有很多工作其实都是

课程内容

爬虫框架Scrapy简介

当你写了很多个爬虫程序之后,你会发现每次写爬虫程序时,都需要将页面获取、页面解析、爬虫调度、异常处理、反爬应对这些代码从头至尾实现一遍,这里面有很多工作其实都是简单乏味的重复劳动。那么,有没有什么办法可以提升我们编写爬虫代码的效率呢?答案是肯定的,那就是利用爬虫框架,而在所有的爬虫框架中,Scrapy 应该是最流行、最强大的框架。

Scrapy 概述

Scrapy 是基于 Python 的一个非常流行的网络爬虫框架,可以用来抓取 Web 站点并从页面中提取结构化的数据。下图展示了 Scrapy 的基本架构,其中包含了主要组件和系统的数据处理流程(图中带数字的红色箭头)。

Scrapy的组件

我们先来说说 Scrapy 中的组件。

  1. Scrapy 引擎(Engine):用来控制整个系统的数据处理流程。
  2. 调度器(Scheduler):调度器从引擎接受请求并排序列入队列,并在引擎发出请求后返还给它们。
  3. 下载器(Downloader):下载器的主要职责是抓取网页并将网页内容返还给蜘蛛(Spiders)。
  4. 蜘蛛程序(Spiders):蜘蛛是用户自定义的用来解析网页并抓取特定URL的类,每个蜘蛛都能处理一个域名或一组域名,简单的说就是用来定义特定网站的抓取和解析规则的模块。
  5. 数据管道(Item Pipeline):管道的主要责任是负责处理有蜘蛛从网页中抽取的数据条目,它的主要任务是清理、验证和存储数据。当页面被蜘蛛解析后,将被发送到数据管道,并经过几个特定的次序处理数据。每个数据管道组件都是一个 Python 类,它们获取了数据条目并执行对数据条目进行处理的方法,同时还需要确定是否需要在数据管道中继续执行下一步或是直接丢弃掉不处理。数据管道通常执行的任务有:清理 HTML 数据、验证解析到的数据(检查条目是否包含必要的字段)、检查是不是重复数据(如果重复就丢弃)、将解析到的数据存储到数据库(关系型数据库或 NoSQL 数据库)中。
  6. 中间件(Middlewares):中间件是介于引擎和其他组件之间的一个钩子框架,主要是为了提供自定义的代码来拓展 Scrapy 的功能,包括下载器中间件和蜘蛛中间件。

数据处理流程

Scrapy 的整个数据处理流程由引擎进行控制,通常的运转流程包括以下的步骤:

  1. 引擎询问蜘蛛需要处理哪个网站,并让蜘蛛将第一个需要处理的 URL 交给它。

  2. 引擎让调度器将需要处理的 URL 放在队列中。

  3. 引擎从调度那获取接下来进行爬取的页面。

  4. 调度将下一个爬取的 URL 返回给引擎,引擎将它通过下载中间件发送到下载器。

  5. 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎;如果下载失败了,引擎会通知调度器记录这个 URL,待会再重新下载。

  6. 引擎收到下载器的响应并将它通过蜘蛛中间件发送到蜘蛛进行处理。

  • 蜘蛛处理响应并返回爬取到的数据条目,此外还要将需要跟进的新的 URL 发送给引擎。

  • 引擎将抓取到的数据条目送入数据管道,把新的 URL 发送给调度器放入队列中。

  • 上述操作中的第2步到第8步会一直重复直到调度器中没有需要请求的 URL,爬虫就停止工作。

    安装和使用Scrapy

    可以使用 Python 的包管理工具pip来安装 Scrapy。

    pip install scrapy
    

    在命令行中使用scrapy命令创建名为demo的项目。

    scrapy startproject demo
    

    项目的目录结构如下图所示。

    demo
    |____ demo
    |________ spiders
    |____________ __init__.py
    |________ __init__.py
    |________ items.py
    |________ middlewares.py
    |________ pipelines.py
    |________ settings.py
    |____ scrapy.cfg
    

    切换到demo 目录,用下面的命令创建名为douban的蜘蛛程序。

    scrapy genspider douban movie.douban.com
    

    一个简单的例子

    接下来,我们实现一个爬取豆瓣电影 Top250 电影标题、评分和金句的爬虫。

    1. 在items.py的Item类中定义字段,这些字段用来保存数据,方便后续的操作。

      import scrapy
      
      
      class DoubanItem(scrapy.Item):
          title = scrapy.Field()
          score = scrapy.Field()
          motto = scrapy.Field()
      
    2. 修改spiders文件夹中名为douban.py 的文件,它是蜘蛛程序的核心,需要我们添加解析页面的代码。在这里,我们可以通过对Response对象的解析,获取电影的信息,代码如下所示。

      import scrapy
      from scrapy import Selector, Request
      from scrapy.http import HtmlResponse
      
      from demo.items import MovieItem
      
      
      class DoubanSpider(scrapy.Spider):
          name = 'douban'
          allowed_domains = ['movie.douban.com']
          start_urls = ['https://movie.douban.com/top250?start=0&filter=']
      
          def parse(self, response: HtmlResponse):
              sel = Selector(response)
              movie_items = sel.css('#content > div > div.article > ol > li')
              for movie_sel in movie_items:
                  item = MovieItem()
                  item['title'] = movie_sel.css('.title::text').extract_first()
                  item['score'] = movie_sel.css('.rating_num::text').extract_first()
                  item['motto'] = movie_sel.css('.inq::text').extract_first()
                  yield item
      

      通过上面的代码不难看出,我们可以使用 CSS 选择器进行页面解析。当然,如果你愿意也可以使用 XPath 或正则表达式进行页面解析,对应的方法分别是xpath和re。

      如果还要生成后续爬取的请求,我们可以用yield产出Request对象。Request对象有两个非常重要的属性,一个是url,它代表了要请求的地址;一个是callback,它代表了获得响应之后要执行的回调函数。我们可以将上面的代码稍作修改。

      import scrapy
      from scrapy import Selector, Request
      from scrapy.http import HtmlResponse
      
      from demo.items import MovieItem
      
      
      class DoubanSpider(scrapy.Spider):
          name = 'douban'
          allowed_domains = ['movie.douban.com']
          start_urls = ['https://movie.douban.com/top250?start=0&filter=']
      
          def parse(self, response: HtmlResponse):
              sel = Selector(response)
              movie_items = sel.css('#content > div > div.article > ol > li')
              for movie_sel in movie_items:
                  item = MovieItem()
                  item['title'] = movie_sel.css('.title::text').extract_first()
                  item['score'] = movie_sel.css('.rating_num::text').extract_first()
                  item['motto'] = movie_sel.css('.inq::text').extract_first()
                  yield item
      
              hrefs = sel.css('#content > div > div.article > div.paginator > a::attr("href")')
              for href in hrefs:
                  full_url = response.urljoin(href.extract())
                  yield Request(url=full_url)
      

      到这里,我们已经可以通过下面的命令让爬虫运转起来。

      scrapy crawl movie
      

      可以在控制台看到爬取到的数据,如果想将这些数据保存到文件中,可以通过-o参数来指定文件名,Scrapy 支持我们将爬取到的数据导出成 JSON、CSV、XML 等格式。

      scrapy crawl moive -o result.json
      

      不知大家是否注意到,通过运行爬虫获得的 JSON 文件中有275条数据,那是因为首页被重复爬取了。要解决这个问题,可以对上面的代码稍作调整,不在parse方法中解析获取新页面的 URL,而是通过start_requests方法提前准备好待爬取页面的 URL,调整后的代码如下所示。

      import scrapy
      from scrapy import Selector, Request
      from scrapy.http import HtmlResponse
      
      from demo.items import MovieItem
      
      
      class DoubanSpider(scrapy.Spider):
          name = 'douban'
          allowed_domains = ['movie.douban.com']
      
          def start_requests(self):
              for page in range(10):
                  yield Request(url=f'https://movie.douban.com/top250?start={page * 25}')
      
          def parse(self, response: HtmlResponse):
              sel = Selector(response)
              movie_items = sel.css('#content > div > div.article > ol > li')
              for movie_sel in movie_items:
                  item = MovieItem()
                  item['title'] = movie_sel.css('.title::text').extract_first()
                  item['score'] = movie_sel.css('.rating_num::text').extract_first()
                  item['motto'] = movie_sel.css('.inq::text').extract_first()
                  yield item
      
    3. 如果希望完成爬虫数据的持久化,可以在数据管道中处理蜘蛛程序产生的Item对象。例如,我们可以通过前面讲到的openpyxl操作 Excel 文件,将数据写入 Excel 文件中,代码如下所示。

      import openpyxl
      
      from demo.items import MovieItem
      
      
      class MovieItemPipeline:
      
          def __init__(self):
              self.wb = openpyxl.Workbook()
              self.sheet = self.wb.active
              self.sheet.title = 'Top250'
              self.sheet.append(('名称', '评分', '名言'))
      
          def process_item(self, item: MovieItem, spider):
              self.sheet.append((item['title'], item['score'], item['motto']))
              return item
      
          def close_spider(self, spider):
              self.wb.save('豆瓣电影数据.xlsx')
      

      上面的process_item和close_spider都是回调方法(钩子函数), 简单的说就是 Scrapy 框架会自动去调用的方法。当蜘蛛程序产生一个Item对象交给引擎时,引擎会将该Item对象交给数据管道,这时我们配置好的数据管道的parse_item方法就会被执行,所以我们可以在该方法中获取数据并完成数据的持久化操作。另一个方法close_spider是在爬虫结束运行前会自动执行的方法,在上面的代码中,我们在这个地方进行了保存 Excel 文件的操作,相信这段代码大家是很容易读懂的。

      总而言之,数据管道可以帮助我们完成以下操作:

      • 清理 HTML 数据,验证爬取的数据。
      • 丢弃重复的不必要的内容。
      • 将爬取的结果进行持久化操作。
    4. 修改settings.py文件对项目进行配置,主要需要修改以下几个配置。

      # 用户浏览器
      USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
      
      # 并发请求数量 
      CONCURRENT_REQUESTS = 4
      
      # 下载延迟
      DOWNLOAD_DELAY = 3
      # 随机化下载延迟
      RANDOMIZE_DOWNLOAD_DELAY = True
      
      # 是否遵守爬虫协议
      ROBOTSTXT_OBEY = True
      
      # 配置数据管道
      ITEM_PIPELINES = {
         'demo.pipelines.MovieItemPipeline': 300,
      }
      

      说明:上面配置文件中的ITEM_PIPELINES选项是一个字典,可以配置多个处理数据的管道,后面的数字代表了执行的优先级,数字小的先执行。

    ← 上一课64.使用Selenium抓取网页动态内容下一课 →66.数据分析概述