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()
數據庫中能夠看到,問答頁面數據已經成功抓取並錄入了