拆解 scrapy.Spider
本次采集的目標(biāo)站點為:優(yōu)設(shè)網(wǎng)
每次創(chuàng)建一個 spider
文件之后,都會默認(rèn)生成如下代碼:
1
2
3
4
5
6
7
8
|
import scrapy class UiSpider(scrapy.Spider): name = 'ui' allowed_domains = [ 'www.uisdc.com' ] start_urls = [ 'http://www.uisdc.com/' ] def parse( self , response): self .log() |
繼承的基類 scrapy.Spider
自然就成了我們要研究的第一個內(nèi)容,進入其源碼,發(fā)現(xiàn)如下內(nèi)容。
scrapy.Spider 核心實現(xiàn)的是 start_requests 方法
Spider 主要進行的操作就是初始化 Request 請求,而這些都是通過 start_requests
實現(xiàn)的,詳細(xì)代碼為:
1
2
|
for url in self .start_urls: yield Request(url, dont_filter = True ) |
對 start_requests
方法,你可以自己編寫同名函數(shù)覆蓋修改,編寫時發(fā)現(xiàn)了 make_requests_from_url
方法,該方法在最新版本的 scrapy
中已經(jīng)被廢除。
重寫 start_requests 方法 ,需要注意重寫時,必須返回一個可迭代對象,并且該對象包含 spider
用于爬取的第 1 個 Request
,由于 scrapy
只調(diào)用一次該方法,所以你可以將登錄站點請求放置到該方法中。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import scrapy from scrapy.http import Request class UiSpider(scrapy.Spider): name = 'ui' allowed_domains = [ 'www.uisdc.com' ] start_urls = [ 'http://www.uisdc.com/' ] def start_requests( self ): print ( "重寫 start_requests" ) yield Request( self .start_urls[ 0 ]) def parse( self , response): print (response) |
將登錄信息放置到 start_requests
中,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import scrapy from scrapy.http import FormRequest class UiSpider(scrapy.Spider): name = 'ui' allowed_domains = [ 'www.uisdc.com' ] start_urls = [ 'http://www.uisdc.com/' ] def start_requests( self ): print ( "手動 start_requests" ) yield FormRequest( "https://httpbin.org/post" , formdata = { "user" : "ca" }, callback = self .parse) def parse( self , response): print (response.text) |
scrapy.Spider 屬性值
name 屬性:
表示爬蟲名稱,spider 的名稱用于 scrapy
定位爬蟲,所以非常重要,一般常見的名稱方式是使用網(wǎng)站域名(domain),命名 spider,例如 baidu.com
命名為 baidu
,但是工作喜歡還是攜帶 .com
后綴。
allowed_domains 屬性:
該屬性需要配置 offsiteMiddleware
使用,當(dāng)該中間件啟用之后,待采集 URL 的域名如果不在 allowed_domains
列表中,會被禁止訪問。
domains
內(nèi)容添加,假設(shè)你的目標(biāo) URL 是 http://www.baidu.com/123.html
,僅填寫 baidu.com
即可。
start_urls 屬性:
起始的 URL 列表,主要用于 start_request
方法進行迭代。
custom_settings 屬性:
自定義配置,可以覆蓋 settings.py
的配置,以字典格式賦值。
1
2
3
|
custom_settings = { "ROBOTSTXT_OBEY" : False # 不請求 robot.txt 文件 } |
crawler 屬性:
該屬性在爬蟲啟動后,由類方法 from_crawler()
設(shè)置。
settings 屬性:
指定配置文件的實例。
logger 屬性:
spider 日志輸出對象,默認(rèn)以 spider 名稱創(chuàng)建,可以自定義。
1
2
|
self .logger.info( '輸出響應(yīng)地址 %s' , response.url) logger.info( '輸出響應(yīng)地址 %s' , response.url) |
補充一下 scrapy 日志級別
在 settings.py
中設(shè)置 log
級別,只需要增加一行代碼:
1
|
LOG_LEVEL = 'WARNING' |
設(shè)置為 WARNING
級別,會發(fā)現(xiàn) scrapy 默認(rèn)的各種調(diào)試信息,都不在控制臺輸出。
scrapy
日志級別與 logging
模塊一致。
CRITICAL:嚴(yán)重錯誤;
ERROR :一般錯誤;
WARNING: 警告信息;
INFO :一般信息;
DEBUG:調(diào)試信息。
在 scrapy
中的 settings
中關(guān)于日志的配置如下:
LOG_ENABLED
:默認(rèn): True,表示啟用 logging;
LOG_ENCODING
: 默認(rèn): utf-8,logging 使用的編碼;
LOG_FILE
默認(rèn): None,日志保存的文件名;
LOG_LEVEL
: 默認(rèn) DEBUG ,log 的最低級別。
scrapy.Spider 實例方法與類方法
from_crawler 類方法
在查看源碼之后,該方法的功能會比較清晰。
1
2
3
4
5
6
7
8
9
10
|
@classmethod def from_crawler( cls , crawler, * args, * * kwargs): spider = cls ( * args, * * kwargs) spider._set_crawler(crawler) return spider def _set_crawler( self , crawler): self .crawler = crawler self .settings = crawler.settings crawler.signals.connect( self .close, signals.spider_closed) |
該方法設(shè)置了 crawler
和 settings
兩個屬性,該方法在上一篇博客已經(jīng)有所涉及,直接回顧即可。
parse 方法
當(dāng)請求(Request)沒有指定回調(diào)參數(shù)(callback)時,該方法是 scrapy
用來處理響應(yīng)的默認(rèn)回調(diào)方法。
log 方法
使用 self.log()
方法記錄日志。
學(xué)習(xí)到這里,對 Spider 模塊有了一個比較整體的認(rèn)識。
爬取優(yōu)設(shè)網(wǎng)
接下來進入爬蟲采集相關(guān)代碼編寫,有了前文知識鋪墊之后,采集代碼就變得非常簡單了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import scrapy from uisdc.items import UisdcItem class UiSpider(scrapy.Spider): name = 'ui' allowed_domains = [ 'www.uisdc.com' ] start_urls = [ 'https://www.uisdc.com/archives' ] custom_settings = { "ROBOTSTXT_OBEY" : False } def parse( self , response): # print(response.text) # self.log("測試是否有數(shù)據(jù)輸出", logging.WARNING) items = response.xpath( '//div[@id="archive_list"]/div/div[1]/div[1]/div[contains(@class,"item-article")]' ) for i in items: item = UisdcItem() title = i.xpath( ".//h2[@class='item-title']/a/text()" ).extract_first() author = i.xpath( ".//h3[@class='meta-name']/text()" ).extract_first() tag = i.xpath( ".//div[@class='meta-tag']/a/text()" ).extract_first() item[ "title" ] = title item[ "author" ] = author item[ "tag" ] = tag yield item |
接下來修改源碼,增加 ** Item Loaders** 填充容器機制。通過 from scrapy.loader import ItemLoader
導(dǎo)入新類,該類的構(gòu)造函數(shù)如下:
1
|
def __init__( self , item = None , selector = None , response = None , parent = None , * * context) |
其中 item
是容器類,selector
為 Selector 對象,提取填充數(shù)據(jù)的選擇器,response
為 Response 響應(yīng)對象。
代碼修改之后得到如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import scrapy from uisdc.items import UisdcItem from scrapy.loader import ItemLoader class UiSpider(scrapy.Spider): name = 'ui' allowed_domains = [ 'www.uisdc.com' ] start_urls = [ 'https://www.uisdc.com/archives' ] custom_settings = { "ROBOTSTXT_OBEY" : False } def parse( self , response): items = response.xpath( '//div[@id="archive_list"]/div/div[1]/div[1]/div[contains(@class,"item-article")]' ) for i in items: l = ItemLoader(item = UisdcItem(), selector = i) l.add_xpath( 'title' , ".//h2[@class='item-title']/a/text()" ) l.add_xpath( 'author' , ".//h3[@class='meta-name']/text()" ) l.add_xpath( 'tag' , ".//div[@class='meta-tag']/a/text()" ) yield l.load_item() |
其中需要注意 l = ItemLoader(item=UisdcItem(), selector=i) 使用 selector
參數(shù),并賦值為迭代變量 i
,如果使用 response
會得到重復(fù)數(shù)據(jù)。
最后,當(dāng)所有數(shù)據(jù)被收集起來之后, 調(diào)用 ItemLoader.load_item()
方法, 返回 Item
對象。
輸出 item
對象,發(fā)現(xiàn)每一個數(shù)據(jù)都是列表。
1
2
3
|
{ 'author' : [ '土撥鼠' ], 'tag' : [ '產(chǎn)品設(shè)計' ], 'title' : [ '6000+干貨!資深總監(jiān)的四條產(chǎn)品設(shè)計工作觀(附私藏神器包)' ]} |
接下來需要處理每一項的值,ItemLoader 得到的數(shù)據(jù),在存入 item
容器前,是支持對數(shù)據(jù)進行預(yù)處理的,即輸入處理器和輸出處理器,修改 items.py
文件。
1
2
3
4
5
6
7
8
9
10
11
12
|
from scrapy.item import Item, Field from scrapy.loader.processors import MapCompose, TakeFirst def ext(value): return "新聞:" + value class UisdcItem(Item): # define the fields for your item here like: title = Field( input_processor = MapCompose(ext), output_processor = TakeFirst() ) author = Field(output_processor = TakeFirst()) tag = Field(output_processor = TakeFirst()) |
Field 字段的兩個參數(shù):
輸入處理器(input_processor):可以在傳進來的值做一些預(yù)處理。
輸出處理器(output_processor) :輸出值前最后的一步處理。
其中用到了 TakeFirst()
,返回第一個非空(non-null/ non-empty
)值,常用于單值字段的輸出處理器,無參數(shù)。
還用到了 MapCompose
,能把多個函數(shù)執(zhí)行的結(jié)果按順序組合起來,產(chǎn)生最終的輸出,通常用于輸入處理器。
其余內(nèi)置的處理器如下
Identity:不進行任何處理,返回原來的數(shù)據(jù),無參數(shù);
Join:返回用分隔符連接后的值,分隔符默認(rèn)為空格;
Compose:用給定的多個函數(shù)的組合,來構(gòu)造處理器,list 對象一次被傳遞到各個函數(shù)中,由最后一個函數(shù)返回整個處理器的輸出,默認(rèn)情況下遇到 None
值(list 中有 None 值)的時候停止處理,可以通過傳遞參數(shù) stop_on_none = False
改變這種行為;
MapCompose:輸入值是被迭代的處理的,List 對象中的每一個元素被單獨傳入,依次執(zhí)行對應(yīng)函數(shù)。
關(guān)于 item loader 還有一些其它的知識點,我們后面再聊。
以上就是python scrapy拆解查看Spider類爬取優(yōu)設(shè)網(wǎng)極細(xì)講解的詳細(xì)內(nèi)容,更多關(guān)于scrapy拆解Spider類爬取優(yōu)設(shè)網(wǎng)的資料請關(guān)注服務(wù)器之家其它相關(guān)文章!
原文鏈接:https://blog.csdn.net/hihell/article/details/120936534