ThinkSSL🔒 一键申购 5分钟快速签发 30天无理由退款 购买更放心 广告
[TOC] 中文文档:http://shouce.jb51.net/scrapy0.24/topics/signals.html ## 1. 基础概念 编写一个爬虫程序,大概需要以下几步: 1. 编写Spider类:包含要处理的url和数据解析 2. 编写item类 : 处理解析后数据的结构化映射 3. 编写pipeline:限定数据的去处(需要在settings配置文件导入我们写的pipeline类) ### 1.1 scrapy架构 > * scrapy纯Python实现的爬虫框架,只需要些网页的分析某块,就可以实现网站数据的抓取 > * 如果是请求,scrapy交给Downloader下载,并把数据交给爬虫,如果是数据就交给ItemPipeline处理,依次循环 ![](https://box.kancloud.cn/8c591d54457bb033812a2b0364011e9c_700x494.png) > * Scrapy Engine(引擎): 负责Spider、ItemPipeline、Downloader、Scheduler中间的通讯,信号、数据传递等。 > * Scheduler(调度器): 它负责接受引擎发送过来的Request请求,并按照一定的方式进行整理排列,入队,并调用Downloader下载。当引擎需要时,交还给引擎。 > * Downloader(下载器): 负责下载scheduler(调度器)发送的所有Requests请求,并将其获取到的Responses交还给Scrapy Engine(引擎),由引擎交给Spider来处理,对应DownloaderMiddlewareManager类 > * Spider(爬虫): 它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器) > * Item Pipeline(管道): 它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方. > * Downloader Middlewares(下载中间件): 你可以当作是一个可以自定义扩展下载功能的组件。 > * Spider Middlewares(Spider中间件): 你可以理解为是一个可以自定扩展和操作引擎和Spider中间通信的功能组件(比如进入Spider的Responses;和从Spider出去的Requests) ### 1.2 数据流向 > 1. 引擎从自定义爬虫中获取初始化请求(也叫种子URL); > 2. 引擎把该请求放入调度器中,同时引擎向调度器获取一个待下载的请求(这两部是异步执行的); > 3. 调度器返回给引擎一个待下载的请求; > 4. 引擎发送请求给下载器,中间会经过一系列下载器中间件; > 5. 这个请求通过下载器下载完成后,生成一个响应对象,返回给引擎,这中间会再次经过一系列下载器中间件; > 6. 引擎接收到下载返回的响应对象后,然后发送给爬虫,执行自定义爬虫逻辑,中间会经过一系列爬虫中间件; > 7. 爬虫执行对应的回调方法,处理这个响应,完成用户逻辑后,会生成结果对象或新的请求对象给引擎,再次经过一系列爬虫中间件; > 8. 引擎把爬虫返回的结果对象交由结果处理器处理,把新的请求对象通过引擎再交给调度器; > 9. 从1开始重复执行,直到调度器中没有新的请求处理; > ### 1.3 源码脑图 ![](https://box.kancloud.cn/c843f1285a2e70b2b917f90439a939d2_1842x1279.png) ## 2. 入门实例 爬取传智播客老师的数据 ![](https://box.kancloud.cn/52279555a13b55c2b4861085f47db6ee_1161x614.png) > * 我们写爬虫,只要写Spider,item,pipeline这三个部分就可以了,并发、请求下载都是有scrapy完成 ### 2.1 安装开发环境 1. Windows 安装方式 ~~~ # python2.x pip install Scrapy # python3.x pip3 install Scrapy ~~~ Ubuntu 需要9.10或以上版本安装方式 ~~~ Python 2 / 3 安装非Python的依赖 sudo apt-get install python-dev python-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev 通过pip 安装 Scrapy 框架 sudo pip install scrapy ~~~ ### 2.2 编写爬虫程序 以爬取传智播客的数据为例 #### 2.2.1. 新建一个爬虫项目 ~~~ # 任意目录下执行 scrapy startproject mySpider ~~~ mySpider :项目名称 ![](https://box.kancloud.cn/fd9dedebd25aeefbabb7fa3bf621e185_632x222.png) mySpider目录下还有一个mySpider 将新建的项目导入intellij ![](https://box.kancloud.cn/f1294da71c6869b13207c8dece0e0079_384x349.png) ~~~ scrapy.cfg :项目的配置文件 mySpider/ :项目的Python模块,将会从这里引用代码 mySpider/items.py :项目的目标文件,爬取到的数据的映射结构 mySpider/pipelines.py :项目的管道文件,规定了爬取到的数据的去处 mySpider/settings.py :项目的设置文件 mySpider/spiders/ :存储爬虫代码目录,我们写的代码都在这里 ~~~ #### 2.2.2 Spider爬虫类 * 继承scrapy.Spider类 * 四个固定写法 > 1. name = 'tuna' 固定写法,爬虫标识名,用这个名驱动爬虫程序 > 2. allowed_domains = ['itcast.cn'] 固定写法,限制访问的域 > 3. start_urls = ['http://www.itcast.cn/channel/teacher.shtml#aandroid','http://www.itcast.cn/channel/teacher.shtml#ac'] 固定写法,定义爬虫入口url,可以写多个标识在规定域内不同类型的数据 > 4. 重写parse方法,解析HTML数据 ~~~ # -*- coding: utf-8 -*- import scrapy from mySpider.items import teacherItem import chardet class TunaSpider(scrapy.Spider): name = 'tuna' # 爬虫识别名称,唯一且不同的爬虫有不一样的名字 allowed_domains = ['itcast.cn'] # 限制搜索域名范围 start_urls = ['http://www.itcast.cn/channel/teacher.shtml#aandroid','http://www.itcast.cn/channel/teacher.shtml#ac'] # 爬虫入口url,这里爬取了传智播客的教安卓和C++的老师的数据 # 解析方法,每个初始URL完成下载后被调用,调用时传入每个URL的传回的 # response对象作为唯一的参数 def parse(self, response): teacher_list = response.xpath('//div[@class="li_txt"]') for each in teacher_list: item = teacherItem() # item映射 # 不加extract() 结果为xpath匹配对象 # 这里只匹配到一行文本,如果多行用extract()[0]只取一行信息导致不全 # 所有extract()获取整个列表,再用"".join(extract())把列表转成字符串 name = each.xpath('./h3/text()').extract()[0] # title level = each.xpath('./h4/text()').extract()[0] # info info = each.xpath('./p/text()').extract()[0] print(name[0]) # 返回的是数组,取第一个值就行,否则这样打印['胡老师'] print(level[0]) print(info[0]) # 建立item映射 item['name'] = name item['level'] = level item['info'] = info yield item ~~~ ![](https://box.kancloud.cn/c1a593fbb46ad6a45ba07ccfe76427e9_786x481.png) 我们用xpath可能匹配出来多个,也可能匹配出一个,xtract()[0] 把xpath对象转换成Unicode字符串列表,[0] 代表第一行文字 **利用模板生成spider** * 我们可以执行 ~~~ `scrapy genspider tuna "itcast.cn"` ~~~ > 命令直接生成上述Spider模板类,就不用按照条条框框写了tuna是类名,itcast.cn是限定的域名,这样就会在spiders目录下创建一个符合scrapy框架的爬虫类。 #### 2.2.3 Item类 * 继承scrapy.Item类 ~~~ # -*- coding: utf-8 -*- import scrapy """ 定义结构化字段,用来保存爬取到的数据 """ class teacherItem(scrapy.Item): name = scrapy.Field() # 这里建立了三个映射关系结构scrapy.Field(),接受Spider的数据 level = scrapy.Field() info = scrapy.Field() ~~~ #### 2.2.4 Pipelines管道类 > * 管道类负责采集到的数据,是写入文件还是数据库都随便了 > * 需要继承object类 代码,把爬取到的数据写入了文件 ~~~ # -*- coding: utf-8 -*- import json class teacherPipeline(object): # 执行一次 def __init__(self): self.file = open("teacher.josn","w") # 来数据就执行 def process_item(self,item,spider): json_dump = json.dumps(dict(item), ensure_ascii=False) + "\n" self.file.write(json_dump) # 爬虫结束执行 def close_spider(self,spider): self.file.close() ~~~ > 修改配置文件,导入管道类,修改settings文件 ![](https://box.kancloud.cn/10f041d024657d41454acfecdb1cb2da_1066x197.png) > 以下是整个工程的结构,spiders下都是我们的自定义爬虫类 ![](https://box.kancloud.cn/721f2d8a3176bb2b3015feff41487629_556x449.png) #### 2.2.5 执行爬虫程序 ~~~ # tunapipeline是我们自定义的爬虫类 scrapy crawl tunapipeline ~~~ ![](https://box.kancloud.cn/c057e07df85efb7722460a55f43f950a_1733x258.png) **如果不规定管道类,我们可以直接输出问文件 !!!!!!!** ~~~ scrapy crawl tunapipeline -o teacherllll.xml ~~~ ![](https://box.kancloud.cn/7c419a82b71b70d9082f940f513d6fff_1773x578.png) #### 2.2.6 执行流程 > 1. Scrapy engine 会把class TunaSpider(scrapy.Spider):中规定的url交给下载器 > 2. 下载器把url的数据下载下来返回给我们自定义的`class TunaSpider(scrapy.Spider):`,并调用它的`parse(self,response)`方法,把下载的数据传给response > 3. class TunaSpider(scrapy.Spider):的parse(self,response)首先对抓取的数据进行解析,然后根据item映射,如果有pipeline就按照pipeline的规则进行输出 > ## 3. 中间件 ![](https://box.kancloud.cn/59a67506ddf2f8698126694c7a18d6cb_591x356.png) * 在downloadmidware 自定义中间件,对request和response进行修改 ### 3.1 设置User-Agent 处于engine和downloader中间,处理request和response * fake-useragent开源useragent,这样就不需要我们自己来维护useragent列表了 1. 使用fake-useragent GitHub:https://github.com/hellysmile/fake-useragent 1. 安装 ~~~ pip install fake-useragent ~~~ 2. 自定义Middlware类 ~~~ class RandomUserAgentMiddlware(object): #随机更换user-agent def __init__(self, crawler): super(RandomUserAgentMiddlware, self).__init__() self.ua = UserAgent() self.ua_type = crawler.settings.get("RANDOM_UA_TYPE", "random") @classmethod def from_crawler(cls, crawler): return cls(crawler) def process_request(self, request, spider): def get_ua(): return getattr(self.ua, self.ua_type) request.headers.setdefault('User-Agent', get_ua()) ~~~ 3. 设置settings.py配置下载中间件 scrapy有一个默认的下载中间件,他默认的把User-Agent设置为scrapy,**一定要把默认的useragent优先级设置为None**所以我们要把我们定义的中间的优先级设置的高一些,让自定义的中间件后执行,这样就把默认的覆盖了。 ~~~ from scrapy import signals class UserAgentMiddleware(object): """This middleware allows spiders to override the user_agent""" def __init__(self, user_agent='Scrapy'): self.user_agent = user_agent @classmethod def from_crawler(cls, crawler): o = cls(crawler.settings['USER_AGENT']) crawler.signals.connect(o.spider_opened, signal=signals.spider_opened) return o def spider_opened(self, spider): self.user_agent = getattr(spider, 'user_agent', self.user_agent) def process_request(self, request, spider): if self.user_agent: request.headers.setdefault(b'User-Agent', self.user_agent) ~~~ ~~~ DOWNLOADER_MIDDLEWARES = { 'ArticleSpider.middlewares.RandomUserAgentMiddlware': 543, # scrapy默认的下载中间件 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, } ~~~ ### 3.2 ip代理