http://scrapy-chs.readthedocs.io/zh_CN/latest/intro/tutorial.html#id5*
參考書籍 《精通Scrapy網絡爬蟲》
網絡爬蟲指的是在互聯網上進行自動爬取網站內容的信息得程序,也被稱作網絡蜘蛛和網絡機器人
基本得爬取流程為:
簡介:
Scrapy使用python語言基於Twisted框架編寫得開源得網絡爬蟲框架,目前支持python2.7及python3.4+
安裝
pip install scrapy //安裝中如果系統提示缺乏依賴文件 pip install wheel //這是一個Twisted的依賴 pip install C:\Users\10338\Downloads\Twisted-17.9.0-cp36-cp36m-win_amd64.whl
http://books.toscrape.com/
利用shell使用命令行進行創建
//scrapy startproject 項目名稱 scrapy startproject book_spider
from scrapy import cmdline cmdline.execute("scrapy crawl LG_Spider -o LG_Spider.csv".split())
編寫代碼(在pycharm中添加scrapy工程)
import scrapy class BooksSpider(scrapy.Spider): #定義一個爬蟲 name = "books" #定義爬蟲的起點 start_urls = ['http://books.toscrape.com/'] def parse(self, response): #提取數據 #獲取每一本書的信息都在標簽 class=“b-all-content cf” #利用CSS的方法找到所有的元素,並一次迭代 for book in response.css('article.product_pod'): book_name = book.xpath('./h3/a/@title').extract_first() book_price = book.xpath('p.price_color::text').extract_first() yield {'book_name':book_name, 'book_price':book_price, } #提取鏈接 next_url = response.css('ul.pager li.next a::attr(href)').extract_first() if next_url: next_url = response.urljoin(next_url) yield scrapy.Request(next_url,callback=self.parse)
extract.frist()返回字符串 extract 返回數組
name 屬性:在一個scrapy中可能存在多個爬蟲,name屬性是爬蟲的唯一的區分。
start_urls屬性:爬蟲要從某個頁面開始爬取,也就是起始抓取點。
parse:一個頁面抓取成功後,Scrapy會回掉一個我們指定的頁面解析函數(默認的就是parse方法 )。
attr()jQuery。返回被選元素的屬性值
scrapy crawl books -o books.csv
獲取的信息將會保存在books.csv的文件中。
用來整理整個系統的數據流處理。觸發事件
用來接受引擎發過來的請求,壓入隊列,並在引擎再次請求的時候返回,可以認為是一個URL的優先隊列,由他來決定下一個要抓取的網址是什麼,同時去除重復的網址。
用於下載網頁內容,並將網頁的內容返回給spider下載器是建立在twisted這個高效的異步模型上的
爬蟲(工農階級)主要干活的,用於從intenet爬取信息,也就所謂的實體。用戶也可以從中提取出鏈接,讓spider繼續爬取下一個網頁
負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體,驗證實體的有效性,清除不需要的信息,當頁面頁面爬蟲解析後,將被發送到項目管道,並經過幾個特定的次序的處理數據。
位於Scrapy引擎和下載器之間框架,主要用處處理Scrapy引擎於下載器之間的請求和響應
介於Scrapy引擎和Spider之間的框架,主要是處理Scrapy引擎與Spider之間的請求和響應
介於Scrapy引擎和調度器之間的框架,從Scrapy引擎發送到調度的請求和響應
1.引擎從調度器上去取出一個鏈接(URL)用於接下來的爬取
2.引擎把URL封裝成一個請求(request)傳給下載器
3.下載器把資源下載下來,並封裝成應答包(response)
4.爬蟲解析response
5.解析出實體(Item),則交給實體管道進行進一步的處理
6.解析出的是鏈接(URL),則把URL交給調度器等待抓取
request(url[,callback,method='GET',headers,boday,cookies,meta,encoding='utf-8',priority=0,dont_filter =false,errback])
~url :請求地址
~callback:頁面解析函數,callback類型,request對象請求的頁面下載 完成後,有該參數指定的頁面解析被調用,未被調用時spider默認調用parse方法。
~method:HTTP的請求方法,默認為GET方法。
~headers:HTTP請求的頭部字典,dict類型,例如{‘A':'a','B':'b'}如果內部某項的值為NONE,就表明不發送該項的頭部,例如:{‘C’:None},禁止發送C
~body:HTTP請求的正文,bytes或str類型
~cookies:信息字典,dict類型
~meta:request的元數據字典,meta標簽永遠位於head元素的內部。頭部的元信息——用來描述信息的結構,語法,用途及用法等等‘。
~encoding:編碼格式。
~priority:設置請求的優先級。
~dont_filter:默認情況下值為False,值個更改為True可以請求避免被過濾,強制下載。
~errback:錯誤返回。
import scrapy request = scrapy.Request('地址') request = scrapy.Request('地址',callback=self.parseItem)
頁面下載完成後,搞一個Response的子類對象出來,子類有TextResponse、HtmlResponse、XmlResponse,由於通常都是搞網頁玩,所以一般使用HtmlResponse,注意TextResponse是HtmlResponse和XmlResponse的父類。
~url:HTTP響應的url地址,str類型。
~status:HTTP響應的狀態碼。
~headers:HTTP響應的頭,dict類型。
~body:HTTP響應正文,bytes裡類型
~text:文本形式正文向應。
~encoding:編碼格式。
~request :產生該HTTP響應的Request的對象。
~meta:即response.request.meta,構造Request對象時可以取出response.meta信息。
~selector: Selector對象 用於在response中提取數據(選擇器)、
~Xpath(query):使用xpath在response中提取數據,實際上是response.selector.xpath方法的快捷方式。
~css(query):使用CSS選擇器在response中數據提取。實際為response.selector.css方法的快捷方式。
~urljoin:用於構造絕對url.當傳入url參數四一個相對的地址時,根據response.url計算出相應的絕對的url.
常用的方法為:css xpath 進行數據的提取,利用urljoin進行構造絕對的url.
開發的四個步驟:
1.繼承scrapy.Spider
2.為Spider取名
3.設置起始爬取點。
4.實現頁面解析函數。
Scrapy框架提供了一個Spider基類,。
在Spider基類中實現了:
1.供Scrapy引擎調用的接口
2.供用戶使用的實用工具函數
3。供用戶訪問的屬性。
一個項目中可以實現多個spider,name屬性時區分這些工作的小爬爬的唯一屬性。在執行scrapy crawl時就會用到這個標識了
Spider爬去的起始網頁,start_urls 通常是一個列表,其中放入所有得爬取點url。
頁面解析函數,也就是構造Request對象時通過callback參數指定的回掉函數(或者parse方法)。需要完成的工作:
1.使用選擇器提取頁面中的數據,數據封裝(Item或dict)提交給Scrapy引擎
2.使用選擇器或者LinkExtractor提取頁面中的鏈接,用其構造新的request對象並提交給Scrapy引擎
常用的處理HTML頁面解析的模塊
~BeatifulSoup
~LXML
Scrapy結合了兩者實現了Selector類,使用先通過Xpath或者CSS選擇器選中頁面下的數據,然後提取
創建Selector對象時,可將頁面的HTML文檔字符串傳遞給Selector構造器的方法的text參數數;也可以利用Response對象構造Selector對象將其傳遞給Selector構造器方法的response參數。
利用Xpath和css方法
由於Selenium使用xpath定位時采用遍歷頁面的方式,在性能上采用CSS選擇器的方式更優。xpath雖然性能指標比差,但是在了浏覽器中有比較好的插件支持,定位元素比較方便,對於性能要求嚴格的可以交替使用。
Xpath:
xpath使用路徑表達式在xml文檔中進行導航
xpath包含一個標准的函數庫
xpath是XSLT(將xsl轉換,xsl是可擴展樣式表語言)中的主要元素
xpath是一個W3C標准(web的技術標准)
在xpath中,有七種類型的節點:元素、屬性、文本、命名空間、處理指令、注釋一級文檔(根節點)。XML文檔是被作為樹來對待的,樹的根被稱為文檔節點或者根節點。
xpath和css方法返回一個SelectorList對象,其中包含每個被選中部分對應的Selector對象,SelelctorList支持列表接口,可以使用for語句訪問其中的每一個Selector對象:
for sel in selector_list: print(sel.xpath('./text()'))
selector_list對象也有xpath和css方法,調用他們的行為是:以接受到的參數分別調用其中每一個Selector對象的xpath和CSS方法,並將其所有的結果收到一個新的SelectorLiist對象返回給用戶eg:
>>>selector_list.xpath('./text()') [<Selector xpath='./text()'data='hello world'>,<Selector xpath='./text()'data='Hello world'>]
調用Selector和SelectorList對象的方法:extract()、re()、extract_first()、re_first()(後兩個為SelectorList專有)
1.extract()方法
調用Selector對象的extract方法將返回選中內容的Unicode字符串,與SelectorList對象的xpath和css類似, SelectorList對象的extract方法內部會調用其每個Selector對象的extract方法,並把所有的結果收集到一個列表返回給用戶.
2.extract_first()
該方法放回其中第一個Selector對象調用extract方法的結果。在selectorList對象只包含一個Selector對象時調用該方法,直接提取出Unicode字符串而不是列表。
3.re和re_first
利用正則表達式提取內容的某部分,可以使用re方法,re_first方法同樣返回其中的第一個Selector對象調用re方法的結果。
其實在應用過程中,幾乎不需要手動創建Selector對象,在第一次訪問一個Response對象的selector屬性時,Response對象內部會以自身為參數自動創建Selector對象,並將該Selector對象緩存,方便下次使用。
知識點補充:@propety 屬性修飾
例子:
class Student(object): def __init__(self,name;score): self.name = name self.score = score @property def score(self): return self.__score @score.setter def score(self,score): if score < 0 or score > 1000: raise ValueError('invaid score') self.__score = score
注意啦:第一個Score(self)是get 方法,用@property裝飾,第二個score(self,score)是set方法,用@score.setter裝飾,@score.setter是前一個@property裝飾後的副廠品。
score屬性設置。
>>> s = Student('Bob', 59) >>> s.score = 60 >>> print s.score 60 >>> s.score = 1000 Traceback (most recent call last): ... ValueError: invalid score
XPath也就是XML路徑語言(XML Path Language)
Xpath的常用基本語法
<html> <head> <base href='http://example.com/'/> <title>Example website</title> </head> <body> <div id='images'> <a href='image1.html'>Name:Image 1<br/><img src='image1.jpg'></a> <a href='image1.html'>Name:Image 2<br/><img src='image2.jpg'></a> <a href='image1.html'>Name:Image 3<br/><img src='image3.jpg'></a> <a href='image1.html'>Name:Image 4<br/><img src='image4.jpg'></a> <a href='image1.html'>Name:Image 5<br/><img src='image5.jpg'></a> <a href='image1.html'>Name:Image 6<br/><img src='image6.jpg'></a> </div> </body> </html>
from scrapy.selector import Selector from scrapy.http import HtmlResponse response = HtmlResponse(url = 'http://www.example.com',body=body.encoding='utf8') response.xpath('/html') response.xpath('/html/head') response.xpath('/html/body/div/a') response.xpath('//a')
這部分內容查閱填充
xpath提供許多函數,例如數字、字符串、時間、日期、統計等等。
string(arg)返回參數的字字符串值。
CSS即層樣式表器,其選擇器是一種來確定HTML文檔某部分位置的語言。CSS使用起來比xpath簡單,但是不如xpath功能強。
實際上在使用CSS方法時,python庫將cssselect將CSS選擇器表達式翻譯成xpath表達式然後調用Selector對象的Xpath方法。
CSS選擇器
Item基類:自定義數據類(支持字典的接口)
field類:用來描述自定義數據類包含哪些字段
自定義一個數據類,只需要繼承Item,並創建一系列的Field對象的類屬性
對字段賦值時,如果是內部沒有的字段,這時會拋出異常。
實際上Field是Python字典的子類,可以通過鍵獲取Field對象中的元數據
改寫之前的代碼
from scrapy import Item,Field class BookSpiderItem(scrapy.Item): name = Field() price = Field()
修改之前的BooksSpider,使用BookItem替代Python字典
from ..Item import BookSpiderItem class BooksSPider(scrapy.Spider): def parse(self,response): for sel in response.css('article.product_pod'): book = BookSpiderItem() book['name'] = sel.xpath('./h3/a/@title').extract_first() book['price'] = sel.css('p.price_color::text').extract_frist() yield book
在ltem中添加新的字段利用 Field()類的屬性。
一項數據由Spider提交給Scrapy引擎後,可能會提交給其他組件進行處理(Item Pipline Exporter)處理,假設想傳遞額外的信息給處理數據的某個組件(例如,高手組件應該如何處理數據),此時可以使用Field的元數據。
進行數據處理,一個項目中可以啟用多個Item Pipeline ,它的典型應用
清洗數據
驗證數據的有效性
過濾重復的數據
將數據存入數據庫
項目創建時會生成一個pipelines.py的文件,
(1)一個Iltem Pipleine 不需要繼承特定基類、只需要實現某些特定的方法,例如 process-_Iitem open_spider close_spider
(2)一個item Pipeline 必須實現一個process_item(item,spider)方法,該方法用來處理每一項由spider爬取的數據,其中每兩個參數
Item 爬取到的一項數據(Item或字典)。
Spider 爬取此項數據的spider對象。
(3)如果process_item在處理某項item返回了一項數據(Item或字典),返回的數據會遞送給下一極Item pipeline繼續處理。
(4)如果process_Item在處理某項item時拋出(raise)一個Droptem異常(scarpy.exceptions.DropItem),該項Item會被拋棄,不再遞送給後面的Item pipeline 繼續處理,也不會導出到文件,通常,我們再檢測到無效數據或過濾數據時我們會拋出DropItem異常。
(5)open_spider(self,spider)
Spider打開時(處理數據之前)回調該方法,通常該方法用於再開始處理數據之前完成某些初始化工作,如鏈接數據庫
(6)close_spider(self,Spider)
Spider關閉時(處理數據後)回調該方法,通常該方法用於處理完所有數據之後完成清理工作,如關閉數據庫。
(7)from_crawlwer(cls,crawler)
創建Item Pipeline 對象時回調該類方法,通常在該方法中通過crawler.settings讀取配置,根據配置創建Item pipeline對象。
在scrapy中。想要啟用某個Item Pipleine 需要在配置文件settings.py中進行配置:
ITEM_PIPELINES = { 'example.piplines.priceConverPipeline':300, }
ITEM_PIPELINES是一個字典。我們把想要啟用的Item Pipeline添加到這個字典中,值為0~1000的數字,數字越小越先執行。
實現
class PriceConveterPipeline(object): exchange_rate = 8.5209 def process_item(self,item,spider): price = float(item['price'][1:])*self.exchange_rate item['price']='¥%.2f'%price
代碼解釋Item Pipeline不需要繼承特定的基類。只需要實現某些特定的方法例如process_item、oepn_spider、close_spider.
上述的實現方法很簡單將書籍的英鎊價格轉換為浮點數乘以匯率並保留兩位小數,然後賦值給item中的price字段,最後返回被處理過的item。
可以看出,process_item在處理某項item時返回了一項數據(Item或者字典),返回的數據會遞送給下一級的process_item(如果有)繼續處理
過濾重復的數據,處理重復的數據,代碼如下:
from scrapy.exceptions import DropItem class DuplicationPipeline(object): def __init__(self): self.book_set = set() def process_item(self,item,spider): name = item['name'] if name in self.book_set: raise DropItem("Duplicate book found: %s"%item) self.book_set.add(name) return item
增加構造器的方法,在其中初始化用於對書名去重的集合。
在process_item 方法中,先取出item的name字段,檢查書名是否已經在集合book_set中,如果存在,就是重復數據,拋出DropItemy異常,將item拋棄,否則將item的name字段存入集合返回item.
把數據存放到某種數據庫中,可以通過實現Item Pipeline完成
例子:
from scrapy.item import Item import pymongo class MongoDBPipeline(object): DB_URI = 'mongodb://loaclhost:27017/' DB_NAME = 'scrapy_data' def open_spieder(self,spider): self.client = pymongo.MongoClient(self.DB_URI) self.db = self.client[self.DB_NAME] def close_spider(self,spider): self.client.close() def process_item(self,item,spider): collection = self.db[spider.name] post = dict(item)if isinstance(item,item)else item collection.insert_one(post) return item
代碼解釋:
在類屬性中定義兩個常量:DB_URI 數據庫的URI地址
DB_NAME 數據庫的名字
spider爬取時數據庫的連接只需要執行一次,應該在開始數據處理之前連接數據庫,並且在處理完數據庫之後關閉數據庫實現open_spider(spider)和close_spider(spider)。
在process_item中實現MongoDB數據庫的寫入,使用self.db和spider.name獲取集合collection,然後將數據插入集合,集合的insert_one方法需要傳入一個字典對象,不能時item對象,因此在使用前對item的類型進行判斷,如果時item對象就轉換為字典。
在settings.py文件中啟用MongoDBPipelien:
ITEM_PIPELINES = { 'example.pipelines.PriceConverterPipeline':300 'example.pipelines.MongoDBPipline':400 }
在爬取時,頁面中會存放很多的其他網頁的鏈接信息,有時我們需要提取這些信息,一般使用的提取方法有Selector和LinkExtractor兩種方法
1.Selector 因為鏈接也是頁面中的數據,所以使用提取數據相同的方法就可以了,在提取少量的數據的時候,鏈接的提取方法比較簡單,使用Selector也就是足夠了。
2.LinkExtractor scarpy提供了一個專門的用於提取鏈接的類LinkExtractor,在提取大量鏈接或提取規則比較復雜時,使用Link Extractor比較方便。
class BooksSpider(scrapy.SPider): def parse(self,reponse): #提取鏈接 #下一頁的url信息在ul.pager>li>a裡面 next_url = response.css('ul.pager.li.next.a::attr(href)').extract_first() if next_url: #如果找到下一頁的絕對路徑構造新的response對象 next_url = response.urljoin(next_url) yield scrapy.Request(next_url,callback=self.parse)
from scrapy.linkExtractors import LinkExtractor class BooksSpider(scarpy.Spider): def parse(self,reponse): le = LinkExtractor(restrict_css='ul.pager li.next') links = le.extract_links(reponse) if links: next_url = links[0].url yield scrapy.Request(next_url,callback=self.parse)
導入LinkExtractor,它位於scrapy.linkExtractors模塊
創建一個linkExtarctor對象,使用一個或者多個構造器參數描述提取規則,這裡傳遞給restrict_css參數一個CSS選擇器表達方式。他描述出下一頁鏈接所在的區域下。
調用LinkExtarctor對象的extract_Links方法傳入一個Response對象,該方法依靠創建的對象時所描述的提取規則在Response對象所包含的頁面中提取鏈接,最終返回一個列表,其中每個元素都是一個Link對象,也就是提取到的一個鏈接,最終返回一個列表,每個元素都是一個Link對象。
由於頁面的提取一頁的下一個鏈接只有一個,因此用Links[0]獲取到的信息,因此用Links[0]獲取Link對象,Link對象的url屬性便是鏈接頁面的絕對URL地址(無須再調用response.urljoin方法),在用Request構造並進行提交。
學習使用LinkExtractor的構造器參數描述提取規則
<<<<<<!--example.html> <html> <body> <div di="top"> <a class="internal"herf="/intro/install.html">Installation guide</a> <a class="internal"herf="/intro/install.html">Tutorial</a> <a class="internal"herf="/intro/install.html">Examples</a> </div> <div> <p>下面時一些站外鏈接</p> <a href="http://stackoverflow.com/tags/scrapy/info">StackOverflow</a> <a href="http;//github.com/scrapy/scrapy">Fork on</a> </div> </body> </html>
<html> <head> <script type='text/javascript'src='/js/app1.js/'/> <script type='text/javascript'src='/js/app2.js/'/> </head> <body> <a href="/home.html">主頁</a> <a href="javascript:goToPage('/doc.html');return false">文檔</a> <a href="javascript:goToPage('/example.html');return false">案例</a> </body> </html>
使用以上兩個HTML文本構造兩個Response對象
from scrapy.http import HtmlResponse html1 = open('example.html1').read() html2 = open('example.html2').read() reponse1 = HtmlResponse(url='http://example.com',body=html,encoding='utf8') reponse2 = HtmlResponse(url='http://example.com',body=html,encoding='utf8') #構造Html文本的兩個Response對象
注意:LinkExtractor構造器所有參數都擁有默認值,在不做說明的情況下,就會使用默認值。
from scrapy,linkextractor import LinkExtractor le = LinkExtractor() links = le.extract_links(reponse1) print([link.url for link in links])
LinkExtractor的參數:
——allow 接受一個正則表達式或者一個正則表達式的列表,提取絕對的url與正則表達式匹配的鏈家,如果該參數為空,就提取全部鏈接。
#提取頁面example.html中的路徑以/info開始的 from scrapy.linkExtractors import LinkExtractor pattern = '/info/.+\.html$' le = LinkExtractor(allow=patten) links = le.extract_links(response1) print(link.url for link in links)
——deny 接受一個正則表達式或者一個正則表達式的列表,與allow相反,排除絕對和url匹配的鏈接
#提取example.html1中是所有站外鏈接排除站內鏈接 from scrapy.linkExtractors import LinkExtractor from urllib.parse import urlparse pattern = patten = '^'+urlparse(reponse1.url).geturl() print(pattern)
——allow_domains 接受一個域名或一個域名的列表,提取到指定域的鏈接
#提取頁面example1.html中所有到GitHub和stackoverflow.com這兩個網址的鏈接 from scrapy.linkextractors import LinkExtarcot domains = ['github.com','stackoverflow.com'] le = LinkExtractor(allow_domains=domains) links = le.extract_links(reponse1) print([link.url link in links])
——deny_domains 接受一個域名或者一個域名列表,與allow_domains相反,排除到指定域的鏈接
#提取頁面example,html中除了GitHub.com域以外的鏈接 from scrapy.linkextractors import LinkExtractor le = LinkExtractor(deny_domains='github.com') links = le.extract_links(response1) print([link.url for link in links])
——restrict_xpaths 接受一個Xpath表達式或一個Xpath的列表,提取Xpath表達式中區域下的鏈接
#提取頁面example.html中<div id="top">元素下的鏈接: from scrapy.linkextractors import LinkExtractor le = LinkExtractor(restrict_xpath='//div[@id="top"]') links = le.extract_links(response) print([link.url for link in links])
——restrict_css 接受一個CSS表達式或者一個CSS的列表 ,提取CSS表達式下的區域鏈接
#提取頁面的example.html中<div id="botton">元素下鏈接 from scrapy.linkextractors import LinkExtractor le = LinkExtractor(restrict_css='div#bottom') links = le.extract_links(response1) print([link.url for link in links])
——tags 接收一個標簽(字符串)或一個標簽列表,提取指定的標簽內的鏈接默認為['a','area']
——attrs 接收一個屬性(字符串)或一個屬性列表,提取指定屬性內的鏈接,默認為['href']
#提取頁面example2.hml中引用JavaScript文件鏈接 from scrapy.linkextractors import LinkExtractor le = LinkExtractor(tags='script',attrs='sec')
——process_value 接收形如func(value)的回調函數。如果使用了該函數,LinkExtractor將回調該函數,對提取的每一個鏈接(如a的harf)進行處理,回調函數正常情況下應返回一個字符串,也就是處理結果,想要拋棄所處理的鏈接時,返回None。
import re def process(value): m = re.search("javascript:goTopage\('(.*?))'",value) if m: value = m.group() return value from scrapy.linkextractors import LinkExtractor le = LinkExtractor(process_value=porcess) links = le.extract_link(response2) print([link.url for link in links])
scrapy中有負責導出數據的組建被叫做Exporter(導出器),scrapy中有多個導出器,每個都對應一種數據格式的導出,支持的數據如下
(1)JSON ——JsonItemExporter
(2)JSON Lines ——JsonLinesItemExporter
(3)CSV ——CsItemExpoter
(4)XML——XmlItemEporter
(5)Pickle——PickleItemExporter
(6)Marshal——MarshalExpotrer
在進行數據導出時,我們需要向Scapy提供,導出文件的路徑和導出的數據格式,就是在說要將什麼人樣的人,從那個地方喊出來。
可以通過命令行參數指定或配置文件指定
在運行scrapy crawl命令時可以通過-o 和-t 參數指定導出文件路徑以及導出文件的數據的格式。
scrapy crawl books -o books.csv
其中的 -o books.csv就指定了文檔的導出路徑,雖然沒有使用-t參數指定導出的數據格式,但是Scrapy爬蟲通過文件後綴名推斷出我們以csv的格式將數據導出,同樣的,如果將數據改為-o books.json就會與以json的格式將數據導出。需要明確指定導出路徑時使用-t參數。
scrapy crawl books -t csv -o book1.data scrapy crawl books -t json -o book2.data scrapy crawl books -t xml -o book3.data
在配置字典FEED_EXPORTERS中搜索Exporter,PEED_EXPORTERS的內容合並而成,默認配置文件中的FRRD_EXPORTERS_BASE,用戶配置文件中的FEED_EXPORTERS。
前者包含內部支持的導出數據格式,後者包含導出用戶自定義的導出數據格式。如果用戶添加了新的導出數據格式(即實現了新的exporter),可以在配置問價settings.py中定義FEED_EXPORTERS例如:
FEED_EXPORTERS={'excel':'my_project.my_exporters.ExcellemExporter'}
在指定文件路徑時,還可以使用%(name)s和%(time)s兩個特殊變量,%(name)s會被替換成Spider的名字,%(time)s會被替換為件創建時間。
配置文件,看下如何在配置文件中導出數據,
FEED_URL 導出路徑
FEED_URL= 'exporter_data/%(name)s.data'
FEED_FORMAT 導出格式
FEED_FORMAT= 'csv'
FEED_EXPORT_ENCODING 導出文件編碼,注意在默認情況下json文件使用數字編碼,其他的使用utf-8編碼
FEED_EXPORT_ENCODING = 'gbk'
FEED_EXPORT_FILEDS 導出數據包含的字段,默認情況下導出所有字段,並指定次序
FEED_EXPORT_FILEDSv={'name','author','price'}
FEED_EXPORTERS 用戶自定義數據格式
FEED_EXPORTERS= {'excel':'my_project.my_exporters.ExcelItemExporter'}
exporter_item(self,item) 負責導出爬取到每一項數據,參數item為 一項爬取到的數據,每一個子類必須實現該方法。
start_exporting(self) 在導出開始時期被調用,可以在該方法中執行某些初始化工作
finish_exporting(self) 在導出完成時被調用,可在該方法中執行某些清理工作
以JsonItemExporter為例子 :
為使最終的導出在一個json中的列表,在start_exporting和finish_exporting方法中分別向文件寫入b"[\n,b"\n]"。
在export_item方法中,調用self.encoder.encode方法將一項數轉換成json串,然後寫入文件。
#在項目中創建一個my_exportes.py(與settings.py同級目錄),在其中實現ExcelItemExporter from scrapy.linkexporters import BaseItemExporter import xlwt class ExcelItemExporter(BaseItemExporter): def __init__(self,file,**kwargs): self._configure(kwargs) self.file = file self.wbook = xlwt.Workbook() self.wsheet = self.wbook.add_sheet('scrapy') self.row = 0 def finish_exporting(self): self.wbook.save(self.file) def export_item(self,item): filed = self._get_serialized_fileds(item) for col,v in enumerate(x for _,x in fields): self.wsheet.write(self.row,col,v) self.row += 1
代碼解釋:
使用第三方庫xlwt將數據寫入Excel文件。
在構造器方法中創建Workbook對象和Worksheet對象,並且初始化用來記錄寫入行坐標的self.row。
在exporter_item方法中調用基類的_get_serialized_fileds方法,獲得item所有字段的迭代器,然後調用self.wsheet.write方法將各字段寫入excel表格。
finish_exporting方法在所有數據都被寫入Excel表格被調用,在該方法中調用self.wbook.save方法將Excel表格寫入Excel文件
完成數據導出的編寫之後子啊settings.py文件中添加配置
FEED_EXPORTERS = {'excle':'example.my_exporters.ExecelitemExporter'}
利用命令行運行爬蟲
scrapy crawl books -t excel -o books.xls
需求:爬取網址“http://books.toscrape.com/”網址的書籍信息。信息包括:書名‘、價格、評價星級、評價數量、書籍編碼和庫存量。詳細源碼見GitHub最後將爬取到的數據輸出到book.csv文檔中。
在scrapy中提供了兩個Item Pipeline,專門用來下載文件和圖片Files Pipeline 和ImagesPipeline,可以將這兩個Item Pipeline看做是特殊的下載器,用戶通過item的一個特殊字段將要下載的文件信息或圖片信息的url傳遞給他們,他們會自動將文件或圖片下載到本地,並將下載的結果信息存入item的另一個字段,以便用戶在導出文件中查閱。
FilePipeline使用方法:
1.在配置文件中啟用FilesPipeline,通常將其置於其他的Item Pipeline之前
ITEM_PIPELINS = {'scarpy pipelines.files.FilesPipeline'}
2.在配置文件中使用FIFLES_STORE 指定文件下載的目錄。例如
FILE_PIPELINS = {'/home/lxs/Dowload/scrapy'}
3.在spider中解析一個包含文件下載鏈接的頁面時將所有需要下載的文的url的地址收集到一個列表,賦值給item的file_url字段(item['file_url'])。FIlePipeline在處理每一項Item時,會讀取item['file_urls'],對其中每一個url進行下載。
class DownloadBookSpider(scrapy.Spider): pass def parse(response): item = {} #下載列表 item['file_urls'] = [] for url in response.xpath('//[email protected]').extract(): download_url = response.urljoin(url) #將url填入列表 item['file_urls'].append(download_url) yield item
當 FilePipeLine 下載完item['file_urls']中的所有的文件之後,會將各文件的下載結果信息收集到一個列表,賦值各給item的files字段(item[files]).下載結果包括:Path 文件下載到本地的相對路徑,checksum 文件的檢驗和,url 文件的url地址。
``