運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲

上一篇內容介紹了爬蟲的基礎架構及環境配置,這一篇我們將用一個簡單的爬蟲例子來熟悉一下scrapy框架。

1 創建項目

首先要創建一個項目,注意:在你想要放代碼的目錄中執行下面的命令(具體如何在pycharm中調試可以看上一篇文章運維學python之爬蟲高級篇(一)Scrapy框架入門

scrapy startproject sp

創建完成後,項目結構如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲

2 第一個spider

2.1 尋找目標並編寫spider

爬取的網站是:中國圖書網
爬取的內容是:搜索python關鍵字返回的頁面信息中書籍名稱。如下圖:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
spider是你定義的類,而Scrapy用來從網站(或一組網站)中抓取信息。spider必須繼承scrapy.Spider子類和定義初始化的請求,可以選擇如何跟蹤頁面中的鏈接,以及如何解析下載的頁面內容以和提取數據。首先,在我們的項目中spiders目錄下創建tushu_spider.py,代碼內容如下:

# -*- coding: utf-8 -*-
import scrapy

class TushuSpider(scrapy.Spider):
    """
    定義圖書網爬蟲類,爬取搜索python後的圖書名稱
    """
    # name必須要有,而且再整個項目中是唯一的不能重複
    name = "tushu"

    def start_requests(self):
        """
        start_requests方法必須返回一個可迭代的請求(可以返回一個請求列表,或者編寫一個生成器函數),
        這將使爬蟲開始爬取數據。後續請求將依次從這些初始請求中生成。
        """
        # 定義urls列表
        urls = [
            'http://www.bookschina.com/book_find2/?stp=python&sCate=0',
            'http://www.bookschina.com/book_find2/default.aspx?stp=python&scate=0&f=1&sort=0&asc=0&sh=0&so=1&p=2&pb=1',
        ]
        for url in urls:
            # 這裏爲了返回一個可迭代的對象,使用了yield,yield的作用就是函數運行到這裏後中斷,
            # 並返回一個迭代值,下次執行時從yield的下一個語句繼續執行。
            yield scrapy.Request(url, callback=self.parse)

    def parse(self, response):
        """
        parse()方法通常解析response,提取數據,並找到新的url來跟蹤和創建新的請求(Request)。
        :param response: start_requests方法實例化的網頁
        """
        # 獲取response中class爲infor的div中class爲name的h2元素
        p = response.css("div.infor h2.name")
        # 從上面獲取的對象中提取出屬性爲title的標籤a的內容(列表形式)
        titles = p.css("a::attr(title)").extract()
        # 打印獲取的內容
        for title in titles:
            print(title)

上面的代碼已經詳細註釋了,過多的就不再說明。
其中的name、start_requests、parse等屬性和方法都是繼承scrapy.Spiders類。

2.2 運行

方法一:進入項目目錄,執行下面命令(其中tushu爲爬蟲類中定義的name=“tushu”)

scrapy crawl tushu

方法二:
在pycharm中運行main.py,main.py的內容如下:

# -*- coding: utf-8 -*-
from scrapy import cmdline
cmdline.execute("scrapy crawl tushu".split())

其實原理和方法一相同,只不過爲了方便在pycharm中使用,輸出結果如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
上面操作的流程如下:
1.通過start_requests中的scrapy.Request請求url;
2.收到response後,實例化爲對象,調用回調函數;
3.將實例化後的對象作爲參數,通過parse方法進行解析。

2.3 start_requests簡單格式

可以使用一個url列表來定義一個start_urls類屬性替代通過執行start_requests()方法從urls中生成scrapy.Request對象,然後,這個url列表將被用於start_requests()的默認實現來爲爬蟲創建初始請求,代碼如下:

# -*- coding: utf-8 -*-
import scrapy

class TushuSpider(scrapy.Spider):
    """
    定義圖書網爬蟲類,爬取搜索python後的圖書名稱
    """
    # name必須要有,而且再整個項目中是唯一的不能重複
    name = "tushu"
    # 定義urls列表,注意要寫成start_urls
    start_urls = [
        'http://www.bookschina.com/book_find2/?stp=python&sCate=0',
        'http://www.bookschina.com/book_find2/default.aspx?stp=python&scate=0&f=1&sort=0&asc=0&sh=0&so=1&p=2&pb=1',
    ]

    def parse(self, response):
        """
        parse()方法通常解析response,提取數據,並找到新的url來跟蹤和創建新的請求(Request)。
        :param response:下載的網頁內容
        """
        # 獲取response中class爲infor的div中class爲name的h2元素
        p = response.css("div.infor h2.name")
        # 從上面獲取的對象中提取出屬性爲title的標籤a的內容(列表形式)
        titles = p.css("a::attr(title)").extract()
        # 打印獲取的內容
        for title in titles:
            print(title)

上面的代碼執行效果與通過start_reuquests的方式是相同的,不過代碼省略了一些。之所以能這樣寫是因爲parse()是Scrapy的默認的回調方法,沒有明確指定回調的情況下,默認會調用parse()。

2.4 提取數據

如果光靠程序去跑,而不提前驗證我們的提取語句是否正確顯然是不高效的,最好的方式是使用scrapy的Scrapy shell 的方式去執行,如下例:

scrapy shell "http://www.bookschina.com/book_find2/?stp=python&sCate=0"

注意:網址要加引號,linux下可以單引號或雙引號,win下必須要用雙引號,防止因爲“?”、“&”等字符造成錯誤。
運行命令後,輸出結果如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
如果我們不確定是否獲取頁面成功可以看圖中標記出來的狀態碼,或者直接運行命令view(response)就會打開你獲取的response,如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
接下來我們就要通過工具分析,來獲取我們需要的內容了:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
從圖中可以看到,我們需要的是class爲infor的div標籤中的a標籤的title屬性。使用scrapy shell,可以使用CSS選擇response對象中的元素:

response.css("div.infor a::attr(title)")

結果如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
運行response.css("div.infor a::attr(title)")的結果是一個類似於列表的對象,名爲SelectorList,它代表了一個包含xml/html元素的選擇器對象列表,允許運行進一步的查詢,以便細粒度選擇或提取數據。
我們可以進一步獲取到title的內容,也就是上圖中data的部分,如下:

>>> response.css("div.infor a::attr(title)").extract()

結果如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
如果只想獲取第一個結果,可以直接運行:

>>> response.css("div.infor a::attr(title)").extract_first()

當然獲取的結果是列表,也可以使用如下命令:

>>> response.css("div.infor a::attr(title)")[0].extract()

以上兩種方法結果都是一樣的,均爲:'機器人Python極客編程入門與實戰::',不過使用.extract_first可以避免沒有獲取結果時返回的錯誤IndexError。
當然也可以是用正則re方式來匹配:

>>> response.css("div.infor a::attr(title)").re("python.*")
['python開始學編程']

還可以使用我們之前講過的xpath方式來獲取:

>>> response.xpath("//h2[@class='name']/a/@title").extract()
['機器人Python極客編程入門與實戰::', 'Python 實用教程', 'Python程序設計', 'Python絕技-運用Python成爲頂級***', '愛上Python-一日精通Python編程', 'Effective Python編寫高質量Python代碼的59個有效方法', '易學Python', 'Python入門經典', 'Python項 目開發實戰', 'Python算法教程', 'Python Wsb開發實戰', 'Python項目開發實戰', 'Python編程基礎', '流利的Python語言', 'Python開發實踐教程', 'Python網絡爬蟲實戰', 'Python金融實戰', 'Python開發嚮導', '毫無障礙學Python', '流暢的Python', 'Python數據分析', '用Python寫網絡爬蟲', 'Python即學即用', 'Python機器學習算法', 'Python漫遊指南-(影印版)', '人工智能:Python實現', 'Python大學教程', 'Python自然語言處理', 'Python遊戲編程入門', 'Python硬件編程實戰', 'Python測試驅動開發-(影印版)', 'Python程序設計', 'Python網絡數據採集', 'Python語言程序設計', 'Python密碼學編程', 'Python 3基礎教程', '零基礎入門學習Python', '從python開始學編程', 'Python數據可視化', 'Python機器學習實踐指南', 'Python數據科學實踐指南', 'Python爬蟲開發與項目實戰', 'Python高性能編程', 'Python 3.5從零開始學', '預測分析-Python語言實現', 'Python 數據分析基礎', 'Python 數據分析實踐', 'Python機器學習經典實例', 'Python數據分析基礎', '跟9787121325601老齊學Python實戰', 'Python程序設計教程', 'Python語言程序 設計']

至於cssxpath方式,需要大家多練習,可以用scrapy shell方式去嘗試獲取response的內容,不知道怎麼獲取的用搜索引擎查一查,因爲這樣記憶起來比去被背每一個的作用會好(個人拙見)。

2.5 存儲數據

最簡單的存儲方式是使用-o參數指定輸出:

scrapy crawl tushu -o title.json

如果是小項目,我們用上面的這種方式就可以,但大項目就要用到scrapy的Item pipline功能了,後續我們會介紹。

3 翻頁

爬取一頁數據肯定不是我們希望的,我們想要爬取搜索結果的所有頁面title,那要怎麼辦呢?好的,接下來介紹如何實現翻頁,其實更準確的是提取鏈接,不光可以用來翻頁,可以提取出初始網頁的鏈接進行進一步爬取。
我們使用翻頁是爲了更好理解,頁面信息如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
我們要獲取箭頭所指出的鏈接,如下:

>>> response.css("li.next a::attr(href)").extract_first()
'/book_find2/default.aspx?stp=python&scate=0&f=1&sort=0&asc=0&sh=0&so=1&p=2&pb=1'

現在看一看我們的spider,可以實現遞歸地跟蹤到下一個頁面的鏈接,從中提取數據:

# -*- coding: utf-8 -*-
import scrapy

class TushuSpider(scrapy.Spider):
    """
    定義圖書網爬蟲類,爬取搜索python後的圖書名稱
    """
    # name必須要有,而且再整個項目中是唯一的不能重複
    name = "tushu"
    # 定義urls列表
    start_urls = [
        'http://www.bookschina.com/book_find2/?stp=python&sCate=0',
    ]

    def parse(self, response):
        """
        parse()方法通常解析response,提取數據,並找到新的url來跟蹤和創建新的請求(Request)。
        :param response:下載的網頁內容
        """
        # 獲取response中class爲infor的div中class爲name的h2元素
        p = response.css("div.infor h2.name")
        # 從上面獲取的對象中提取出屬性爲title的標籤a的內容(列表形式)
        titles = p.css("a::attr(title)").extract()
        # 打印獲取的內容
        for title in titles:
            print(title)
        # 獲取下一頁中的鏈接
        next_page = response.css("li.next a::attr(href)").extract_first()
        if next_page is not None:
            # 通過urljoin將相對鏈接拼接成絕對鏈接
            next_page = response.urljoin(next_page)
            # 通過Request獲取next_page對象作爲參數傳入parse方法
            yield scrapy.Request(next_page, callback=self.parse)

輸出結果就是4頁的內容,太多就不截圖了。使用這個功能,就可以構建複雜的爬蟲程序,根據定義的規則來跟蹤鏈接,並根據所訪問的頁面提取不同類型的數據。
我們還可以使用response.follow來實現創建請求對象的快捷方式,代碼如下:

重複代碼略
        # 獲取下一頁中的鏈接
        next_page = response.css("li.next a::attr(href)").extract_first()
        if next_page is not None:
            # 使用response.follow拼接爲絕對路徑後獲取請求,調用回調函數處理
            yield response.follow(next_page, callback=self.parse)

不像scrapy.Request, response.follow直接支持相對url——不需要調用urljoin。注意response.follow只返回一個請求實例;仍然需要yield這個請求。

4 使用spider 參數

你可以使用命令行參數通過-a參數,指定spider的默認屬性,如下面命令中的tag:

scrapy crawl quotes -o quotes-humor.json -a tag=index

參數被傳遞給spider的init方法,並在默認情況下成爲了spider屬性。我們再用例子說明:

# -*- coding: utf-8 -*-
import scrapy

class TushuSpider(scrapy.Spider):
    """
    定義圖書網爬蟲類,爬取搜索python後的圖書名稱
    """
    # name必須要有,而且再整個項目中是唯一的不能重複
    name = "tushu"

    def start_requests(self):
        # 初始鏈接
        urls = 'http://www.bookschina.com/'
        # 通過getattr獲取tag屬性,不存在默認爲None
        tag = getattr(self, 'tag', None)
        if tag is not None:
            url = urls + tag
            yield scrapy.Request(url, self.parse)

    def parse(self, response):
        """
        parse()方法通常解析response,提取數據,並找到新的url來跟蹤和創建新的請求(Request)。
        :param response:下載的網頁內容
        """
        # 獲取response中class爲infor的div中class爲name的h2元素
        p = response.css("div.infor h2.name")
        # 從上面獲取的對象中提取出屬性爲title的標籤a的內容(列表形式)
        titles = p.css("a::attr(title)").extract()
        # 打印獲取的內容
        for title in titles:
            print(title)
        # 獲取下一頁中的鏈接
        next_page = response.css("li.next a::attr(href)").extract_first()
        if next_page is not None:
            # 使用response.follow拼接爲絕對路徑後獲取請求,調用回調函數處理
            yield response.follow(next_page, callback=self.parse)

執行命令:

scrapy crawl quotes -a tag=index

傳入的tag爲index,執行結果如下:
運維學python之爬蟲高級篇(二)用Scrapy框架實現簡單爬蟲
因爲頁面不存在,所以是404,而且我們也可以看到,訪問的地址只有:http://www.bookschina.com/index 這一個。

好了,這一篇就到這裏了,雖然只是做了一個簡單的基礎部分介紹,但篇幅還是比較長,許多功能仍沒有展開,後續我也會梳理條目來寫,主要還是多練習。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章