python之scrapy(三)spider的用法
Scrapy框架中Spider的用法
在Scrapy裡面,要抓取網站的連結配置、抓取邏輯、解析邏輯裡,都是在Spider裡面去完成的。
一、Spider的執行流程
在實現Scrapy爬蟲專案裡面,最核心的就是Spider類了,它定義瞭如何爬取某個網站的流程和解析方式。簡單來講,Spider就做兩件事情:
-
定義爬取的動作
-
分析爬取下來的網頁
對於Spider類來說,整個流程如下,可以參考中文官方文件:
-
以初始的URL初始化Request,並設定回撥函式。 當該request下載完畢並返回時,將生成response,並作為引數傳給該回調函式。spider中初始的request是通過呼叫
-
在回撥函式內分析返回的(網頁)內容,返回 Item 物件或者 Request 或者一個包括二者的可迭代容器。 返回的Request物件之後會經過Scrapy處理,下載相應的內容,並呼叫設定的callback函式(函式可相同)。
-
在回撥函式內,您可以使用 選擇器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 來分析網頁內容,並根據分析的資料生成item。
-
最後,由spider返回的item將被存到資料庫(由某些 Item Pipeline 處理)或使用 Feed exports 存入到檔案中。
通過以上幾步的迴圈往復的進行,就可以完成站點的爬取。
二、Spider類分析
scrapy.Spider.[namespider] 例如class JdbraSpider(scrapy.Spider)中JdbraSpider就是namespider的名字,在使用scrapy genspider Jdbra時,會自動建立這樣一個類。Spider並沒有提供什麼特殊的功能。 其僅僅請求給定的 start_urls/start_requests
2.1 name
定義spider名字的字串(string)。spider的名字定義了Scrapy如何定位(並初始化)spider,所以其必須是唯一的。 不過您可以生成多個相同的spider例項(instance),這沒有任何限制。 name是spider最重要的屬性,而且是必須的。
如果該spider爬取單個網站(single domain),一個常見的做法是以該網站(domain)(加或不加 字尾 )來命名spider。 例如,如果spider爬取 mywebsite.com ,該spider通常會被命名為 mywebsite 。
2.2 allow_domains
允許爬取的域名,是可選配置,不在此域名範圍的連結,不會被跟進爬取。
2.3 start_urls
URL列表。當沒有制定特定的URL時,spider將從該列表中開始進行爬取。 因此,第一個被獲取到的頁面的URL將是該列表之一。 後續的URL將會從獲取到的資料中提取。
2.4 custom_settings
它是一個字典,是專屬於Spider的配置,此方法會覆蓋全域性的配置,此設定必須在初始化前被更新,必須定義成類變數。
理解的操作方式:
在之前的專案裡面,建立一個知乎的Spider:
在start_urls的連結改成start_urls = ['http://www.zhihu.com/explore']
在settings.py裡面DEFAULT_REQUEST_HEADERS加入user-agent資訊
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'user-agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36',
}
先執行下給spider看看有什麼問題:
會看到,返回200的狀態碼:
在zhihu.py檔案裡面加入custom_settings相關配置:
custom_settings={
'DEFAULT_REQUEST_HEADERS':{
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'user-agent': None,
}
}
在執行zhihu看看有什麼問題:
請求時出現了400的狀態碼,說明custom_settings相關配置已經生效,得到了錯誤的響應。
說明custom_settings用來覆蓋settings的全域性配置。
2.5 crawler
它是由from_crawler方法設定的,代表本Spider對應的Crawler物件,包含了許多專案元件。我們可以利用它來獲取專案中的一些配置資訊,最常見的就是從settings.py裡面獲取專案的配置資訊。
2.6 from_crawler()
使用該方法可以獲取Crawler物件裡面的專案元件配置資訊。此方法和Pipeline裡面使用是一樣的。
2.7 start_requests()
此方法用於生成初始請求,它必須必須返回一個可迭代物件。此方法會預設使用start_urls裡面的URL來構建Request,而且Request是以GET方式進行請求。如果我們想在啟動時,想以POST的請求方式訪問某個網站,可以直接重寫這個方法。
理解的操作方式:(http.org是一個進行http請求方式模擬的一個網站)
重新建立一個httpbin的Spider:
原封不動進行執行:
看效果,完成的是get請求:
先對start_urls改成start_urls = ['http://httpbin.org/post']
再執行,出現了以下情況:
接下來對start_requests()進行改寫:
def start_requests(self):
yield scrapy.Request(url='http://httpbin.org/post',method='POST',callback=self.parse_post)
def parse(self, response):
pass
def parse_post(self,response):
print('請求成功:',response.status)
再次執行,可以看到如下資訊:
2.8 make_requests_from_url(url)
該方法接受一個URL並返回用於爬取的 Request 物件。 該方法在初始化request時被 start_requests() 呼叫,也被用於轉化url為request。
預設未被複寫(overridden)的情況下,該方法返回的Request物件中, parse() 作為回撥函式,dont_filter引數也被設定為開啟。 (詳情參見 Request)。
理解的操作方式:
重新建立一個baidu的Spider:
改寫make_requests_from_url(),改變回調函式:
def make_requests_from_url(self, url):
return scrapy.Request(url=url,callback=self.parse_page)
def parse(self, response):
pass
def parse_page(self,response):
print(response.status)
進行執行:
通過上面該寫可以看到,url直接來走start_urls裡面的元素,還可以直接改變回調函式
如果我們在加入改寫的start_request(),就不會再呼叫make_requests_from_url()方法。
def start_requests(self):
yield scrapy.Request(url='http://www.baidu.com/',callback=self.parse_index)
def parse_index(self,response):
print('呼叫start_resquest()方法')
def make_requests_from_url(self, url):
return scrapy.Request(url=url,callback=self.parse_page)
def parse(self, response):
pass
def parse_page(self,response):
print(response.status)
self.logger.info(response.status)
執行spider之後就會看到如下資訊:
2.9 parse()
當response沒有指定回撥函式時,該方法是Scrapy處理下載的response的預設方法。
parse 負責處理response並返回處理的資料以及(/或)跟進的URL。 Spider 對其他的Request的回撥函式也有相同的要求。
該方法及其他的Request回撥函式必須返回一個包含 Request、dict 或 Item 的可迭代的物件。
2.10 logger()
日誌輸出的方法,有info()和DEBUG()方法,可以輸出日誌的誒輸出資訊:方法見2.8最後一段程式碼。
2.11 close()
當spider關閉時,該函式被呼叫。有一個引數reason,表示當前引數中斷的原因。
def close(self,spider, reason):
print('++++++++++++')
self.logger.debug(reason)