# [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)

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 的能力。