scrapy初探

Spider設置:


在該目錄運行下面命令


E:\py\Quotetutorial\Quotetutorial>scrapy3 genspider zhihu www.zhihu.com   生成zhihu.py,並且域名爲www.zhihu.com

E:\py\Quotetutorial\Quotetutorial>scrapy3 crawl zhihu     運行zhihu.py


start_requests()

該方法必須返回一個可迭代對象(iterable)。該對象包含了spider用於爬取的第一個Request。

當spider啓動爬取並且未制定URL時,該方法被調用。 當指定了URL時,make_requests_from_url() 將被調用來創建Request對象。 該方法僅僅會被Scrapy調用一次,因此您可以將其實現爲生成器。

該方法的默認實現是使用 start_urls 的url生成Request。

如果您想要修改最初爬取某個網站的Request對象,您可以重寫(override)該方法。 例如,如果您需要在啓動時以POST登錄某個網站,你可以這麼寫:

def start_requests(self):
    return [scrapy.FormRequest("http://www.example.com/login",
                               formdata={'user': 'john', 'pass': 'secret'},
                               callback=self.logged_in)]

def logged_in(self, response):
    # here you would extract links to follow and return Requests for
    # each of them, with another callback
    pass


make_requests_from_url(url)

該方法接受一個URL並返回用於爬取的 Request 對象。 該方法在初始化request時被 start_requests() 調用,也被用於轉化url爲request。

默認未被複寫(overridden)的情況下,該方法返回的Request對象中, parse()作爲回調函數,dont_filter參數也被設置爲開啓。 (詳情參見 Request).






Spiders

Spider類定義瞭如何爬取某個(或某些)網站。包括了爬取的動作(例如:是否跟進鏈接)以及如何從網頁的內容中提取結構化數據(爬取item)。 換句話說,Spider就是您定義爬取的動作及分析某個網頁(或者是有些網頁)的地方。

對spider來說,爬取的循環類似下文:

  1. 以初始的URL初始化Request,並設置回調函數。 當該request下載完畢並返回時,將生成response,並作爲參數傳給該回調函數。

    spider中初始的request是通過調用 start_requests() 來獲取的。 start_requests()讀取 start_urls 中的URL, 並以 parse 爲回調函數生成 Request 。

  2. 在回調函數內分析返回的(網頁)內容,返回 Item 對象或者 Request 或者一個包括二者的可迭代容器。 返回的Request對象之後會經過Scrapy處理,下載相應的內容,並調用設置的callback函數(函數可相同)。

  3. 在回調函數內,您可以使用 選擇器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 來分析網頁內容,並根據分析的數據生成item。

  4. 最後,由spider返回的item將被存到數據庫(由某些 Item Pipeline 處理)或使用 Feed exports 存入到文件中。

雖然該循環對任何類型的spider都(多少)適用,但Scrapy仍然爲了不同的需求提供了多種默認spider。 之後將討論這些spider。

Spider參數

Spider可以通過接受參數來修改其功能。 spider參數一般用來定義初始URL或者指定限制爬取網站的部分。 您也可以使用其來配置spider的任何功能。

在運行 crawl 時添加 -a 可以傳遞Spider參數:

scrapy crawl myspider -a category=electronics

Spider在構造器(constructor)中獲取參數:

import scrapy

class MySpider(Spider):
    name = 'myspider'

    def __init__(self, category=None, *args, **kwargs):
        super(MySpider, self).__init__(*args, **kwargs)
        self.start_urls = ['http://www.example.com/categories/%s' % category]
        # ...

Spider參數也可以通過Scrapyd的 schedule.json API來傳遞。 參見 Scrapyd documentation.





Item Pipeline

當Item在Spider中被收集之後,它將會被傳遞到Item Pipeline,一些組件會按照一定的順序執行對Item的處理。

每個item pipeline組件(有時稱之爲“Item Pipeline”)是實現了簡單方法的Python類。他們接收到Item並通過它執行一些行爲,同時也決定此Item是否繼續通過pipeline,或是被丟棄而不再進行處理。

以下是item pipeline的一些典型應用:

  • 清理HTML數據
  • 驗證爬取的數據(檢查item包含某些字段)
  • 查重(並丟棄)
  • 將爬取結果保存到數據庫中

編寫你自己的item pipeline

編寫你自己的item pipeline很簡單,每個item pipeline組件是一個獨立的Python類,同時必須實現以下方法:

process_item(selfitemspider)

每個item pipeline組件都需要調用該方法,這個方法必須返回一個 Item (或任何繼承類)對象, 或是拋出 DropItem 異常,被丟棄的item將不會被之後的pipeline組件所處理。

參數:
  • item (Item 對象) – 被爬取的item
  • spider (Spider 對象) – 爬取該item的spider

此外,他們也可以實現以下方法:

open_spider(selfspider)

當spider被開啓時,這個方法被調用。

參數:spider (Spider 對象) – 被開啓的spider
close_spider(spider)

當spider被關閉時,這個方法被調用

參數:spider (Spider 對象) – 被關閉的spider
from_crawler(clscrawler)

If present, this classmethod is called to create a pipeline instance from a Crawler. It must return a new instance of the pipeline. Crawler object provides access to all Scrapy core components like settings and signals; it is a way for pipeline to access them and hook its functionality into Scrapy.

參數:crawler (Crawler object) – crawler that uses this pipeline

Item pipeline 樣例

驗證價格,同時丟棄沒有價格的item

讓我們來看一下以下這個假設的pipeline,它爲那些不含稅(price_excludes_vat 屬性)的item調整了 price 屬性,同時丟棄了那些沒有價格的item:

from scrapy.exceptions import DropItem

class PricePipeline(object):

    vat_factor = 1.15

    def process_item(self, item, spider):
        if item['price']:
            if item['price_excludes_vat']:
                item['price'] = item['price'] * self.vat_factor
            return item
        else:
            raise DropItem("Missing price in %s" % item)

將item寫入JSON文件

以下pipeline將所有(從所有spider中)爬取到的item,存儲到一個獨立地 items.jl 文件,每行包含一個序列化爲JSON格式的item:

import json

class JsonWriterPipeline(object):

    def __init__(self):
        self.file = open('items.jl', 'wb')

    def process_item(self, item, spider):
        line = json.dumps(dict(item)) + "\n"
        self.file.write(line)
        return item

註解

JsonWriterPipeline的目的只是爲了介紹怎樣編寫item pipeline,如果你想要將所有爬取的item都保存到同一個JSON文件, 你需要使用 Feed exports 。

Write items to MongoDB

In this example we’ll write items to MongoDB using pymongo. MongoDB address and database name are specified in Scrapy settings; MongoDB collection is named after item class.

The main point of this example is to show how to use from_crawler() method and how to clean up the resources properly.

註解

Previous example (JsonWriterPipeline) doesn’t clean up resources properly. Fixing it is left as an exercise for the reader.

import pymongo

class MongoPipeline(object):

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        collection_name = item.__class__.__name__
        self.db[collection_name].insert(dict(item))
        return item

去重

一個用於去重的過濾器,丟棄那些已經被處理過的item。讓我們假設我們的item有一個唯一的id,但是我們spider返回的多個item中包含有相同的id:

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):

    def __init__(self):
        self.ids_seen = set()

    def process_item(self, item, spider):
        if item['id'] in self.ids_seen:
            raise DropItem("Duplicate item found: %s" % item)
        else:
            self.ids_seen.add(item['id'])
            return item

啓用一個Item Pipeline組件

爲了啓用一個Item Pipeline組件,你必須將它的類添加到 ITEM_PIPELINES 配置,就像下面這個例子:

ITEM_PIPELINES = {
    'myproject.pipelines.PricePipeline': 300,
    'myproject.pipelines.JsonWriterPipeline': 800,
}

分配給每個類的整型值,確定了他們運行的順序,item按數字從低到高的順序,通過pipeline,通常將這些數字定義在0-1000範圍內。




下載器中間件(Downloader Middleware)

下載器中間件是介於Scrapy的request/response處理的鉤子框架。 是用於全局修改Scrapy request和response的一個輕量、底層的系統。


編寫您自己的下載器中間件

編寫下載器中間件十分簡單。每個中間件組件是一個定義了以下一個或多個方法的Python類:

classscrapy.contrib.downloadermiddleware.DownloaderMiddleware
process_request(requestspider)

當每個request通過下載中間件時,該方法被調用。

process_request() 必須返回其中之一: 返回 None 、返回一個 Response 對象、返回一個 Request 對象或raise IgnoreRequest 。

如果其返回 None ,Scrapy將繼續處理該request,執行其他的中間件的相應方法,直到合適的下載器處理函數(download handler)被調用, 該request被執行(其response被下載)。

如果其返回 Response 對象,Scrapy將不會調用 任何 其他的 process_request() 或 process_exception() 方法,或相應地下載函數; 其將返回該response。 已安裝的中間件的 process_response() 方法則會在每個response返回時被調用。

如果其返回 Request 對象,Scrapy則停止調用 process_request方法並重新調度返回的request。當新返回的request被執行後, 相應地中間件鏈將會根據下載的response被調用。

如果其raise一個 IgnoreRequest 異常,則安裝的下載中間件的 process_exception() 方法會被調用。如果沒有任何一個方法處理該異常, 則request的errback(Request.errback)方法會被調用。如果沒有代碼處理拋出的異常, 則該異常被忽略且不記錄(不同於其他異常那樣)。

參數:
  • request (Request 對象) – 處理的request
  • spider (Spider 對象) – 該request對應的spider
process_response(requestresponsespider)

process_request() 必須返回以下之一: 返回一個 Response 對象、 返回一個 Request 對象或raise一個 IgnoreRequest 異常。

如果其返回一個 Response (可以與傳入的response相同,也可以是全新的對象), 該response會被在鏈中的其他中間件的 process_response() 方法處理。

如果其返回一個 Request 對象,則中間件鏈停止, 返回的request會被重新調度下載。處理類似於 process_request() 返回request所做的那樣。

如果其拋出一個 IgnoreRequest 異常,則調用request的errback(Request.errback)。 如果沒有代碼處理拋出的異常,則該異常被忽略且不記錄(不同於其他異常那樣)。

參數:
  • request (Request 對象) – response所對應的request
  • response (Response 對象) – 被處理的response
  • spider (Spider 對象) – response所對應的spider
process_exception(requestexceptionspider)

當下載處理器(download handler)或 process_request() (下載中間件)拋出異常(包括 IgnoreRequest 異常)時, Scrapy調用 process_exception() 。

process_exception() 應該返回以下之一: 返回 None 、 一個 Response 對象、或者一個 Request 對象。

如果其返回 None ,Scrapy將會繼續處理該異常,接着調用已安裝的其他中間件的process_exception() 方法,直到所有中間件都被調用完畢,則調用默認的異常處理。

如果其返回一個 Response 對象,則已安裝的中間件鏈的 process_response() 方法被調用。Scrapy將不會調用任何其他中間件的 process_exception() 方法。

如果其返回一個 Request 對象, 則返回的request將會被重新調用下載。這將停止中間件的process_exception() 方法執行,就如返回一個response的那樣。

參數:
  • request (是 Request 對象) – 產生異常的request
  • exception (Exception 對象) – 拋出的異常
  • spider (Spider 對象) – request對應的spider




參考:

http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/spiders.html


發佈了56 篇原創文章 · 獲贊 157 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章