# [Scrapy](https://scrapy.org/) > Request 跟 BeautifulSoup 不香嗎?為什麼要多學一個 Framework? 當處理更複雜的抓取任務、大規模資料收集,或者需要非同步請求和中間件支援等高級功能時,Scrapy 是一個絕佳的選擇。 [介紹簡報](https://gamma.app/public/Scrapy--gcaz7e50zoyneeb?mode=doc) [完整範例](https://github.com/HannahLo/quotes-scraper) ## 特色 1. **結構化的框架**:Scrapy提供了一個清晰的結構和生命週期,使您能夠更輕鬆地組織和管理Web抓取項目。 2. **強大的選擇器**:Scrapy支持XPath和CSS選擇器,使您能夠輕鬆地從HTML頁面中提取所需的數據。 3. **異步請求**:Scrapy允許進行異步請求,從而提高了抓取速度,尤其是在處理大量頁面時。 4. **內置HTTP請求和錯誤處理**:Scrapy具有內置的HTTP請求和錯誤處理機制,可以自動處理請求和錯誤狀態代碼,並允許您定義自定義錯誤處理邏輯。 5. **數據管道**:Scrapy允許您定義數據管道,以方便地對抓取的數據進行處理和存儲,支持多種輸出格式,如JSON、CSV和數據庫。 6. **中間件支持**:Scrapy允許用戶編寫自定義中間件來處理請求和響應,例如更改用戶代理、IP輪換和處理cookies。 7. **多網站抓取**:Scrapy支持同時抓取多個網站,並且可以定義多個Spider來處理不同網站的數據。 8. **自動限速**:Scrapy支持自動限速設置,以遵守網站的使用規定,避免被封禁或限制訪問。 9. **可擴展性**:Scrapy是高度可擴展的,允許您通過中間件(Middleware)和擴展來自定義其功能,以適應各種不同的抓取需求。 10. **日誌記錄**:Scrapy具有強大的日誌記錄功能,有助於跟蹤和排查抓取期間的問題。 11. **支持分布式抓取**:Scrapy可以與其他庫(如Celery、Scrapyd)結合使用,以支持分布式抓取,進一步提高抓取速度和效率。 12. **大型社區和文件**:Scrapy擁有龐大的用戶社區和豐富的文件資源,因此您可以輕鬆獲得支持和解決問題。 總之,Scrapy是一個功能強大且高度靈活的Web抓取框架,適用於從複雜網站中提取數據、大規模數據收集以及需要高級功能的抓取任務。它提供了一整套工具和機制,使Web抓取變得更加高效和可維護。 ## [架構](https://docs.scrapy.org/en/latest/topics/architecture.html) ![](https://hackmd.io/_uploads/BkavlBVb6.png) 1. **專案(Project)**: - 專案是整個網頁爬取工作的基礎。您可以使用 `scrapy startproject` 命令建立一個新專案。 - 專案目錄包括 Scrapy 配置文件、Spider 定義、數據管道、中介軟體和其他相關元件。 2. **爬蟲(Spider)**: - 爬蟲是 Scrapy 中的核心元件,用於定義如何爬取特定網站的資料。 - 每個爬蟲是一個 Python 類別,必須包括網站的起始 URL、如何跟隨連結以獲取更多頁面,以及如何從頁面中提取所需資料的規則。 3. **項目(Item)**: - 項目代表爬取到的結構化資料,類似於字典或資料模型,用於存儲網頁中提取的信息。 - 您需要為每個爬蟲定義一個或多個項目類別,以指定爬取的資料結構。 4. **選擇器(Selector)**: - 選擇器用於從下載的頁面中提取資料。Scrapy 支援 XPath 和 CSS 選擇器來定位和提取所需的元素。 5. **管道(Pipeline)**: - 管道是資料處理和存儲的通道。Scrapy 提供了多個內建的管道,用於處理爬取到的資料,例如存儲到資料庫、寫入檔案或進行其他處理。 - 您可以定義自己的管道以適應特定需求。 6. **中介軟體(Middleware)**: - 中介軟體是 Scrapy 中的中介元件,可用於處理請求和響應。例如,您可以編寫中介軟體來更改用戶代理、實現代理池、處理 Cookies 等。 - Scrapy 具有內建的中介軟體,同時也允許用戶編寫自定義中介軟體。 - Download Middleware Scrapy Spider 發出 Requests 到 Scrapy Engine,然後 Scrapy Engine 使用 Downloader Middleware 處理這些請求,它可以修改請求、添加代理、設置User-Agent等。Requests 經過 Downloader Middleware 處理後發送給 Downloader,Downloader 負責下載網頁並返回 Responses。 - Spider Middleware Responses 返回給 Scrapy Engine,然後 Scrapy Engine 使用 Spider Middleware 處理 Responses,Spider Middleware 也可以修改 Responses、進行數據處理等。最後,Responses 返回給 Scrapy Spider。 7. **下載器(Downloader)**: - 下載器負責發送 HTTP 請求並接收網頁的響應。Scrapy 會自動處理請求和響應的下載過程,但您可以透過自訂的下載器中介軟體來進行更高級的控制。 8. **設置(Settings)**: - Scrapy 的設置檔案包含了各種配置選項,例如請求標頭、延遲、併發數等。這些選項可以在專案的 settings.py 檔案中進行配置。 9. **Scrapy Shell**: - Scrapy 提供了一個互動式的 Shell 工具,用於快速測試和調試 XPath 和 CSS 選擇器,以及檢視頁面內容和提取的資料。 11. **Scheduler**: - 管理待抓取的請求隊列,確保請求按照合適的順序被下載。允許限制並發請求的數量,以避免對目標網站造成過大的負載。 - 功能包括請求的調度、防止重覆請求、重新調度失敗請求、以及實現請求的深度優先或廣度優先爬取策略,從而幫助開發者管理抓取流程。 總體而言,Scrapy 的結構化設計使專案更有組織性、可維護性和可擴展性。您可以透過建立多個爬蟲來爬取不同網站的資料,並使用各種中介軟體和管道來處理和存儲爬取到的信息,使其成為一個強大的網頁爬取框架。 ## Scrapy 基本抓取流程 UR<sup>2</sup>IM ```mermaid flowchart TD A[URL] B[Request] C[Response] D[Item] E[More URL] F((Database)) A --> B --> C --> D --> E E --> B D --> |pipeline| F ``` ## 使用時機 需要在多個網站抓取大量資料時就適合使用 只處理一個網頁的話,除非資料量很大,不然使用 Request + Beautiful Soup 就夠了 ## Scrapy 實作 #### 1. Scrapy基礎 ##### 1.1 安裝Scrapy Python: 3.10.6 Scrapy: 2.11.0 在進行任何Scrapy專案之前,我們需要確保Scrapy已經安裝。請按照以下步驟進行安裝: ```bash pip install scrapy ``` 確保安裝過程沒有出現錯誤。 ##### 1.2 創建新的Scrapy專案 要創建一個新的Scrapy專案,請遵循以下步驟: ```bash scrapy startproject quotes_scraper ``` 這將創建一個名為`quotes_scraper`的新Scrapy專案,並在該目錄下生成相關的文件和目錄。 ##### 1.3 Scrapy的基本結構 Scrapy專案的基本結構如下: ``` quotes_scraper/ scrapy.cfg quotes_scraper/ __init__.py items.py middlewares.py pipelines.py settings.py spiders/ __init__.py ``` - `quotes_scraper/`:專案目錄。 - `scrapy.cfg`:Scrapy的配置文件。 - `quotes_scraper/`:專案的Python模塊。 - `__init__.py`:模塊初始化文件。 - `items.py`:用於定義專案項目(Item)的文件,用於存儲爬取的數據。 - `middlewares.py`:自定義中介軟體的地方。 - `pipelines.py`:定義數據管道(Pipeline)的地方,用於處理爬取到的數據。 - `settings.py`:Scrapy的設定選項,包括User-Agent、延遲、併發等。 - `spiders/`:包含爬蟲(Spider)的目錄。 - `__init__.py`:初始化文件。 這個基本結構有助於組織您的Scrapy專案,讓您能夠輕鬆地添加新的爬蟲和自定義功能。 ##### 1.4 Scrapy專案的目錄結構 確保您的Scrapy專案的目錄結構如上所示。一旦專案創建完成,您就可以開始創建新的Spider來爬取網頁數據。 這些是Scrapy基礎的步驟,它們為您提供了建立Scrapy專案的基本知識和工具。現在,我們可以繼續創建一個新的Spider,以實際爬取 http://quotes.toscrape.com/ 網站的數據。 #### 2 創建新的Scrapy Spider 一個Scrapy Spider是用於定義如何爬取特定網站的規則和操作的Python類別。以下是創建一個新的Spider的詳細步驟: 1. 打開終端,確保您的工作目錄位於Scrapy專案的根目錄,即 `quotes_scraper/` 目錄下。 2. 在終端中運行以下命令以創建一個新的Spider。在這個示例中,我們將使用名稱為 `quotes_spider` 的Spider,您可以自行選擇一個名稱。 ```bash scrapy genspider quotes_spider quotes.toscrape.com ``` - `quotes_spider`:新Spider的名稱。 - `quotes.toscrape.com`:您希望爬取的網站的域名。 3. Scrapy將自動創建一個新的Spider文件並放置在 `quotes_scraper/spiders/` 目錄下。這個文件的名稱將是 `quotes_spider.py`(或您指定的名稱)。 4. 打開新創建的Spider文件,您將看到一個Python類別 `QuotesSpiderSpider`,其中包括了一個名為 `start_urls` 的屬性,這是Spider的起始URL。您可以根據需要修改這個URL。 5. 在Spider中,您可以定義如何跟蹤連結、提取數據以及處理響應的規則。例如,您可以使用Scrapy的選擇器來提取所需的數據,然後將其存儲在爬取的項目(Item)中。 下面是一個簡單的示例,展示如何在Spider中提取網站上的名言: ```python import scrapy class QuotesSpider(scrapy.Spider): name = "quotes_spider" start_urls = ["http://quotes.toscrape.com/"] def parse(self, response): for quote in response.css("div.quote"): yield { "text": quote.css("span.text::text").get(), "author": quote.css("span small::text").get(), } ``` 這個示例中,Spider設置了起始URL為 http://quotes.toscrape.com/,然後使用CSS選擇器從網頁中提取了名言的文本和作者。提取的數據將以字典的形式返回,並存儲在項目中。 當使用 `quote.css("span.text::text").get()` 這樣的Scrapy選擇器表達式時,它的作用如下: - `quote.css("span.text")`:這一部分表示使用CSS選擇器 `span.text` 選取HTML文檔中所有標記為 `<span class="text">` 的元素。這通常用於選擇包含名言文本的HTML元素。 - `::text`:這是Scrapy的擴展選擇器語法,用於提取所選元素的文本內容。換句話說,它將獲取選中元素的內部文本。 - `.get()`:這是Scrapy選擇器的方法,用於提取所選元素的數據。在這個上下文中,`get()` 將返回第一個匹配的元素的文本內容。 總之,當您使用 `quote.css("span.text::text").get()` 時,它將提取所爬取網頁中所有 `<span class="text">` 元素的文本內容,並返回第一個匹配元素的文本。這通常用於爬取名言網站的名言文本。如果網頁包含多個名言,此選擇器將返回第一個名言的文本內容。 - `quote.css("span small")`:這一部分表示使用CSS選擇器 span small 選取HTML文檔中所有標記為 `<span>` 元素下的 `<small>` 元素。 運行腳本可以在 Log 中確認抓取到的內容 `scrapy crawl quotes_spider` 這就是創建一個新的Scrapy Spider 的詳細步驟。請根據您的需求自訂Spider以達到您想要爬取的數據的目標。接下來,我們將設置項目(Item)以存儲這些數據。 ##### 使用 Scrapy Sheel 測試 Selector 當您想要使用 `https://quotes.toscrape.com` 這個網站來進行測試時,您可以使用Scrapy Shell來模擬請求和驗證您的選擇器。以下是如何使用Scrapy Shell來驗證選擇器的步驟: 1. 打開終端,確保您的工作目錄位於Scrapy專案的根目錄下。 2. 啟動Scrapy Shell,可以使用以下命令: ```bash scrapy shell "https://quotes.toscrape.com" ``` 在這個命令中,我們提供了要訪問的URL,Scrapy Shell將載入該URL的內容。 3. 在Scrapy Shell中,您可以使用選擇器來獲取網頁上的數據。例如,要獲取第一個名言的文本,可以運行以下命令: ```python response.css("span.text::text").get() ``` 這將返回第一個名言的文本內容。 4. 您可以測試其他選擇器,以查看它們是否返回所需的數據。例如,要獲取第一個名言的作者,可以運行: ```python response.css("span small::text").get() ``` 這將返回第一個名言的作者信息。 5. 在Scrapy Shell中,您可以模擬網頁的瀏覽,查找元素,並測試選擇器,以確保它們返回正確的數據。 6. 當您完成測試後,您可以退出Scrapy Shell,然後繼續編輯和優化您的Scrapy Spider。 要退出 Scrapy Shell(或任何 Python Shell)在 macOS 上,您可以使用以下方法之一: 1. **使用快捷键:** 在 Scrapy Shell 或 Python Shell 中,按下 `Ctrl + D`(Control鍵和D鍵一起按),這將退出Shell。 2. **使用 `exit()` 函数:** 在 Scrapy Shell 或 Python Shell 中,您可以運行 `exit()` 函數來退出。輸入以下命令: ```python exit() ``` 3. **使用 `quit()` 函數:** 同樣,您也可以使用 `quit()` 函數來退出 Shell。輸入以下命令: ```python quit() ``` 使用Scrapy Shell與實際網頁交互,測試和驗證選擇器是一種非常有用的工具,有助於確保您的Scrapy項目正確處理目標網站的數據。這特別適用於開發和調試 Scrapy Spider。 #### 3. 定義Scrapy項目(Item) 項目(Item)是用於存儲從網頁中提取的數據的Python類別。在我們的示例中,我們想要存儲名言的文本和作者,因此我們需要定義一個項目。 1. 在Scrapy專案的根目錄下,打開 `items.py` 文件。 2. 在 `items.py` 文件中,定義一個Python類別,這個類別將代表我們的項目。在我們的示例中,我們可以定義一個名為 `QuoteItem` 的項目: ```python import scrapy class QuoteItem(scrapy.Item): text = scrapy.Field() author = scrapy.Field() ``` 這裡我們使用Scrapy的 `Field` 類型來定義項目中的字段。在這個項目中,我們有 `text` 和 `author` 兩個字段,分別用於存儲名言文本和作者。 使用 scrapy.Field 來定義Spider的Item類,有助於Scrapy識別每個數據字段的類型。這樣Scrapy可以更好地處理不同類型的數據,例如文本、數字、日期等。 3. 保存 `items.py` 文件。 這樣,我們已經成功定義了一個Scrapy項目,以便存儲我們從網頁中提取的數據。下一步,我們將在Spider中使用這個項目來存儲爬取的信息。 #### 4. 將數據存入 Scrapy 項目 (Item) 1. 打開您的Spider文件,即 `quotes_spider.py`。 2. 在Spider文件中,導入Scrapy的項目模塊: ```python import scrapy from quotes_scraper.items import QuotesScraperItem ``` 確保您已經將 `QuotesScraperItem` 引入,這是我們在上一步中定義的項目。 3. 在Spider類別中的 `parse` 方法中,使用Scrapy的選擇器來提取數據。在我們的示例中,我們使用CSS選擇器來提取名言的文本和作者,並存儲在我們的項目中: ```python def parse(self, response): for quote in response.css("div.quote"): item = QuotesScraperItem() item["text"] = quote.css("span.text::text").get() item["author"] = quote.css("span small::text").get() yield item ``` 這個代碼片段中,我們遍歷網頁上的每個名言元素,然後使用CSS選擇器從每個元素中提取文本和作者信息,最後將它們存儲在我們的 `QuotesScraperItem` 項目中。使用 `yield` 將項目返回,以便Scrapy將它們處理和存儲。 4. 確保您的Spider文件保存。 #### 5. 設置Pipeline Scrapy使用Pipeline來處理爬取到的數據,您可以設置Pipeline來執行各種任務,例如將數據存儲到文件中。在這個示例中,我們將設置一個Pipeline,將爬取到的名言存儲到JSON文件中。 1. 打開Scrapy專案的 `settings.py` 文件。 2. 在 `settings.py` 文件中,找到 `ITEM_PIPELINES` 配置選項,並將其設置為一個字典,其中包括您的Pipeline: ```python ITEM_PIPELINES = { 'quotes_scraper.pipelines.QuotesToJsonPipeline': 300, } ``` 這個示例中,我們設置了一個名為 `QuotesToJsonPipeline` 的Pipeline,並指定了其處理順序(300)。 處理順序數值指定了Scrapy在處理請求隊列時使用的優先級順序,數字越低表示優先級越高。 3. 確保您的Scrapy專案中已經創建了一個名為 `pipelines.py` 的文件。如果沒有,請創建它。 4. 在 `pipelines.py` 文件中,定義 `QuotesToJsonPipeline`: ```python import json class QuotesToJsonPipeline: def __init__(self): self.file = open('quotes.json', 'w') def process_item(self, item, spider): line = json.dumps(dict(item)) + "\n" self.file.write(line) return item def close_spider(self, spider): self.file.close() ``` 這個Pipeline將爬取到的項目轉換為JSON格式並寫入 `quotes.json` 文件中。 5. 保存 `settings.py` 和 `pipelines.py` 文件。 當您運行Spider時,它將使用我們的Pipeline將爬取到的名言存儲到JSON文件中。 #### 6. 執行Spider 我們已經創建了Scrapy Spider,設置了項目(Item),並配置了Pipeline,接下來我們將執行Spider以開始爬取網站的數據。 1. 打開終端,確保您的工作目錄位於Scrapy專案的根目錄,即 `quotes_scraper/` 目錄下。 2. 使用以下命令執行您的Spider: ```bash scrapy crawl quotes_spider ``` - `quotes_spider`:這是您在Spider文件中定義的Spider的名稱。 3. Scrapy將開始執行Spider,並開始爬取 http://quotes.toscrape.com/ 網站的數據。您將看到Scrapy的日誌輸出,顯示每個頁面的爬取進度。 4. 當Spider完成爬取時,它將使用我們的Pipeline將數據存儲到 `quotes.json` 文件中(或您在Pipeline中設置的目標文件)。 5. 檢查您的專案目錄,您將在根目錄下找到 `quotes.json` 文件,其中包含爬取到的名言數據。 您已經成功執行了Scrapy Spider,爬取了 http://quotes.toscrape.com/ 網站的數據並將其存儲為JSON文件。這是一個簡單的示例,演示了如何使用Scrapy來爬取和處理網頁數據。您可以根據需要擴展這個示例,爬取更多數據,設置更多Pipeline,或者進行進一步的數據處理。 #### 7. 實現橫向爬行 為了創建一個新的Spider來實現對`quotes.toscrape.com`的橫向抓取,同時限制最多 3 頁和最多 20 個Item。 1. **創建新的Spider:** 首先,在Scrapy項目目錄下,使用以下命令創建一個新的Spider。 ```bash scrapy genspider quotes_spider_lateral quotes.toscrape.com ``` 這將創建一個名為`quotes_spider_lateral`的新Spider,並將其配置在`quotes.toscrape.com`網站上。 2. **編輯Spider文件:** 打開新創建的Spider文件(位於`quotes_scraper/spiders/`目錄下),並編輯Spider類。在Spider類中,您需要定義如何抓取網頁、提取數據以及設置橫向抓取的規則。以下是示例代碼: ```python import scrapy from quotes_scraper.items import QuotesScraperItem # 改爲繼承 CrawlSpider class QuotesSpiderLateralSpider(scrapy.Spider): name = "quotes_spider_lateral" allowed_domains = ["quotes.toscrape.com"] start_urls = ["https://quotes.toscrape.com"] def parse(self, response): for quote in response.css("div.quote"): item = QuotesScraperItem() item["text"] = quote.css("span.text::text").get() item["author"] = quote.css("span small::text").get() yield item # 爬取下一頁 next_page = response.css("li.next a::attr(href)").get() if next_page: next_page_url = response.urljoin(next_page) yield scrapy.Request(next_page_url, callback=self.parse) ``` 在這個示例中,我們定義了一個新的Spider,`QuotesSpiderLateral`,並在`parse`方法中處理數據提取和下一頁鏈接的抓取。設置了規則來提取下一頁鏈接,以實現橫向抓取。 3. **設置限制:** 為了實現最多 3 頁和最多 20 個Item的限制,您可以在項目的`settings.py`文件中添加以下設置: ```python CLOSESPIDER_PAGECOUNT = 3 CLOSESPIDER_ITEMCOUNT = 20 ``` 這將在Spider運行時強制執行這些限制。 4. **運行Spider:** 最後,運行Spider以啟動橫向抓取: ```bash scrapy crawl quotes_spider_lateral ``` Spider將開始橫向抓取`quotes.toscrape.com`,並在達到指定的頁面數或Item數限制時停止。 ## 可以繼續玩的東西 * Spider 繼承 CrawlSpider 可以指定 Rule,簡單的同時進行縱橫的資料爬取。 * 可以部署爬蟲上 Zyte Scrapy Cloud 管理執行,服務會定期替換 IP,解決部分網站的阻擋策略,目前無法使用 google 帳號註冊。 * 使用 Twisted 處理異步提升爬取效能。 * 使用 Scrapyd 將 Scrapy 項目部署和管理在遠程服務器上,提供了便捷的方式來控制和監控爬蟲的運行,以及在分布式環境中擴展 Scrapy 的能力。