Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。 能够应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。javascript
Scrapy官方架构图 css
这里咱们以爬取马蜂窝问答页面(www.mafengwo.cn/wenda)的文章为例,说明如何构建一个Scrapy爬虫java
#沙箱环境 virtualenv mafengwoenv source mafengwoenv/bin/activate #进入沙箱环境 #安装两个兼容包 pip install cryptography ndg-httpsclient #pip安装本次爬虫项目依赖的包 pip install scrapy #爬虫框架 pip install pymongo #mongo引擎的python驱动
#建立一个名为 mafengwo 的项目 scrapy startproject mafengwo
将默认建立以下结构层次的项目node
mafengwo ├── mafengwo │ ├── __init__.py │ ├── items.py #定义抽象数据模型 │ ├── pipelines.py #定义数据处理管道 │ ├── settings.py #配置文件 │ └── spiders #存放项目全部爬虫 │ └── __init__.py └── scrapy.cfg
为告终构化数据,咱们须要定义爬取数据结构的抽象模型(严格的说,Item不是必须的,你也能够直接在spider中返回dict数据,可是使用Item能得到额外的数据验证机制)
在 mafengwo/mafengwo/items.py 文件中定义咱们须要爬取的标题、做者、时间和内容属性:python
# -*- coding: utf-8 -*- import scrapy class WendaItem(scrapy.Item): title = scrapy.Field() author = scrapy.Field() time = scrapy.Field() content = scrapy.Field()
#从基础爬虫模板建立咱们的爬虫 scrapy genspider --template basic wenda www.mafengwo.cn/wenda
编辑生成的爬虫程序 mafengwo/mafengwo/spiders/wenda.py,完善咱们的数据爬取逻辑:正则表达式
# -*- coding: utf-8 -*- import scrapy from mafengwo import items class WendaSpider(scrapy.Spider): name = "wenda" allowed_domains = ["www.mafengwo.cn"] #必须是和start_urls一致的域名,且不能跟上目录 start_urls = [ 'http://www.mafengwo.cn/wenda/', ] #框架默认的页面解析器入口,start_urls页面将被传入 def parse(self, response): #遍历文章列表 for link in response.xpath("//ul[@class='_j_pager_box']/li"): url = link.xpath("div[@class='wen']/div[@class='title']/a/@href").extract_first() url = response.url + url[7:] #详情页地址 yield scrapy.Request(url, callback=self.parse_detail) #跟进详情页 #当前页条目抓取完毕后,跟进下一页 # next = response.xpath('xxx').extract_first() # if next: # yield scrapy.Request(next, self.parse) #咱们自定义的详情页解析器 def parse_detail(self, response): item = items.WendaItem() #抽取页面信息存入模型 item['author'] = response.xpath("//div[@class='pub-bar fr']/a[@class='name']/text()").extract_first() item['title'] = response.xpath("//div[@class='q-title']/h1/text()").extract_first() item['time'] = response.xpath("//div[@class='pub-bar fr']/span[@class='time']/span/text()").extract_first() item['content'] = response.xpath("//div[@class='q-desc']/text()").extract_first() yield item
DOM调试
scrapy使用Scrapy Selectors(基于XPath 和 CSS )从网页提取数据shell
Selector有四个基本方法:数据库
xpath简要:浏览器
绝对路径
、相对路径
两种,并由 路径表达式
组成:
/
根节点//
匹配节点.
当前节点..
父节点路径表达式
由 步进表达式
组成(轴::节点测试[谓语]):
preceding
、preceding-sibling
、self
、following-sibling
、following
、ancestor
、parent
、child
、attribute
...节点名
、*
、text()
、node()
...[索引数字]
、[last()]
、[@class="hot"]
...咱们能够经过如下指令进入ScrapyShell来测试xpath规则bash
scrapy shell --nolog 'http://www.mafengwo.cn/wenda/' #进入交互式工具 >>>sel.xpath('/div/span') #shell中测试xpath >>>fetch("http://www.mafengwo.cn") #切换页面,将会刷新response等对象
咱们也能够直接在爬虫解析器中嵌入一个钩子 scrapy.shell.inspect_response(response, self)
,从而在爬取过程当中回调到ScrapyShell,并在当时特定场景下进行调试
特别的,对于xpath,咱们也能够在浏览器console中经过以下方法来测试xpath
$x('规则')
parse解析器调试
咱们能够经过如下命令来调试解析器对页面数据的分析状况
scrapy parse --spider=爬虫名 -c 解析器名 -d 跟进深度 -v 调试地址
当Item数据在Spider中被收集以后,它将会被传递到Item Pipeline,并按序执行全部管道 管道接收到Item后能够执行自定义逻辑,同时也决定此Item是否继续经过pipeline,或是被丢弃 管道典型的运用:
爬虫数据经常使用的持久化策略是mongo引擎:
下面咱们经过一个mongo管道作爬虫数据的持久化
固然,你也能够直接将持久化逻辑写入爬虫主程序,可是ItemPipline中的持久化逻辑能避免低配IO对爬虫的阻塞
# -*- coding: utf-8 -*- import pymongo from scrapy import exceptions class MongoPipeline(object): #不用事先建立mongo数据库、集合 和 定义文档,即插即用 mongo_uri = 'localhost' mongo_database = 'mafengwo' collection_name = 'wenda_pages' def __init__(self, mongo_uri, mongo_db): self.mongo_uri = mongo_uri self.mongo_db = mongo_db #下面这个类方法定义了如何由Crawler对象建立这个管道实例 #在这里咱们能够经过crawler参数相似于 `crawler.settings.get()` 形式访问到诸如settings、signals等全部scrapy框架内核组件 @classmethod def from_crawler(cls, crawler): return cls(cls.mongo_uri, cls.mongo_database) def open_spider(self, spider): self.client = pymongo.MongoClient(self.mongo_uri) self.collection = self.client[self.mongo_db][self.collection_name] def close_spider(self, spider): self.client.close() # 管道必须实现的一个方法,在此实现具体的持久化逻辑 def process_item(self, item, spider): if (not item['title']): raise exceptions.DropItem('丢弃一个标题不存在页面') else: self.collection.insert(dict(item)) return item
咱们能够在 mafengwo/mafengwo/settings.py 文件中写入配置
ITEM_PIPELINES = { 'mafengwo.pipelines.MongoPipeline': 1, #数字肯定了不一样管道运行的前后顺序,从低到高 }
可是为了避免污染全局管道配置,咱们把setting写入爬虫自配置中,即爬虫主程序的 custom_settings 属性中:
class WendaSpider(scrapy.Spider): custom_settings = { 'ITEM_PIPELINES': {'mafengwo.pipelines.MongoPipeline': 1} }
scrapy crawl wenda
当运行 scrapy爬虫模块时,scrapy尝试从中查找Spider的定义,而且在爬取引擎中运行它。 爬取启动后,scrapy首先依据模块的 start_urls 属性建立请求,并将请求的response做为参数传给默认回调函数 parse 。 在回调函数 parse中,咱们能够产生(yield)更多的请求,并将响应传递给下一层次的回调函数。
mongo >show dbs >use mafengwo #切换数据库 >show collections >db.wenda_pages.findOne() >db.wenda_pages.find().limit(3).pretty()
数据库中能够看到,问答页面数据已经成功抓取并录入了