Python爬蟲(chóng)進(jìn)階Scrapy框架精文講解
一、前情提要
為什么要使用Scrapy 框架?
前兩篇深造篇介紹了多線程這個(gè)概念和實(shí)戰(zhàn)
多線程網(wǎng)頁(yè)爬取
多線程爬取網(wǎng)頁(yè)項(xiàng)目實(shí)戰(zhàn)
經(jīng)過(guò)之前的學(xué)習(xí),我們基本掌握了分析頁(yè)面、分析動(dòng)態(tài)請(qǐng)求、抓取內(nèi)容,也學(xué)會(huì)使用多線程來(lái)并發(fā)爬取網(wǎng)頁(yè)提高效率。這些技能點(diǎn)已經(jīng)足夠我們寫(xiě)出各式各樣符合我們要求的爬蟲(chóng)了。
但我們還有一個(gè)沒(méi)解決的問(wèn)題,那就是工程化。工程化可以讓我們寫(xiě)代碼的過(guò)程從「想一段寫(xiě)一段」中解脫出來(lái),變得有秩序、風(fēng)格統(tǒng)一、不寫(xiě)重復(fù)的東西。
而Scrapy 就是爬蟲(chóng)框架中的佼佼者。它為我們提前想好了很多步驟和要處理的邊邊角角的問(wèn)題,而使用者可以專心于處理解析頁(yè)面、分析請(qǐng)求這種最核心的事情。
二、Scrapy框架的概念
Scrapy 是一個(gè)純 Python 實(shí)現(xiàn)的、流行的網(wǎng)絡(luò)爬蟲(chóng)框架,它使用了一些高級(jí)功能來(lái)簡(jiǎn)化網(wǎng)頁(yè)的抓取,能讓我們的爬蟲(chóng)更加的規(guī)范、高效。
它可以分為如下幾個(gè)部分
組件 | 功能 |
---|---|
Scrapy Engine | Scrapy 引擎,負(fù)責(zé)控制整個(gè)系統(tǒng)的數(shù)據(jù)流和事件的觸發(fā) |
Scheduler | 調(diào)度器,接收 Scrapy 引擎發(fā)來(lái)的請(qǐng)求并將其加入隊(duì)列中,等待引擎后續(xù)需要時(shí)使用 |
Downloader | 下載器,爬取網(wǎng)頁(yè)內(nèi)容,將爬取到的數(shù)據(jù)返回給 Spiders(爬蟲(chóng)) |
Spiders | 爬蟲(chóng),這部分是核心代碼,用于解析、提取出需要的數(shù)據(jù) |
Item Pipeline | 數(shù)據(jù)管道,處理提取出的數(shù)據(jù),主要是數(shù)據(jù)清洗、驗(yàn)證和數(shù)據(jù)存儲(chǔ) |
Downloader middlewares | 下載器中間件,處理 Scrapy 引擎和下載器之間的請(qǐng)求和響應(yīng) |
Spider middlewares | 爬蟲(chóng)中間件,處理爬蟲(chóng)輸入的響應(yīng)以及輸出結(jié)果或新的請(qǐng)求 |
Scrapy 中數(shù)據(jù)流的過(guò)程如下
步驟 | 數(shù)據(jù)流 |
---|---|
1 | 引擎打開(kāi)一個(gè)網(wǎng)站,找到處理該網(wǎng)站對(duì)應(yīng)的爬蟲(chóng),并爬取網(wǎng)頁(yè)的第一個(gè)頁(yè)面 |
2 | 引擎從爬蟲(chóng)中獲取第一個(gè)頁(yè)面地址,并將其作為請(qǐng)求放進(jìn)調(diào)度器中進(jìn)行調(diào)度 |
3 | 引擎從調(diào)度器中獲取下一個(gè)頁(yè)面的地址 |
4 | 調(diào)度器返回下一個(gè)頁(yè)面的地址給 Scrapy 引擎,Scrapy 引擎通過(guò)下載器中間件傳遞給下載器進(jìn)行爬取 |
5 | 爬取到數(shù)據(jù)后,下載器通過(guò)下載器中間件回傳給 Scrapy 引擎 |
6 | Scrapy 引擎將爬取到的數(shù)據(jù)通過(guò)爬蟲(chóng)中間件傳遞給爬蟲(chóng)進(jìn)行數(shù)據(jù)解析、提取 |
7 | 爬蟲(chóng)處理完數(shù)據(jù)后,將提取出的數(shù)據(jù)和新的請(qǐng)求回傳給 Scrapy 引擎 |
8 | Scrapy 將提取出的數(shù)據(jù)傳給數(shù)據(jù)管道進(jìn)行數(shù)據(jù)清洗等操作,同時(shí)將新的請(qǐng)求傳遞給調(diào)度器準(zhǔn)備進(jìn)行下一頁(yè)的爬取 |
9 | 重復(fù) 2-8 步,直到調(diào)度器中沒(méi)有新的請(qǐng)求,數(shù)據(jù)爬取結(jié)束 |
三、Scrapy安裝
Win + R打開(kāi)運(yùn)行,點(diǎn)擊確定
然后在命令行上敲上
pip install scrapy -i https://pypi.doubanio.com/simple/ # 這句話后面 -i https://pypi.doubanio.com/simple/ 表示使用豆瓣的源,這樣安裝會(huì)更快
之后點(diǎn)擊回車,等待它自己安裝完成便可!
然后,我們?cè)诿钚猩锨蒙?mark>scrapy,便會(huì)顯示scrapy的信息,這樣就代表安裝成功啦!
注意!!再然后我們?cè)诿钚星蒙?explorer . (Mac 中是 open .,注意 . 前面有個(gè)空格) 命令并回車,可以打開(kāi)命令行當(dāng)前所在的目錄。下面,我們就要在這個(gè)目錄里開(kāi)始編寫(xiě)代碼。
四、Scrapy實(shí)戰(zhàn)運(yùn)用
這次我們?cè)囍肧crapy爬取的網(wǎng)站是:小眾軟件 https://www.appinn.com/category/windows/
在進(jìn)行網(wǎng)頁(yè)爬取前,我們先需要?jiǎng)?chuàng)建代碼文件,然后利用Scrapy命令進(jìn)行執(zhí)行。
在上面我們利用 explorer . 命令打開(kāi)了目錄,在這個(gè)目錄下我們創(chuàng)建一個(gè) spider.py 的文件↓
方法:創(chuàng)建文本文件改后綴名即可
然后將爬蟲(chóng)代碼放進(jìn)去,現(xiàn)在大家先復(fù)制黏貼代碼,進(jìn)行嘗試一下,之后我再來(lái)講解代碼的含義!
爬蟲(chóng)代碼
import scrapy # 定義一個(gè)類叫做 TitleSpider 繼承自 scrapy.Spider class TitleSpider(scrapy.Spider): name = 'title-spider' # 設(shè)定開(kāi)始爬取的頁(yè)面 start_urls = ['https://www.appinn.com/category/windows/'] def parse(self, response): # 找到所有 article 標(biāo)簽 for article in response.css('article'): # 解析 article 下面 a 標(biāo)簽里的鏈接和標(biāo)題 a = article.css('h2.title a') if a: result = { 'title': a.attrib['title'], 'url': a.attrib['href'], } # 得到結(jié)果 yield result # 解析下一頁(yè)的鏈接 next_page = response.css('a.next::attr(href)').get() if next_page is not None: # 開(kāi)始爬下一頁(yè),使用 parse 方法解析 yield response.follow(next_page, self.parse)
然后在命令行中執(zhí)行 scrapy 的 runspider 命令
scrapy runspider spider.py -t csv -o apps.csv # spider.py 是剛剛寫(xiě)的爬蟲(chóng)代碼的文件名 # -t 表示輸出的文件格式,我們用 csv,方便用 Excel 等工具打開(kāi) # -o 表示輸出的文件名,所以執(zhí)行完會(huì)出現(xiàn)一個(gè) apps.csv 的文件
敲完上面這句命令,稍等一下,你應(yīng)該能看見(jiàn)很多的輸出👇
網(wǎng)頁(yè)爬取結(jié)果
以及目錄里多出來(lái)一個(gè) apps.csv 文件。有 Excel 的同學(xué)可以用 Excel 打開(kāi) apps.csv,或者直接用記事本或者其他編輯器打開(kāi)它。
打開(kāi)后能看見(jiàn) 400 多篇小眾軟件的軟件推薦文章的標(biāo)題和鏈接👇
可是我們的代碼里完全沒(méi)有用到 requests、beautifulsoup、concurrent 以及文件相關(guān)的庫(kù),是怎么完成了一次快速的爬取并寫(xiě)到文件的呢?別急,讓我為你慢慢講解!
這一串代碼干了什么?
上面用到的爬蟲(chóng)代碼
import scrapy # 定義一個(gè)類叫做 TitleSpider 繼承自 scrapy.Spider class TitleSpider(scrapy.Spider): name = 'title-spider' # 設(shè)定開(kāi)始爬取的頁(yè)面 start_urls = ['https://www.appinn.com/category/windows/'] def parse(self, response): # 找到所有 article 標(biāo)簽 for article in response.css('article'): # 解析 article 下面 a 標(biāo)簽里的鏈接和標(biāo)題 a = article.css('h2.title a') if a: result = { 'title': a.attrib['title'], 'url': a.attrib['href'], } # 得到結(jié)果 yield result # 解析下一頁(yè)的鏈接 next_page = response.css('a.next::attr(href)').get() if next_page is not None: # 開(kāi)始爬下一頁(yè),使用 parse 方法解析 yield response.follow(next_page, self.parse)
當(dāng)運(yùn)行scrapy runspider spider.py -t csv -o apps.csv時(shí),Scrapy 會(huì)執(zhí)行我們寫(xiě)在 spider.py里的爬蟲(chóng),也就是上面那段完整的代碼
1、首先,Scrapy 讀到我們?cè)O(shè)定的啟動(dòng)頁(yè)面 start_urls,開(kāi)始請(qǐng)求這個(gè)頁(yè)面,得到一個(gè)響應(yīng)。
// An highlighted block start_urls = ['https://www.appinn.com/category/windows/']
2、之后,Scrapy 把這個(gè)響應(yīng)交給 默認(rèn) 的解析方法 parse 來(lái)處理。響應(yīng) response 就是 parse 的第一個(gè)參數(shù)
def parse(self, response):
3、在我們自己寫(xiě)的 parse 方法里,有兩個(gè)部分:一是解析出頁(yè)面里的 article 標(biāo)簽,得到標(biāo)題和鏈接作為爬取的結(jié)果;二是解析 下一頁(yè) 按鈕這個(gè)位置,拿到下一頁(yè)的鏈接,并同樣繼續(xù)請(qǐng)求、然后使用 parse 方法解析
# 把這條結(jié)果告訴 Scrapy yield result # 通知 Scrapy 開(kāi)始爬下一頁(yè),使用 parse 方法解析 yield response.follow(next_page, self.parse)
yield 是 Python 中一個(gè)較高級(jí)的用法,在這里我們只需要知道,我們通過(guò) yield 通知 Scrapy 兩件事:我們拿到了結(jié)果,快去處理吧、我們拿到了下一個(gè)要爬的鏈接,快去爬取吧。
流程圖
沒(méi)錯(cuò),除了解析想要的數(shù)據(jù),其他的一切都是 Scrapy 替你完成的。這就是 Scrapy 的最大優(yōu)勢(shì):
requests 去哪了?不需要,只要把鏈接交給 Scrapy 就會(huì)自動(dòng)幫你完成請(qǐng)求;
concurrent 去哪了?不需要,Scrapy 會(huì)自動(dòng)把全部的請(qǐng)求都變成并發(fā)的;
怎么把結(jié)果寫(xiě)到文件?不用實(shí)現(xiàn)寫(xiě)文件的代碼,使用 yield 通知一下 Scrapy 結(jié)果即可自動(dòng)寫(xiě)入文件;
怎么繼續(xù)爬取下一個(gè)頁(yè)面?使用 yield 通知 =Scrapy下一個(gè)頁(yè)面的鏈接和處理方法就好;
BeautifulSoup 去哪了?可以不需要,Scrapy 提供了好用的 CSS 選擇器。
解析數(shù)據(jù)這件事情還是值得我們關(guān)心的,即使 Scrapy 沒(méi)有強(qiáng)制讓我們使用什么,因此我們非要繼續(xù)使用 BeautifulSoup也是可以的,只需在 parse() 方法里將 response.text 傳遞給 BeautifulSoup 進(jìn)行解析、提取即可。
但是 Scrapy 提供了很好用的工具,叫做 CSS 選擇器。CSS 選擇器我們?cè)?BeautifulSoup 中簡(jiǎn)單介紹過(guò),你還有印象嗎?
對(duì)BeautifulSoup這個(gè)庫(kù)忘記了的同學(xué),可以看看我之前寫(xiě)的一篇文章:requests庫(kù)和BeautifulSoup庫(kù)
Scrapy 中的 CSS 選擇器語(yǔ)法和 BeautifulSoup 中的差不多,Scrapy 中的 CSS 選擇器更加強(qiáng)大一些
# 從響應(yīng)里解析出所有 article 標(biāo)簽 response.css('article') # 從 article 里解析出 class 為 title 的 h2 標(biāo)簽 下面的 a 標(biāo)簽 article.css('h2.title a') # 取出 a 里面的 href 屬性值 a.attrib['href'] # 從響應(yīng)里解析出 class 為 next 的 a 標(biāo)簽的 href 屬性,并取出它的值 response.css('a.next::attr(href)').get()
scrapy 中的 CSS 選擇器可以取代 beautifulsoup 的功能,我們直接用它就解析、提取獲取到的數(shù)據(jù)??吹竭@里,再回頭看上面的完整代碼,試著結(jié)合流程圖再理解一下就會(huì)有不錯(cuò)的了解了。
五、Scrapy的css選擇器教學(xué)
我們還是打開(kāi)之前爬取的網(wǎng)站:小眾軟件 https://www.appinn.com/category/windows/
用網(wǎng)頁(yè)開(kāi)發(fā)者工具選中 下一頁(yè)↓
注意截圖中被框住的部分,瀏覽器已經(jīng)展示出來(lái)這個(gè)按鈕的 CSS 選擇方法是什么了。它告訴我們下一頁(yè)按鈕的選擇方式是a.next.page-numbers。
開(kāi)始css選擇器教學(xué)前,我建議你使用 Scrapy 提供的互動(dòng)工具來(lái)體驗(yàn)一下 CSS 選擇器。方式是在命令行中輸入以下命令并回車
scrapy shell "https://www.appinn.com/category/windows/"
這時(shí) Scrapy 已經(jīng)訪問(wèn)了這個(gè)鏈接,并把獲取到的結(jié)果記錄了下來(lái),你會(huì)進(jìn)入到一個(gè)交互環(huán)境,我們可以在這個(gè)環(huán)境里寫(xiě)代碼并一句一句執(zhí)行。輸入 response 并回車,你能看見(jiàn)類似下面的響應(yīng),這就是上面獲取到的網(wǎng)頁(yè)結(jié)果。
我之前有說(shuō)過(guò),輸出的200其實(shí)就是一個(gè)響應(yīng)狀態(tài)碼,意思就是請(qǐng)求成功了!
之前的文章:requests庫(kù)和BeautifulSoup庫(kù)
下面我們就來(lái)學(xué)習(xí)一下css選擇器吧,我們以下圖中“下一頁(yè)”按鈕為例子👇
按標(biāo)簽名選擇
小眾軟件網(wǎng)站下一頁(yè)按鈕的選擇方式是 a.next.page-numbers,其中的 a 是標(biāo)簽名。試著在互動(dòng)環(huán)境中,輸入 response.css(‘a(chǎn)'),可以看到頁(yè)面上所有的 a 元素。其他元素也是一樣,例如寫(xiě) response.css(‘ul') 就可以選擇出所有 ul 元素,response.css(‘div') 可以選擇出 div 元素。
按 class 選擇
a.next.page-numbers 中的.next 和 .page-numbers 表示 class的名字。當(dāng)我們想要選擇 class 包含 container 的 div 元素,我們可以寫(xiě)response.css(‘div.container')。
上面的選擇器前面是標(biāo)簽名,. 表示 class,后面跟著 class 的名稱。注意,它們是緊緊挨在一起的,中間不能有空格!
當(dāng)要選擇的元素有多個(gè) class 時(shí),比如下面這樣的一個(gè)元素
<a class="next page-numbers" href="/windows/page/2/" rel="external nofollow">下一頁(yè)</a>
這個(gè) a 元素有 next 和 page-number 兩個(gè) class,可以寫(xiě)多個(gè) . 來(lái)選擇:response.css(‘a(chǎn).next.page-numbers')。表示選擇 class 同時(shí)包含 next 和 page-numbers 的 a 元素,這里它們也必須緊挨在一起,之前不能有空格。
按 id 選擇
除了 class 選擇器外,同樣也有 id 選擇器,Scrapy 中也是用 # 代表 id。比如網(wǎng)頁(yè)上的菜單按鈕,我們看到它的 id 是 pull,class 是 toggle-mobile-menu。所以可以寫(xiě) response.css(‘a(chǎn)#pull'),表示我們想選擇一個(gè) id 為 pull 的 a 元素。
當(dāng)然,你也可以組合使用:response.css(‘a(chǎn)#pull.toggle-mobile-menu')。表示我們要選擇 id 為 pull,并且 class 包含 toggle-mobile-menu 的 a 元素。
按層級(jí)關(guān)系選擇
還是小眾軟件的這個(gè)頁(yè)面,如果我們想用 CSS 選擇器選中標(biāo)題這個(gè)位置的 a 元素,使用 Chrome 選取之后發(fā)現(xiàn)這個(gè) a 元素既沒(méi)有 id 也沒(méi)有 class,瀏覽器也只給我們提示一個(gè) a👇
這時(shí),我們就需要在該元素的父元素上找找線索,例如我們發(fā)現(xiàn)這個(gè) a 元素在一個(gè) h2 元素的下面,而這個(gè) h2 元素是有 class 的,class 為 title 和 post-title。所以我們要做的是選擇 class 為 title 和 post-title 的 h2 元素下面的 a 元素,用 CSS 選擇器寫(xiě)作
response.css('h2.title.post-title a::text')
可以看到,title 和 post-title 的 h2 元素寫(xiě)作 h2.title.post-title,是緊緊連在一起的,而下面的 a 元素則在 a 前面加了一個(gè)空格。想起之前說(shuō)過(guò)的規(guī)則了嗎?規(guī)則便是:并列關(guān)系連在一起,層級(jí)關(guān)系用空格隔開(kāi)。
.title 和 .post-title 緊跟在 h2 后面,它倆都是 h2 的篩選條件。而空格后面跟著的 a 表示符合 h2.title.post-title 條件元素的子元素中的所有 a 元素。
我們之前還說(shuō)過(guò),空格后面表示的是所有的子元素,不管是處于多少層的子元素。而如果只想要第一層的子元素則應(yīng)該用 > 分隔開(kāi)。這里的 a 元素就是第一層的子元素,所以 h2.title.post-title a 和 h2.title.post-title > a 這兩種寫(xiě)法的效果是一樣的。
取元素中的文本
我們拿到了標(biāo)題位置的 a 元素,想要拿到其中的文本內(nèi)容就需要在后面加上 ::text,代碼如下
response.css('h2.title.post-title a::text')
在互動(dòng)環(huán)境執(zhí)行一下,會(huì)發(fā)現(xiàn)文本內(nèi)容能獲取到,但不是我們想要的純文本,如果想拿到純文本,還需要使用 get() 或者 getall() 方法,如下
# 取符合條件的第一條數(shù)據(jù) response.css('h2.title.post-title a::text').get() # 取符合條件的所有數(shù)據(jù) response.css('h2.title.post-title a::text').getall()
取元素的屬性
還是用這個(gè) a 元素舉例。如果我們想得到這個(gè) a 元素的 href 屬性,需要調(diào)用這個(gè)元素的 attrib 屬性。在互動(dòng)環(huán)境中執(zhí)行下面兩句代碼
# 拿到符合選擇器條件的第一個(gè) a 標(biāo)簽 a = response.css('h2.title.post-title a') a.attrib['href']
attrib 屬性實(shí)際上是一個(gè)字典,里面存儲(chǔ)了元素上的所有 HTML 屬性。如果把第二句換成 a.attrib,你就能看到這個(gè) a 元素上的所有屬性。類似的,輸入 a.attrib[‘title'],你可以得到它的 title 屬性。
現(xiàn)在我們?cè)囋嚧蛴∷蟹?h2.title.post-title a 這個(gè)條件的標(biāo)簽的 href 屬性,就像下面這樣
for a in response.css('h2.title.post-title a'): print(a.attrib['href'])
或者另一種寫(xiě)法也可以取到 href 屬性,就是加上 ::attr(href)
for href in response.css('h2.title.post-title a::attr(href)').getall(): print(href)
本次分享到此結(jié)束了,非常感謝大家閱讀!!
有問(wèn)題歡迎評(píng)論區(qū)留言?。?/p>
以上就是Python爬蟲(chóng)進(jìn)階Scrapy框架精文講解的詳細(xì)內(nèi)容,更多關(guān)于Python爬蟲(chóng)Scrapy框架的資料請(qǐng)關(guān)注本站其它相關(guān)文章!
版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。