Python的Scrapy的學習和應用
Scrapy爬蟲的入門到精通
http://scrapy-chs.readthedocs.io/zh_CN/latest/intro/tutorial.html#id5*
參考書籍 《精通Scrapy網路爬蟲》
1.1爬蟲的定義和工作概述
網路爬蟲指的是在網際網路上進行自動爬取網站內容的資訊得程式,也被稱作網路蜘蛛和網路機器人
基本得爬取流程為:
1.2 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
1.3編寫第一個Scrapy爬蟲
需求:爬取網上的資訊
建立專案
利用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。返回被選元素的屬性值
1.34 執行爬蟲
scrapy crawl books -o books.csv
獲取的資訊將會儲存在books.csv的檔案中。
2 編寫Spider
2.1scrapy框架的結構機工作原理
引擎(scrapy)
用來整理整個系統的資料流處理。觸發事件
排程器(Scheduler)
用來接受引擎發過來的請求,壓入佇列,並在引擎再次請求的時候返回,可以認為是一個URL的優先佇列,由他來決定下一個要抓取的網址是什麼,同時去除重複的網址。
下載器(Downloader)
用於下載網頁內容,並將網頁的內容返回給spider下載器是建立在twisted這個高效的非同步模型上的
爬蟲(spiders)
爬蟲(工農階級)主要幹活的,用於從intenet爬取資訊,也就所謂的實體。使用者也可以從中提取出連結,讓spider繼續爬取下一個網頁
專案管道(pipline)
負責處理爬蟲從網頁中抽取的實體,主要的功能是持久化實體,驗證實體的有效性,清除不需要的資訊,當頁面頁面爬蟲解析後,將被髮送到專案管道,並經過幾個特定的次序的處理資料。
下載器中介軟體(Dowloader Middlewares)
位於Scrapy引擎和下載器之間框架,主要用處處理Scrapy引擎於下載器之間的請求和響應
爬蟲中介軟體(Spider Middlewares)
介於Scrapy引擎和Spider之間的框架,主要是處理Scrapy引擎與Spider之間的請求和響應
排程中間值(Scheduler MIddewares)
介於Scrapy引擎和排程器之間的框架,從Scrapy引擎傳送到排程的請求和響應
scrapy的工作流程
1.引擎從排程器上去取出一個連結(URL)用於接下來的爬取
2.引擎把URL封裝成一個請求(request)傳給下載器
3.下載器把資源下載下來,並封裝成應答包(response)
4.爬蟲解析response
5.解析出實體(Item),則交給實體管道進行進一步的處理
6.解析出的是連結(URL),則把URL交給排程器等待抓取
2.2 Request和Response
2.2.1 Request
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)
2.2.2Response物件
頁面下載完成後,搞一個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.
2.3Spider的開發流程
開發的四個步驟:
1.繼承scrapy.Spider
2.為Spider取名
3.設定起始爬取點。
4.實現頁面解析函式。
2.3.1繼承scrapy.Spider
Scrapy框架提供了一個Spider基類,。
在Spider基類中實現了:
1.供Scrapy引擎呼叫的介面
2.供使用者使用的實用工具函式
3。供使用者訪問的屬性。
2.3.2為spider命名
一個專案中可以實現多個spider,name屬性時區分這些工作的小爬爬的唯一屬性。在執行scrapy crawl時就會用到這個標識了
2.3.3設定起始爬取點
Spider爬去的起始網頁,start_urls 通常是一個列表,其中放入所有得爬取點url。
2.3.4實現頁面解析函式
頁面解析函式,也就是構造Request物件時通過callback引數指定的回掉函式(或者parse方法)。需要完成的工作:
1.使用選擇器提取頁面中的資料,資料封裝(Item或dict)提交給Scrapy引擎
2.使用選擇器或者LinkExtractor提取頁面中的連結,用其構造新的request物件並提交給Scrapy引擎
3 Selector物件
常用的處理HTML頁面解析的模組
~BeatifulSoup
~LXML
Scrapy結合了兩者實現了Selector類,使用先通過Xpath或者CSS選擇器選中頁面下的資料,然後提取
3.1.1建立物件
建立Selector物件時,可將頁面的HTML文件字串傳遞給Selector構造器的方法的text引數數;也可以利用Response物件構造Selector物件將其傳遞給Selector構造器方法的response引數。
3.1.2選中資料
利用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'>]
3.1.3 提取資料
呼叫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方法的結果。
3.2 Response內建Selector
其實在應用過程中,幾乎不需要手動建立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
3.3Xpath
XPath也就是XML路徑語言(XML Path Language)
Xpath的常用基本語法
3.3.1基礎語法
表示式 | 描述 |
---|---|
/ | 選中文件的根(root) |
. | 選中當前節點 |
.. | 選中當前節點的父節點 |
ELEMENT | 選中子節點中所有ELEMENT元素節點 |
//ELEMENT | 選中後代節點中所有ELEMENT元素節點 |
* | 選中所有元素的子節點、 |
text() | 選中所有文字子節點 |
@ATTR | 選中名字為ATTR的屬性節點 |
@* | 選中所有屬性節點 |
[謂語] | 謂語用來查詢某個特定的節點或者包含某個特定值得節點 |
<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')
3.3.2常用語法
這部分內容查閱填充
xpath提供許多函式,例如數字、字串、時間、日期、統計等等。
string(arg)返回引數的字字串值。
3.4 CSS選擇器
CSS即層樣式表器,其選擇器是一種來確定HTML文件某部分位置的語言。CSS使用起來比xpath簡單,但是不如xpath功能強。
實際上在使用CSS方法時,python庫將cssselect將CSS選擇器表示式翻譯成xpath表示式然後呼叫Selector物件的Xpath方法。
CSS選擇器
表示式 | 描述 | 例子 |
---|---|---|
* | 選中所有元素 | * |
E | 選中E元素 | p |
E1,E2 | 選中E1,E2元素 | div,pre |
E1>E2 | 選中E1後代元素中的E2元素 | div p |
E1+E2 | 選中E1兄弟元素中的E2元素 | p+strong |
.CLASS | 選中CLASS屬性包含CLASS的元素 | .info |
#ID | 選中id屬性為ID的元素 | #main |
[ATTR] | 選中包含ATTR屬性的元素 | [href] |
[ATTR=VALUE] | 選中包含ATTR屬性且值為VALUE的元素 | [method=post] |
[ATTR~=VALUE] | 選中ATTR屬性且值包含Value的元素 | [class~=clearfix] |
E:nth-child(n) E:nth-last-child(n) | 選中E元素,且該元素必須是其父元素的(倒數)第n個子元素 | a:ntn-child(1) a:nth-last-child(2) |
E:first-child E:last-child | 選中E元素且該元素必須是其父元素的(倒數)第一個子元素 | a:first-child a:last-child |
E:empty | 選中沒有子元素的E元素 | div:empy |
E:text | 選中E元素的文字節點(Text Node) |
第4章 使用Item進行資料封裝
4.1Item和Field
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
4.2拓展Item子類
在ltem中新增新的欄位利用 Field()類的屬性。
4.3field元資料
一項資料由Spider提交給Scrapy引擎後,可能會提交給其他元件進行處理(Item Pipline Exporter)處理,假設想傳遞額外的資訊給處理資料的某個元件(例如,高手元件應該如何處理資料),此時可以使用Field的元資料。
第五章 使用Item Pipeline處理資料
進行資料處理,一個專案中可以啟用多個Item Pipeline ,它的典型應用
清洗資料
驗證資料的有效性
過濾重複的資料
將資料存入資料庫
5.1實現 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物件。
5.2啟用 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(如果有)繼續處理
5.3例子
過濾重複的資料,處理重複的資料,程式碼如下:
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.
5.4將資料存入MongoDB
把資料存放到某種資料庫中,可以通過實現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 }
第六章 使用LinkExtractor提取連結
在爬取時,頁面中會存放很多的其他網頁的連結資訊,有時我們需要提取這些資訊,一般使用的提取方法有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)
6.1使用Link Extractor
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構造並進行提交。
6.2 描述提取規則
學習使用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])
第七章 使用Exporter匯出資料
scrapy中有負責匯出資料的組建被叫做Exporter(匯出器),scrapy中有多個匯出器,每個都對應一種資料格式的匯出,支援的資料如下
(1)JSON ——JsonItemExporter
(2)JSON Lines ——JsonLinesItemExporter
(3)CSV ——CsItemExpoter
(4)XML——XmlItemEporter
(5)Pickle——PickleItemExporter
(6)Marshal——MarshalExpotrer
7.1指定如何匯出資料
在進行資料匯出時,我們需要向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'}
7.2實現Exporter
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地址。
``