文章目录
scrapy介绍
scrapy是一个基于Twisted 的异步处理框架,它是纯Python实现的爬虫框架,其架构清晰, 模块之
间的耦合程度低,可扩展性极强,可以灵活完成各种需求。同时scrapy也是每位Python爬虫师的必备框架,
它可以帮助我们以少量的代码完成一个高并发,高扩展的爬虫程序。
scrapy的组成部分
组件 | 功能 | 是否需要手写实现 |
---|---|---|
Engine(引擎) | 总指挥(调度另外四个模块)负责数据和信号在不同模块间传递 | 否 |
Schedule(调度器) | 它是一个队列(默认为后入先出顺序), 将Request对象入队列和出队列 |
否 |
Spiders(爬虫) | 处理引擎发来的response 1.提取需要数据;2.提取需要的url |
是 |
Downloader(下载器) | 下载引擎发来的request请求 | 否 |
Item Pipeline (管道) | 处理从引擎传来的数据: 1.查重验证 2.清洗数据3.存入数据库 |
是 |
Downloader Middlewares (下载中间件) | 1.在request进入下载器前,对其进行处理,如添加headers、代理等 2.在response进入Spiders前,对response进行处理 |
是 |
Spider Middlewares (爬虫中间件) | 可以自定义requests请求和进行response过滤 | 一般不用 |
scrapy流程图介绍
scrapy 中的数据流由引擎控制,数据流的过程如下:
该数据流希望每位爬虫开发者牢记于心
流程顺序 | 详细解释 |
---|---|
1.Spider -> Engine -> Scheduler | Engine从Spider中获取到第一个要爬取的URL, 并通过Scheduler以Request 的形式调度 |
2.Scheduler -> Engine | Engine向Scheduler 请求下一个要爬取的Request |
3.Engine -> Downloader Middlewares -> Downloader | Scheduler返回Request给Engine Engine将Request通过Downloader Middlewares 转发给Downloader下载 |
4.Downloader -> Downloader Middlewares -> Engine | Downloader 对给定的Request生成该页面的Response 并将其通过Downloader Middle wares 发送给Engine |
5.Engine -> Spider Middlewares -> Spiders | Engine 接收到Response后 ,将其通过Spider Middlewares发送给Spider 处理 |
6.Spiders -> Spider Middlewares -> Engine | Spider 处理Response ,并通过Spider Middlewares 返回爬取到的Item和新的Request 给Engine |
7.Engine -> Item Pipeline, Engine -> Scheduler | Engine分两个方向,一是将item传向管道;二是将新的Request传给Scheduler |
8.循环2-7步骤 | 一直到Scheduler 中没有更多的Request , Engine 关闭该网站,爬取结束。 |
scrapy 文件介绍
-
spider.py
用户主要写的逻辑、代码都在该文件中
import scrapy
class HttpbinSpider(scrapy.Spider):
name = 'httpbin' # 该爬虫名称
allowed_domains = ['httpbin.com'] # 爬虫允许的域名(不在此范围的链接不会被爬取)
start_urls = ['http://httpbin.com/'] # 爬虫入口地址
def parse(self, response):
"""
用于解析start_urls列表中url的响应
:param response: 在本例中,得到是'http://httpbin.com/'的响应
:return: 1.返回的是字典或Item对象,则会进入Item Pipeline
2.返回的是Reqeust对象, 则会进入到Scheduler
# 注: 该方法一般是一个yield迭代器,用于不断生成Item对象或是Request对象
"""
pass
-
items.py
该文件主要定义用户想要获取的字段,
该文件下定义的字段名称可以与spider下的字段名称互相检查,可有效防止用户字段名称的误写
import scrapy
class TestItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
pass
-
pipelines.py
用户可定义多个pipelines,scrapy会根据定义的优先级顺序执行每个pipeline
class TestPipeline(object):
def open_spider(self, spider):
"""
用于当前spider启动时做一些初始化工作, 如:数据库的连接等
:param spider: 当前spider
:return: None
"""
pass
@classmethod
def from_crawler(cls, crawler):
"""
该方法是类方法,通过crawler, 可以拿到Scrapy的所有核心组件,如全局配置的每个信息,然后创
建一个Pipeline 实例
:param crawler:
:return: cls()实例对象
"""
pass
def process_item(self, item, spider):
"""
每个抓取到的Item对象会被该方法调用
:param item: 待处理的Item对象
:param spider: 该Item对应的spider
:return Item 对象:那么此Item会被低优先级的Item Pipeline的process_item方法处理,直到所有的方法被调用完毕
or return Drop Item 异常: 那么此Item会被丢弃,不再进行处理。
"""
return item
-
middlewares.py
与pipelines类似,用户可定义多个Middleware,scrapy会根据定义的优先级顺序执行每个Middleware
由于下载中间件的使用频率远高于爬虫中间件,所以先介绍下载中间件。
from scrapy import signals
class TestDownloaderMiddleware(object):
# Not all methods need to be defined. If a method is not defined,
# scrapy acts as if the downloader middleware does not modify the
# passed objects.
'英文大意: 并不是所有方法都需要实现,如果方法未定义,scrapy则不会修改传递来的对象'
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_request(self, request, spider):
"""
每个进入downloader前的request会被该方法调用
:param request: 需要处理的Request对象
:param spider: 该request对应的spider
# Must either: 必须返回以下其中一种
# - return None: 仅仅处理request,如头部信息
# - or return a Response object: 产生新的响应(低优先级的process_request停止调用)进入到process_response方法
# - or return a Request object: 产生新的请求进入调度器(同理低优先级的process_request停止调用)
# 然后重新顺序执行每个DownloaderMiddleware的process_request方法
# - or raise IgnoreRequest: 所有的 Downloader Middleware 的process_exception方法会依次执行。
"""
return None
def process_response(self, request, response, spider):
# Called with the response returned from the downloader.
"""
从Downloader中得到的Response在进入Spider前该方法调用
:param request: 该response对应的Request对象
:param response: 被处理的Response对象
:param spider: 该response对应的spider
# Must either;
# - return a Response object: Response会顺序通过每个DownloaderMiddleware的process_response,最后进入到Spider中
# - return a Request object: Request会重新放到调度队列里等待被调度,随后会被process_request方法依次处理。
# - or raise IgnoreRequest: Request的errorback方法会回调。如果该异常还没有被处理,那么它便会被忽略。
"""
return response
def process_exception(self, request, exception, spider):
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.
"""
当Downloader或process_request方法抛出异常时,例如抛出Ignore Request 异常,该方法就会被调用
:param request: 产生异常对象的Request
:param exception: 抛出的Exception对象
:param spider: 产生异常的spider
"""
# Must either:
# - return None: continue processing this exception
# - return a Response object: stops process_exception() chain
# - return a Request object: stops process_exception() chain
pass
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
-
settings.py
该文件存放scrapy的所有配置信息,下面我介绍一些经常用到的配置,以后待补充。
# 项目名称
BOT_NAME = 'httpbintest'
# scrapy将会在以下路径模块中寻找爬虫
SPIDER_MODULES = ['httpbintest.spiders']
# 使用genspider命令创建的爬虫模块
NEWSPIDER_MODULE = 'httpbintest.spiders'
# 设置爬虫初始化时USER_AGENT(注:如要随机更换则需要在下载中间件中设置)
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36"
# 不遵循robots.txt(默认为True,一般要置为False)
ROBOTSTXT_OBEY = False
# 设置日志输出文件路径(设置后将不再控制台输出)
# LOG_FILE = 'spider.log'
# 日志等级从低到高分为 1.DEBUG 2.INFO 3.WARNING 4.ERROR 5.CRITICAL
# 等级越高,输出的日志将越少(默认DEBUG)
# LOG_LEVEL = 'ERROR'
# 设置scrapy最大并发请求数目(默认16)
# CONCURRENT_REQUESTS = 32
# 对同一网站下的连续请求设置时间间隔(默认为0,但在大于0的情况下scrapy设置的不会是固定时间,
# 而是在0.5 * DOWNLOAD_DELAY 和 1.5 * DOWNLOAD_DELAY之间)
# DOWNLOAD_DELAY = 3
# 以下请求延迟设置只会生效其中之一:
# CONCURRENT_REQUESTS_PER_DOMAIN = 16 # 对同一域名下的请求并发数
# CONCURRENT_REQUESTS_PER_IP = 16 # 对同一ip的请求并发数
# 设置下载的页面超时时间,超出后将会抛出异常(默认180, 建议设置10秒以内)
DOWNLOAD_TIMEOUT = 8
# 设置爬取深度(默认为0,不限制深度,一般根据情况设定,可以有效防止爬虫进入死循环)
# DEPTH_LIMIT = 3
# 设置为广度优先抓取(默认为深度优先)
# DEPTH_PRIORITY = 1
# 禁用COOKIES(默认生效)
# COOKIES_ENABLED = False
# 禁用远程控制(默认生效)
# TELNETCONSOLE_ENABLED = False
# 重写默认的请求头
# DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
# }
# See https://doc.scrapy.org/en/latest/topics/spider-middleware.html
# 启用自定义的SpiderMiddleware,数值越小越接近Engine
SPIDER_MIDDLEWARES = {
'httpbintest.middlewares.HttpbintestSpiderMiddleware': 543,
}
# See https://doc.scrapy.org/en/latest/topics/downloader-middleware.html
# 启用自定义的DOWNLOADER_MIDDLEWARES, 数字越小越接近Engine,越大越靠近Downloader
# 根据scrapy数据流可以知道request依次经过数值从小到大的DOWNLOADER_MIDDLEWARE,
# 而response则会依次经过数值从大到小的DOWNLOADER_MIDDLEWARE
DOWNLOADER_MIDDLEWARES = {
'httpbintest.middlewares.ProxyMiddleware': 543,
}
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
# 启用管道(数字越小越先处理)
ITEM_PIPELINES = {
'httpbintest.pipelines.HttpbintestPipeline': 300,
}
结语:本篇是我的原创博客,其中花费了不少的心思。当然也借鉴了一些优秀书籍内容,希望能够帮助到正在学习scrapy框架的你们,其中也会有很多不足之处,欢迎指正。