python scrapy拆解查看Spider類(lèi)爬取優(yōu)設(shè)網(wǎng)極細(xì)講解
拆解 scrapy.Spider
本次采集的目標(biāo)站點(diǎn)為:優(yōu)設(shè)網(wǎng)
每次創(chuàng)建一個(gè) spider
文件之后,都會(huì)默認(rèn)生成如下代碼:
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()
繼承的基類(lèi) scrapy.Spider
自然就成了我們要研究的第一個(gè)內(nèi)容,進(jìn)入其源碼,發(fā)現(xiàn)如下內(nèi)容。
scrapy.Spider 核心實(shí)現(xiàn)的是 start_requests 方法
Spider 主要進(jìn)行的操作就是初始化 Request 請(qǐng)求,而這些都是通過(guò) start_requests
實(shí)現(xiàn)的,詳細(xì)代碼為:
for url in self.start_urls: yield Request(url, dont_filter=True)
對(duì) start_requests
方法,你可以自己編寫(xiě)同名函數(shù)覆蓋修改,編寫(xiě)時(shí)發(fā)現(xiàn)了 make_requests_from_url
方法,該方法在最新版本的 scrapy
中已經(jīng)被廢除。
重寫(xiě) start_requests 方法 ,需要注意重寫(xiě)時(shí),必須返回一個(gè)可迭代對(duì)象,并且該對(duì)象包含 spider
用于爬取的第 1 個(gè) Request
,由于 scrapy
只調(diào)用一次該方法,所以你可以將登錄站點(diǎn)請(qǐng)求放置到該方法中。
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("重寫(xiě) start_requests") yield Request(self.start_urls[0]) def parse(self, response): print(response)
將登錄信息放置到 start_requests
中,代碼如下:
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("手動(dòng) start_requests") yield FormRequest("https://httpbin.org/post", formdata={"user": "ca"}, callback=self.parse) def parse(self, response): print(response.text)
scrapy.Spider 屬性值
name 屬性:
表示爬蟲(chóng)名稱(chēng),spider 的名稱(chēng)用于 scrapy
定位爬蟲(chóng),所以非常重要,一般常見(jiàn)的名稱(chēng)方式是使用網(wǎng)站域名(domain),命名 spider,例如 baidu.com
命名為 baidu
,但是工作喜歡還是攜帶 .com
后綴。
allowed_domains 屬性:
該屬性需要配置 offsiteMiddleware
使用,當(dāng)該中間件啟用之后,待采集 URL 的域名如果不在 allowed_domains
列表中,會(huì)被禁止訪(fǎng)問(wèn)。
domains
內(nèi)容添加,假設(shè)你的目標(biāo) URL 是 http://www.baidu.com/123.html
,僅填寫(xiě) baidu.com
即可。
start_urls 屬性:
起始的 URL 列表,主要用于 start_request
方法進(jìn)行迭代。
custom_settings 屬性:
自定義配置,可以覆蓋 settings.py
的配置,以字典格式賦值。
custom_settings = { "ROBOTSTXT_OBEY": False # 不請(qǐng)求 robot.txt 文件 }
crawler 屬性:
該屬性在爬蟲(chóng)啟動(dòng)后,由類(lèi)方法 from_crawler()
設(shè)置。
settings 屬性:
指定配置文件的實(shí)例。
logger 屬性:
spider 日志輸出對(duì)象,默認(rèn)以 spider 名稱(chēng)創(chuàng)建,可以自定義。
self.logger.info('輸出響應(yīng)地址 %s', response.url) logger.info('輸出響應(yīng)地址 %s', response.url)
補(bǔ)充一下 scrapy 日志級(jí)別
在 settings.py
中設(shè)置 log
級(jí)別,只需要增加一行代碼:
LOG_LEVEL = 'WARNING'
設(shè)置為 WARNING
級(jí)別,會(huì)發(fā)現(xiàn) scrapy 默認(rèn)的各種調(diào)試信息,都不在控制臺(tái)輸出。
scrapy
日志級(jí)別與 logging
模塊一致。
CRITICAL:嚴(yán)重錯(cuò)誤;
ERROR :一般錯(cuò)誤;
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 的最低級(jí)別。
scrapy.Spider 實(shí)例方法與類(lèi)方法
from_crawler 類(lèi)方法
在查看源碼之后,該方法的功能會(huì)比較清晰。
@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
兩個(gè)屬性,該方法在上一篇博客已經(jīng)有所涉及,直接回顧即可。
parse 方法
當(dāng)請(qǐng)求(Request)沒(méi)有指定回調(diào)參數(shù)(callback)時(shí),該方法是 scrapy
用來(lái)處理響應(yīng)的默認(rèn)回調(diào)方法。
log 方法
使用 self.log()
方法記錄日志。
學(xué)習(xí)到這里,對(duì) Spider 模塊有了一個(gè)比較整體的認(rèn)識(shí)。
爬取優(yōu)設(shè)網(wǎng)
接下來(lái)進(jìn)入爬蟲(chóng)采集相關(guān)代碼編寫(xiě),有了前文知識(shí)鋪墊之后,采集代碼就變得非常簡(jiǎn)單了。
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("測(cè)試是否有數(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
接下來(lái)修改源碼,增加 ** Item Loaders** 填充容器機(jī)制。通過(guò) from scrapy.loader import ItemLoader
導(dǎo)入新類(lèi),該類(lèi)的構(gòu)造函數(shù)如下:
def __init__(self, item=None, selector=None, response=None, parent=None, **context)
其中 item
是容器類(lèi),selector
為 Selector 對(duì)象,提取填充數(shù)據(jù)的選擇器,response
為 Response 響應(yīng)對(duì)象。
代碼修改之后得到如下代碼:
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
會(huì)得到重復(fù)數(shù)據(jù)。
最后,當(dāng)所有數(shù)據(jù)被收集起來(lái)之后, 調(diào)用 ItemLoader.load_item()
方法, 返回 Item
對(duì)象。
輸出 item
對(duì)象,發(fā)現(xiàn)每一個(gè)數(shù)據(jù)都是列表。
{'author': ['土撥鼠'], 'tag': ['產(chǎn)品設(shè)計(jì)'], 'title': ['6000+干貨!資深總監(jiān)的四條產(chǎn)品設(shè)計(jì)工作觀(guān)(附私藏神器包)']}
接下來(lái)需要處理每一項(xiàng)的值,ItemLoader 得到的數(shù)據(jù),在存入 item
容器前,是支持對(duì)數(shù)據(jù)進(jìn)行預(yù)處理的,即輸入處理器和輸出處理器,修改 items.py
文件。
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 字段的兩個(gè)參數(shù):
輸入處理器(input_processor):可以在傳進(jìn)來(lái)的值做一些預(yù)處理。
輸出處理器(output_processor) :輸出值前最后的一步處理。
其中用到了 TakeFirst()
,返回第一個(gè)非空(non-null/ non-empty
)值,常用于單值字段的輸出處理器,無(wú)參數(shù)。
還用到了 MapCompose
,能把多個(gè)函數(shù)執(zhí)行的結(jié)果按順序組合起來(lái),產(chǎn)生最終的輸出,通常用于輸入處理器。
其余內(nèi)置的處理器如下
Identity:不進(jìn)行任何處理,返回原來(lái)的數(shù)據(jù),無(wú)參數(shù);
Join:返回用分隔符連接后的值,分隔符默認(rèn)為空格;
Compose:用給定的多個(gè)函數(shù)的組合,來(lái)構(gòu)造處理器,list 對(duì)象一次被傳遞到各個(gè)函數(shù)中,由最后一個(gè)函數(shù)返回整個(gè)處理器的輸出,默認(rèn)情況下遇到 None
值(list 中有 None 值)的時(shí)候停止處理,可以通過(guò)傳遞參數(shù) stop_on_none = False
改變這種行為;
MapCompose:輸入值是被迭代的處理的,List 對(duì)象中的每一個(gè)元素被單獨(dú)傳入,依次執(zhí)行對(duì)應(yīng)函數(shù)。
關(guān)于 item loader 還有一些其它的知識(shí)點(diǎn),我們后面再聊。
以上就是python scrapy拆解查看Spider類(lèi)爬取優(yōu)設(shè)網(wǎng)極細(xì)講解的詳細(xì)內(nèi)容,更多關(guān)于scrapy拆解Spider類(lèi)爬取優(yōu)設(shè)網(wǎng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 提升Python Scrapy庫(kù)數(shù)據(jù)采集速度實(shí)現(xiàn)高效爬蟲(chóng)
- 詳解如何優(yōu)化和調(diào)整Python中Scrapy的性能
- python爬蟲(chóng)框架scrapy代理中間件掌握學(xué)習(xí)教程
- python爬蟲(chóng)框架Scrapy基本應(yīng)用學(xué)習(xí)教程
- python實(shí)戰(zhàn)項(xiàng)目scrapy管道學(xué)習(xí)爬取在行高手?jǐn)?shù)據(jù)
- python實(shí)戰(zhàn)scrapy操作cookie爬取博客涉及browsercookie
- python編程scrapy簡(jiǎn)單代碼實(shí)現(xiàn)搜狗圖片下載器
- Python爬蟲(chóng)進(jìn)階Scrapy框架精文講解
- Scrapy基于Python構(gòu)建強(qiáng)大網(wǎng)絡(luò)爬蟲(chóng)框架實(shí)例探究
相關(guān)文章
Python實(shí)現(xiàn)人機(jī)中國(guó)象棋游戲
中國(guó)象棋是一種古老的棋類(lèi)游戲,大約有兩千年的歷史。本文將介紹如何通過(guò)Python中的Pygame模塊實(shí)現(xiàn)人機(jī)中國(guó)象棋游戲,感興趣的可以學(xué)習(xí)一下2022-01-01Python實(shí)現(xiàn)將數(shù)據(jù)框數(shù)據(jù)寫(xiě)入mongodb及mysql數(shù)據(jù)庫(kù)的方法
這篇文章主要介紹了Python實(shí)現(xiàn)將數(shù)據(jù)框數(shù)據(jù)寫(xiě)入mongodb及mysql數(shù)據(jù)庫(kù)的方法,結(jié)合具體實(shí)例形式分析了Python針對(duì)mongodb及mysql數(shù)據(jù)庫(kù)的連接、寫(xiě)入等操作實(shí)現(xiàn)技巧,需要的朋友可以參考下2018-04-04Django Model層F,Q對(duì)象和聚合函數(shù)原理解析
這篇文章主要介紹了Django Model層F,Q對(duì)象和聚合函數(shù)原理解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11python 執(zhí)行文件時(shí)額外參數(shù)獲取的實(shí)例
今天小編就為大家分享一篇python 執(zhí)行文件時(shí)額外參數(shù)獲取的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-12-12Python使用jsonpath-rw模塊處理Json對(duì)象操作示例
這篇文章主要介紹了Python使用jsonpath-rw模塊處理Json對(duì)象操作,結(jié)合實(shí)例形式分析了Python使用requests與response處理json的方法,并給出了jsonpath_rw模塊操作json對(duì)象的基本示例,需要的朋友可以參考下2018-07-07Jupyter notebook之如何快速打開(kāi)ipynb文件
這篇文章主要介紹了Jupyter notebook之如何快速打開(kāi)ipynb文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09Python?lambda函數(shù)使用方法深度總結(jié)
在本文中,小編將帶大家學(xué)習(xí)一下Python中的lambda函數(shù),并探討使用它的優(yōu)點(diǎn)和局限性。文中的示例代碼講解詳細(xì),感興趣的可以了解一下2022-05-05詳解Python實(shí)現(xiàn)字典合并的四種方法
這篇文章主要為大家詳細(xì)介紹了Python的合并字典的四種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-03-03在pycharm中關(guān)掉ipython console/PyDev操作
這篇文章主要介紹了在pycharm中關(guān)掉ipython console/PyDev操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06