一、概述

Scrapy,Python开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试.

二、Scrapy五大基本构成:

Scrapy框架主要由五大组件组成,它们分别是调度器(Scheduler)、下载器(Downloader)、爬虫(Spider)和实体管道(Item Pipeline)、Scrapy引擎(Scrapy Engine)。下面我们分别介绍各个组件的作用。
(1)、调度器(Scheduler):
调度器,说白了把它假设成为一个URL(抓取网页的网址或者说是链接)的优先队列,由它来决定下一个要抓取的网址是 什么,同时去除重复的网址(不做无用功)。用户可以自己的需求定制调度器。
(2)、下载器(Downloader):
下载器,是所有组件中负担最大的,它用于高速地下载网络上的资源。Scrapy的下载器代码不会太复杂,但效率高,主要的原因是Scrapy下载器是建立在twisted这个高效的异步模型上的(其实整个框架都在建立在这个模型上的)。
(3)、 爬虫(Spider):
爬虫,是用户最关心的部份。用户定制自己的爬虫(通过定制正则表达式等语法),用于从特定的网页中提取自己需要的信息,即所谓的实体(Item)。 用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。
(4)、 实体管道(Item Pipeline):
实体管道,用于处理爬虫(spider)提取的实体。主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。
(5)、Scrapy引擎(Scrapy Engine):
Scrapy引擎是整个框架的核心.它用来控制调试器、下载器、爬虫。实际上,引擎相当于计算机的CPU,它控制着整个流程。
image.png
官网架构图
image.png

scrapy详细工作流程
1.首先Spiders(爬虫)将需要发送请求的url(requests)经ScrapyEngine(引擎)交给Scheduler(调度器)。
2.Scheduler(排序,入队)处理后,经ScrapyEngine,DownloaderMiddlewares(可选,主要有User_Agent, Proxy代理)交给Downloader。
3.Downloader向互联网发送请求,并接收下载响应(response)。将响应(response)经ScrapyEngine,SpiderMiddlewares(可选)交给Spiders。     
4.Spiders处理response,提取数据并将数据经ScrapyEngine交给ItemPipeline保存(可以是本地,可以是数据库)。
5.提取url重新经ScrapyEngine交给Scheduler进行下一个循环。直到无Url请求程序停止结束。
Scrapy运行流程大概如下
首先,引擎从调度器中取出一个链接(URL)用于接下来的抓取
引擎把URL封装成一个请求(Request)传给下载器,下载器把资源下载下来,并封装成应答包(Response)
然后,爬虫解析Response
若是解析出实体(Item),则交给实体管道进行进一步的处理。
若是解析出的是链接(URL),则把URL交给Scheduler等待抓取

Scrapy框架爬虫的几条重要的命令
  1. 创建项目:scrapy startproject xxx
  2. 进入项目:cd xxx
  3. 基本爬虫:scrapy genspider xxx(爬虫名) xxx.com (爬取域)
  4. 还有一条是规则爬虫的命令,只是这条有变化,前俩条不变
  5. 规则爬虫:scrapy genspider -t crawl xxx(爬虫名) xxx.com (爬取域)
  6. 运行命令:scrapy crawl xxx -o xxx.json

开发Scrapy爬虫的步骤
  1. 创建项目:scrapy startproject xxx(项目名字,不区分大小写)
  2. 明确目标 (编写items.py):明确你想要抓取的目标
  3. 制作爬虫 spiders/xxspider.py):制作爬虫开始爬取网页
  4. 存储内容 pipelines.py):设计管道存储爬取内容
  5. 启动程序的py文件(start.py):等同于此命令(scrapy crawl xxx -o xxx.json
  6. from scrapy import cmdline
  7. cmdline.execute("scrapy crawl 项目名".split())

三、安装

  1. pip install scrapy
  2. #报错1: building 'twisted.test.raiser' extension
  3. # error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++
  4. # Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools
  5. 解决1
  6. # http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
  7. # Twisted‑20.3.0‑cp37‑cp37m‑win_amd64.whl
  8. # cp是你的python版本
  9. # amd是你的操作系统的版本
  10. # 下载完成之后 使用pip install twisted的路径 安装
  11. # 切记安装完twisted 再次安装scrapy
  12. # (3) 报错2 提示python -m pip install --upgrade pip
  13. # 解决2 运行python -m pip install --upgrade pip
  14. # (4) 报错3 win32的错误
  15. # 解决3 pip install pypiwin32

四、生成项目

  1. #创建项目
  2. scrapy startproject 项目名
  3. #创建爬虫
  4. scrapy genspider 爬虫名 域名
  5. #运行爬虫
  6. scrapy crawl 爬虫名

image.png
创建后目录大致页如下
firstscrapy:.
│ scrapy.cfg #项目基本配置文件
└─firstscrapy
│ items.py #定义数据结构
│ middlewares.py #中间件
│ pipelines.py #数据处理
│ settings.py #全局配置
init.py

├─spiders
│ │ baidu.py #爬虫文件
│ │ init.py

spiders下的baidu.py是scrapy自动为我们生成的
image.png
配置文件解析:settings.py

  1. # 项目名
  2. BOT_NAME = 'firstscrapy'
  3. SPIDER_MODULES = ['firstscrapy.spiders']
  4. NEWSPIDER_MODULE = 'firstscrapy.spiders'
  5. # 默认是注释的,这个东西非常重要,如果不写很容易被判断为电脑,简单点洗一个Mozilla/5.0即可
  6. # USER_AGENT = 'firstscrapy (+http://www.yourdomain.com)'
  7. USER_AGENT = 'Mozilla/5.0'
  8. # 是否遵循机器人协议,默认是true,需要改为false,否则很多东西爬不了
  9. ROBOTSTXT_OBEY = True
  10. # 最大并发数,很好理解,就是同时允许开启多少个爬虫线程
  11. # CONCURRENT_REQUESTS = 32
  12. # 下载延迟时间,单位是秒,控制爬虫爬取的频率
  13. # DOWNLOAD_DELAY = 3
  14. # The download delay setting will honor only one of:
  15. # CONCURRENT_REQUESTS_PER_DOMAIN = 16
  16. # CONCURRENT_REQUESTS_PER_IP = 16
  17. # 是否保存COOKIES,默认关闭
  18. # COOKIES_ENABLED = False
  19. # 是否启用cookies middleware。如果关闭,cookies将不会发送给web server。
  20. # TELNETCONSOLE_ENABLED = False
  21. # 默认请求头
  22. DEFAULT_REQUEST_HEADERS = {
  23. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  24. 'Accept-Language': 'en',
  25. }
  26. '''要启用spider中间件,您可以将其加入到 SPIDER_MIDDLEWARES 设置中。
  27. 该设置是一个字典,键位中间件的路径,值为中间件的顺序(order)。如上就是开启'''
  28. # SPIDER_MIDDLEWARES = {
  29. # 'firstscrapy.middlewares.FirstscrapySpiderMiddleware': 543,
  30. # }
  31. # Enable or disable downloader middlewares
  32. # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
  33. # DOWNLOADER_MIDDLEWARES = {
  34. # 'firstscrapy.middlewares.FirstscrapyDownloaderMiddleware': 543,
  35. # }
  36. '''不缓存设置中的HTTP返回值(code)的request。'''
  37. # EXTENSIONS = {
  38. # 'scrapy.extensions.telnet.TelnetConsole': None,
  39. # }
  40. # 项目管道,300为优先级,越低越爬取的优先度越高
  41. ITEM_PIPELINES = {
  42. 'firstscrapy.pipelines.FirstscrapyPipeline': 300,
  43. }
  44. # Enable and configure the AutoThrottle extension (disabled by default)
  45. # See https://docs.scrapy.org/en/latest/topics/autothrottle.html
  46. # AUTOTHROTTLE_ENABLED = True
  47. # The initial download delay
  48. # AUTOTHROTTLE_START_DELAY = 5
  49. # The maximum download delay to be set in case of high latencies
  50. # AUTOTHROTTLE_MAX_DELAY = 60
  51. # The average number of requests Scrapy should be sending in parallel to
  52. # each remote server
  53. # AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
  54. # Enable showing throttling stats for every response received:
  55. # AUTOTHROTTLE_DEBUG = False
  56. # Enable and configure HTTP caching (disabled by default)
  57. # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
  58. # HTTPCACHE_ENABLED = True
  59. '''HTTP缓存是否开启。'''
  60. # HTTPCACHE_EXPIRATION_SECS = 0
  61. '''缓存的request的超时时间,单位秒。
  62. 超过这个时间的缓存request将会被重新下载。如果为0,则缓存的request将永远不会超时。'''
  63. # HTTPCACHE_DIR = 'httpcache'
  64. '''存储(底层的)HTTP缓存的目录。如果为空,则HTTP缓存将会被关闭。 如果为相对目录,则相对于项目数据目录(project data dir)。
  65. 更多内容请参考 默认的Scrapy项目结构 。'''
  66. # HTTPCACHE_IGNORE_HTTP_CODES = []
  67. # HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'

运行:
1.终端运行
scrapy crawl baidu
image.png
2.创建运行文件。
image.png

  1. from scrapy import cmdline
  2. cmdline.execute('scrapy crawl baidu'.split())

五、日志等级与日志保存

setting.py里面可以设置日志的等级与日志存放的路径

  1. # 1.DEBUG 调试信息
  2. # 2.INFO 一般信息
  3. # 3.WARNING 警告
  4. # 4.ERROR 普通错误
  5. # 5.CRITICAL 严重错误
  6. LOG_LEVEL = "INFO"
  7. LOG_FILE = "scrapyLog.log"

六、导出为json或scv格式

执行爬虫文件时添加-o选项即可
scrapy crawl 项目名 -o .csv
scrapy crawl 项目名 -o
.json
对于json文件,在setting.js文件里添加,设置编码格式,否则会乱码:
FEED_EXPORT_ENCODING=’utf-8’

  1. from scrapy import cmdline
  2. #cmdline.execute('scrapy crawl baidu'.split())
  3. # cmdline.execute('scrapy crawl baidu -o baidu.csv'.split())
  4. cmdline.execute('scrapy crawl baidu -o baidu.json'.split())

七、Scrapy爬虫框架之Middleware文件详解

  1. from scrapy import signals
  2. # 在这里定义蜘蛛中间件的模型
  3. from itemadapter import is_item, ItemAdapter
  4. # ===========================Spider Middleware============================
  5. # 定义:介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
  6. # Spider Middleware功能:处理爬虫的请求输入和响应输出
  7. # scrapy已经提供了一些直接使用的中间件,他被SPIDER_MIDDLEWARES_BASE定义:
  8. # {
  9. # 'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 50,
  10. # 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware': 500,
  11. # 'scrapy.spidermiddlewares.referer.RefererMiddleware': 700,
  12. # 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware': 800,
  13. # 'scrapy.spidermiddlewares.depth.DepthMiddleware': 900,
  14. # }
  15. # =================SpiderMiddleware类==================
  16. class XiaoshuoSpiderMiddleware:
  17. # 类方法,参数crawler,可以通过crawler调用settings里的全局参数
  18. @classmethod
  19. def from_crawler(cls, crawler):
  20. """
  21. :param crawler: 获取settings里的全局参数,如crawler.settings.get(参数)
  22. """
  23. s = cls()
  24. # 调用spider_opened函数进行爬取数据并对该函数发送该信号。该信号一般用来分配spider的资源
  25. crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
  26. # 调用spider_closed函数进行关闭爬虫并对该函数发送该信号。该信号用来释放spider在spider_opened时占用的资源。
  27. return s
  28. # 当返回来的response被Spider Middleware处理时,该方法被调用
  29. def process_spider_input(self, response, spider):
  30. """
  31. :param response: 被Spider Middleware处理的response对象
  32. :param spider: 返回response对应的spider对象
  33. """
  34. return None
  35. # 当spider处理response对象的结果后,该方法被调用
  36. def process_spider_output(self, response, result, spider):
  37. """
  38. :param response: 被spider处理后得到结果的response对象
  39. :param result: result包含Item或request对象的可迭代对象,即spider返回的response结果
  40. :param spider: 返回response对象的spider对象
  41. """
  42. # 遍历返回的可迭代对象
  43. for i in result:
  44. yield i
  45. # 当spider的process_spider_input和process_spider_output发生异常时调用该方法
  46. def process_spider_exception(self, response, exception, spider):
  47. """
  48. :param response: 异常被抛出时被处理的response对象
  49. :param exception: 抛出的异常
  50. :param spider: 抛出该异常的spider对象
  51. """
  52. pass
  53. # 以spider启动的request为参数调用该方法,返回一个request可迭代对象
  54. def process_start_requests(self, start_requests, spider):
  55. """
  56. :param start_requests: 开始请求的可迭代对象
  57. :param spider: 开始请求所对应的spider对象
  58. """
  59. # 遍历可迭代对象
  60. for r in start_requests:
  61. yield r
  62. # 当spider开启时调用该函数,说明开始爬取数据并分配spider的资源
  63. def spider_opened(self, spider):
  64. """
  65. :param spider: 开始爬取的spider对象
  66. """
  67. spider.logger.info('Spider opened: %s' % spider.name)
  68. # # 当某个spider被关闭时,说明关闭该爬虫并释放spider在spider_opened时占用的资源。
  69. # def spider_closed(self, spider):
  70. # """
  71. # :param spider: 开始爬取的spider对象
  72. # """
  73. # spider.logger.info('Spider opened:%s'%spider.name)
  74. # ======================Downloader Middleware========================
  75. # 定义:位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。见scrapy框架图
  76. # Downloader Middleware功能:可以修改User-Agent、处理重定向、设置代理、失败重试、设置Cookies等
  77. # scrapy已经提供了一些直接使用的中间件,他被DOWNLOADER_MIDDLEWARES_BASE定义:
  78. # {
  79. # 'scrapy.contrib.downloadermiddleware.robotstxt.RobotsTxtMiddleware': 100,
  80. # 'scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware': 300,
  81. # 'scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware': 350,
  82. # 'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware': 400,
  83. # 'scrapy.contrib.downloadermiddleware.retry.RetryMiddleware': 500,
  84. # 'scrapy.contrib.downloadermiddleware.defaultheaders.DefaultHeadersMiddleware': 550,
  85. # 'scrapy.contrib.downloadermiddleware.redirect.MetaRefreshMiddleware': 580,
  86. # 'scrapy.contrib.downloadermiddleware.httpcompression.HttpCompressionMiddleware': 590,
  87. # 'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware': 600,
  88. # 'scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware': 700,
  89. # 'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware': 750,
  90. # 'scrapy.contrib.downloadermiddleware.chunked.ChunkedTransferMiddleware': 830,
  91. # 'scrapy.contrib.downloadermiddleware.stats.DownloaderStats': 850,
  92. # 'scrapy.contrib.downloadermiddleware.httpcache.HttpCacheMiddleware': 900,
  93. # }
  94. # ===============DownloaderMiddleware类=================
  95. class XiaoshuoDownloaderMiddleware:
  96. # 类方法,参数crawler,可以通过crawler调用settings里的全局参数
  97. @classmethod
  98. def from_crawler(cls, crawler):
  99. """
  100. :param crawler: 获取settings里的全局参数,如crawler.settings.get(参数)
  101. """
  102. s = cls()
  103. # 调用spider_opened函数进行爬取数据并对该函数发送该信号。该信号一般用来分配spider的资源
  104. # 调用spider_closed函数进行关闭爬虫并对该函数发送该信号。该信号用来释放spider在spider_opened时占用的资源。
  105. crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
  106. return s
  107. # request对象被Downloader Middleware执行后返回response是才调用该方法对response对象进行处理
  108. def process_request(self, request, spider):
  109. """
  110. :param request: 调度出来被Downloader Middleware处理的request对象
  111. :param response: Downloader Middleware处理request对象返回后的response对象
  112. :param spider: response返回来的spider对象
  113. """
  114. return None
  115. # 当process_request和process_response发生异常时调用
  116. def process_response(self, request, response, spider):
  117. """
  118. :param request: 产生异常的request对象
  119. :param exception: 抛出的异常对象
  120. :param spider: 产生异常的request对象的spider对象
  121. """
  122. return response
  123. # 当spider开启时调用该函数,说明开始爬取数据并分配spider的资源
  124. def process_exception(self, request, exception, spider):
  125. """
  126. :param spider: 开始爬取的spider对象
  127. """
  128. pass
  129. # # 当某个spider被关闭时,说明关闭该爬虫并释放spider在spider_opened时占用的资源。
  130. def spider_opened(self, spider):
  131. """
  132. :param spider: 开始爬取的spider对象
  133. """
  134. spider.logger.info('Spider opened: %s' % spider.name)

案例:

一、爬取腾讯视频讲解

1.创建项目

  1. scrapy startproject TXmovies
  2. cd TXmovies
  3. scrapy genspider txms v.qq.com

2.修改setting

  1. ROBOTSTXT_OBEY = False
  2. DOWNLOAD_DELAY = 1
  3. DEFAULT_REQUEST_HEADERS = {
  4. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  5. 'Accept-Language': 'en',
  6. 'User-Agent':'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36'
  7. }
  8. ITEM_PIPELINES = {
  9. 'TXmovies.pipelines.TxmoviesPipeline': 300,
  10. }

3.确认要提取的数据,item项

  1. class TxmoviesItem(scrapy.Item):
  2. name = scrapy.Field()
  3. description = scrapy.Field()

4.写爬虫程序

  1. import scrapy
  2. from ..items import TxmoviesItem
  3. class TxmsSpider(scrapy.Spider):
  4. name = 'txms'
  5. allowed_domains = ['v.qq.com']
  6. # 初始爬取路径
  7. # start_urls = [
  8. # 'https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset=0&pagesize=30']
  9. # offset = 0
  10. #
  11. # def parse(self, response):
  12. # items = TxmoviesItem()
  13. # lists = response.xpath('//div[@class="list_item"]')
  14. # for i in lists:
  15. # items['name'] = i.xpath('./a/@title').get()
  16. # items['description'] = i.xpath('./div/div/@title').get()
  17. # # 移交控制权,在本程序中,我们对item封装数据后,就调用yield把控制权给管道,管道拿到处理后return返回,又回到该程序
  18. # yield items
  19. #
  20. # if self.offset < 120:
  21. # self.offset += 30
  22. # url = 'https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30'.format(
  23. # str(self.offset))
  24. # # 回调机制,即callback,回调的对象是parse,也就是当前方法,通过不断的回调,程序将陷入循环
  25. # yield scrapy.Request(url=url, callback=self.parse)
  26. url = 'https://v.qq.com/x/bu/pagesheet/list?append=1&channel=cartoon&iarea=1&listpage=2&offset={}&pagesize=30'
  27. offset = 0
  28. #重写 spiders 包中的__init__.py方法
  29. def start_requests(self):
  30. for i in range(0, 121, 30):
  31. url = self.url.format(i)
  32. yield scrapy.Request(
  33. url=url,
  34. callback=self.parse
  35. )
  36. def parse(self, response):
  37. items = TxmoviesItem()
  38. lists = response.xpath('//div[@class="list_item"]')
  39. for i in lists:
  40. items['name'] = i.xpath('./a/@title').get()
  41. items['description'] = i.xpath('./div/div/@title').get()
  42. yield items

5.交给管道输出

  1. class TxmoviesPipeline:
  2. def process_item(self, item, spider):
  3. print(item)
  4. return item

6.run,执行项目

  1. from scrapy import cmdline
  2. cmdline.execute('scrapy crawl txms'.split())

image.png

二、爬取小说

网站

1.思路清理

首先第一级是首页,这个页面囊括了小说的分类,比如玄幻魔法,仙侠修真,都市言情等等image.png
二级页面,点击玄幻魔法,就是这个主题下的所有小说了,可以看到一共373页
image.png
三级页面,点击一篇小说进入,小说里有所有章节
image.png
四级页面,点击一篇进入,小说内容
image.png

2.创建爬虫项目

  1. scrapy startproject xiaoshuo
  2. cd xiaoshuo
  3. scrapy genspider xiaoshuo www.qb5.tw

3. 爬虫架构构思

xiaoshuo.py,这是主要的爬虫文件,主要的爬虫程序将在这个文件里写
items.py,实体类,用来存放爬取过程中的各类信息
pipelines.py,项目管道
setting.py ,用于写配置项信息

4.爬虫程序具体分析

item
首先我们要明确实体类,也就是我们要抓取的信息有什么,一个小说需要有小说名,小说类别,章节名,小说内容

  1. import scrapy
  2. class XiaoshuoItem(scrapy.Item):
  3. # define the fields for your item here like:
  4. # name = scrapy.Field()
  5. xiaoshuo_topic = scrapy.Field()
  6. xiaoshuo_name = scrapy.Field()
  7. xiaoshuo_chapter = scrapy.Field()
  8. xiaoshuo_content = scrapy.Field()

pipeLine
爬虫管道,它的作用是将得到的item信息做处理,我们的目的是爬取小说并存文件,安装小说类别-》小说名-》小说章节,这样的目录结构去存为文件

  1. import os
  2. from itemadapter import ItemAdapter
  3. class XiaoshuoPipeline:
  4. def process_item(self, item, spider):
  5. dir = "book\\" + item['xiaoshuo_topic'] + "\\" + item['xiaoshuo_name']
  6. if not os.path.exists(dir):
  7. os.makedirs(dir)
  8. # 章节有可能有空格,存文件不允许有空格字符,所以要做下处理
  9. filename = dir + "\\" + item['xiaoshuo_chapter'].replace('', '_') + ".txt"
  10. with open(filename, 'w', encoding="utf-8") as f:
  11. f.write("".join(item['xiaoshuo_content']))
  12. print('ok')
  13. return item

5.爬取
爬取分类:
第一级解析是拿到小说的主题名和主题链接,拿到一个主题后将控制器移交给第二级解析。
二级解析有两个函数,第一个是遍历小说的页数,第二个是获取某一页下的所有小说名和小说链接。遍历了一页后,将控制器交给二级解析的第二个函数,这也是整个爬虫程序设计巧妙的地方,因为他总是并发进行的。第二个函数获取到了小说链接后就进第三级解析。
第三级解析的作用是拿到某篇小说下的所有章节和链接,拿到后将控制器移交给第四级解析。
第四级解析就是拿小说的内容了,到这里爬虫程序就拿到所有的数据了,我们的item就已经被填满了,这时候就要把控制器移交给pipeLine爬虫管道,爬虫管道将创建目录和小说文件,之后return item。

  1. # -*- coding: utf-8 -*-
  2. import scrapy
  3. from ..items import XiaoshuoItem
  4. class XiaoshuoSpider(scrapy.Spider):
  5. name = 'xiaoshuo'
  6. allowed_domains = ['www.qb5.tw']
  7. start_urls = ['http://www.qb5.tw']
  8. # 一级解析:获取主题
  9. def parse(self, response):
  10. topic_list = response.xpath('/html/body/div[2]/div[1]/ul/li[position()>1 and position()<9]')
  11. for i in topic_list:
  12. xiaoshuo_topic = i.xpath('./a/@title').get()
  13. link = i.xpath('./a/@href').get()
  14. yield scrapy.Request(url=link,meta={'xiaoshuo_topic': xiaoshuo_topic},callback=self.parse_page)
  15. # 二级解析:遍历页码数
  16. def parse_page(self, response):
  17. xiaoshuo_topic = response.meta['xiaoshuo_topic']
  18. #<a href="https://www.qb5.tw/fenlei/1_376/" class="last">376</a>
  19. page_count = response.xpath('//*[@id="pagelink"]/a[@class="last"]/text()').get()
  20. url = response.url
  21. page_count = 1
  22. for i in (1, page_count + 1, 1):
  23. link = url[:-2]+str(i)+"/"
  24. yield scrapy.Request(
  25. url=link,
  26. meta={'xiaoshuo_topic': xiaoshuo_topic},
  27. callback=self.parse_two
  28. )
  29. # 二级解析:获取主题下小说
  30. def parse_two(self, response):
  31. xiaoshuo_topic = response.meta['xiaoshuo_topic']
  32. novle_lists = response.xpath('//div[@class="zp"]')
  33. for i in novle_lists:
  34. xiaoshuo_name = i.xpath('./a/@title').get()
  35. link = i.xpath('./a/@href').get()
  36. yield scrapy.Request(
  37. url=link,
  38. meta={'xiaoshuo_topic': xiaoshuo_topic, 'xiaoshuo_name': xiaoshuo_name,"requestUrl":link},
  39. callback=self.parse_three
  40. )
  41. # 三级解析:获取小说章节
  42. def parse_three(self, response):
  43. xiaoshuo_topic = response.meta['xiaoshuo_topic']
  44. xiaoshuo_name = response.meta['xiaoshuo_name']
  45. requestUrl = response.meta['requestUrl']
  46. chapter_list = response.xpath('/html/body/div[@class="zjbox"]/dl/dd')
  47. for i in chapter_list:
  48. xiaoshuo_chapter = i.xpath('./a/text()').get()
  49. link = str(requestUrl) + i.xpath('./a/@href').get()
  50. yield scrapy.Request(
  51. url=link,
  52. meta={'xiaoshuo_topic': xiaoshuo_topic, 'xiaoshuo_name': xiaoshuo_name,'xiaoshuo_chapter': xiaoshuo_chapter},
  53. callback=self.parse_fuor
  54. )
  55. # 四级解析:获取小说内容
  56. def parse_fuor(self, response):
  57. xiaoshuo_topic = response.meta['xiaoshuo_topic']
  58. xiaoshuo_name = response.meta['xiaoshuo_name']
  59. xiaoshuo_chapter = response.meta['xiaoshuo_chapter']
  60. item = XiaoshuoItem()
  61. item['xiaoshuo_topic'] = xiaoshuo_topic
  62. item['xiaoshuo_chapter'] = xiaoshuo_chapter
  63. item['xiaoshuo_name'] = xiaoshuo_name
  64. item['xiaoshuo_content'] = response.xpath('//div[@id="content"]/text()').extract()
  65. yield item

6.setiing

  1. ROBOTSTXT_OBEY = False
  2. DOWNLOAD_DELAY = 1
  3. DEFAULT_REQUEST_HEADERS = {
  4. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  5. 'Accept-Language': 'en',
  6. 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
  7. }
  8. ITEM_PIPELINES = {
  9. 'novelCrapy2.pipelines.Novelcrapy2Pipeline': 300,
  10. }

注意:在最后一级解析的地方创建item,前面解析到的小说名,小说主题,章节名通过request的meta传递。这个非常重要,如果你在四级解析之前创建了item并为item的键赋值,那么你到最后一级解析的时候需要重新为item赋值,因为程序是并发进行的,item值可能会被其它线程的值覆盖。

5.下载

地址