早安,昨天我們介紹了spider
的基本架構,今天會介紹spider
實現ptt的爬蟲,透過Scrapy
框架可以減少很多程式碼。
因為我們對於爬蟲的流程已經有稍微地瞭解了,我就直接貼上全部的程式碼了:
import scrapy
from scrapy.exceptions import CloseSpider
class PttSpider(scrapy.Spider):
count_page = 1
name = 'ptt'
allowed_domains = ['www.ptt.cc/']
start_urls = ['https://www.ptt.cc/bbs/movie/index.html']
def parse(self, response):
for q in response.css('div.r-ent'):
item = {
'push':q.css('div.nrec > span.hl::text').extract_first(),
'title':q.css('div.title > a::text').extract_first(),
'href':q.css('div.title > a::attr(href)').extract_first(),
'date':q.css('div.meta > div.date ::text').extract_first(),
'author':q.css('div.meta > div.author ::text').extract_first(),
}
yield(item)
next_page_url = response.css('div.action-bar > div.btn-group > a.btn::attr(href)')[3].extract()
if (next_page_url) and (self.count_page < 10):
self.count_page = self.count_page + 1
new = response.urljoin(next_page_url)
else:
raise CloseSpider('close it')
yield scrapy.Request(new, callback = self.parse, dont_filter = True)
如下圖,可以看到我們順利的抓取到內容了!
比起之前用requests
與BeautifulSoup
實作是不是方便又簡單很多?現在來說明上面的程式碼,我們總共要爬標題
, 連結
, 日期
, 作者
, 推文數
等資訊欄位。
介紹程式碼:
CloseSpider
這個方法用來關閉spider,我們放在if判斷內,若超過頁數了,就關閉spider
,停止的時候也會看到[scrapy.core.engine] INFO: Closing spider (close it)
的內容。舉例來說可以:def parse_page(self, response):
if 'Bandwidth exceeded' in response.body:
raise CloseSpider('bandwidth_exceeded')
q.css()
為css
選擇器用來解析網頁資訊。count_page
用來計算總共要爬取幾頁內容。urljoin(url)
用於建置絕對url,當傳入的參數為一個相對位址href時,會根據response.url
組合成對應的url。這裡用來產生上一頁
的url
給new
變數。scrapy.Request(new, callback = self.parse, dont_filter = True)
,傳入參數new
便為上一頁的url,callback
後為頁面解析函數,Requests
請求的頁面下載完後由該參數指定的頁面解析函數被呼叫。yield Request()
: 用yield
函數不會一次把所有值返回給你,會幫你在每次調用next()
返回值,Scrapy
內部會處理next
,所以若用return
則會直接結束函數不會增加新的url
。yield
我理解為是一個迭代器,返回可執行的next函數,進行下一個url
的爬取。for q in response.css('div.r-ent'):
item = {
'push':q.css('div.nrec > span.hl::text').extract_first(),
'title':q.css('div.title > a::text').extract_first(),
'href':q.css('div.title > a::attr(href)').extract_first(),
'date':q.css('div.meta > div.date ::text').extract_first(),
'author':q.css('div.meta > div.author ::text').extract_first(),
}
yield(item)
可以看到上面的例子中,我們使用Python的dictionary
方式存資料,不過這樣可能會有缺點,dictionary
雖然方便卻缺少結構性,容易打錯字或者回傳不一致的數據,特別是在多個Spider
的專案中,所以明天我們會說明Item
類別,用來封裝爬取到的資料,以及說明為什麼要用Item
!
好的,那今天就到這啦!明天見!
參考來源:
Exceptions — Scrapy 1.5.1 documentation