iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 13
0

早安,昨天我們介紹了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)

如下圖,可以看到我們順利的抓取到內容了!

Imgur

比起之前用requestsBeautifulSoup實作是不是方便又簡單很多?現在來說明上面的程式碼,我們總共要爬標題, 連結, 日期, 作者, 推文數等資訊欄位。

介紹程式碼:

  • 爬取的標籤如何找這裡就不多做說明了,之後應該也會再找機會說明各種解析的方式。
  • 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。這裡用來產生上一頁urlnew變數。
  • scrapy.Request(new, callback = self.parse, dont_filter = True),傳入參數new便為上一頁的url,callback後為頁面解析函數,Requests請求的頁面下載完後由該參數指定的頁面解析函數被呼叫。
  • yield Request() : 用yield函數不會一次把所有值返回給你,會幫你在每次調用next()返回值,Scrapy內部會處理next,所以若用return則會直接結束函數不會增加新的urlyield我理解為是一個迭代器,返回可執行的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


上一篇
[Day 12] 撰寫一隻Spider
下一篇
[Day 14] Scrapy Item&Field
系列文
Scrapy爬蟲與資料處理30天筆記30

尚未有邦友留言

立即登入留言