我正在構建一個每日摘要工作流,從 HVAC 行業部落格中獲取內容。我的三個來源(HouseCall Pro、Jobber Academy、HVAC Informed)受到 Cloudflare 保護。當我的 HTTP Request 節點訪問這些 URL 時,它會收到 403「稍候…」Cloudflare 挑戰頁面,而不是實際內容。
這些網站也沒有 RSS 動態摘要,所以 RSS Feed Read 節點無法使用。
我嘗試過的方法:
問題:
-
在 n8n 中是否有辦法原生繞過 Cloudflare 機器人保護?
-
我應該為這些來源使用 Apify、ScrapingBee 還是 BrightData?
-
有沒有社群節點可以處理受 Cloudflare 保護的網站?
-
添加類似瀏覽器的標頭(User-Agent、Accept-Language 等)會有幫助嗎?
@Asim_Arman 我快速檢查了你的 3 個來源後有個好消息 — HouseCall Pro 其實根本不在 Cloudflare 上,如果你只是在 HTTP Request 節點加上真實的瀏覽器 User-Agent header,它的 /feed/ 就能正常運作。我用 Mozilla/5.0 UA 測試過 https://www.housecallpro.com/feed/,得到了有效的 RSS XML。你那邊的 403 幾乎肯定是因為 n8n 的 HTTP Request 節點預設使用通用 UA(或根本沒有),而該網站有基本的 UA 過濾。改用 RSS Feed Read 指向 Housecall Pro 並覆蓋 User-Agent 就搞定了,不需要爬蟲。
Jobber (getjobber.com) 真的在 Cloudflare 上 — responses 中有 cf-ray header — 而且 academy 區域沒有 RSS。HVAC Informed 也沒有公開 RSS。這兩個確實需要爬蟲服務。
關於在 n8n 中本地繞過 Cloudflare 的問題,根本沒有真正的方法。他們的 challenge 需要執行 JS 來解決 proof-of-work 加上行為指紋辨識,而 HTTP Request 節點只是一個沒有瀏覽器引擎的純 http client。2022-2023 年那些 cloudscraper 風格的程式庫都對當前 Cloudflare 失效了,所以少數包裝它們的社群節點也不行。
類似瀏覽器的 header 可以解決基本的 UA 過濾(HouseCall Pro 的 403 就是這個,諷刺吧),但對真正的 Cloudflare challenge 毫無辦法 — 他們會進行 TLS、JA3、request timing 和 JS 執行的指紋辨識,不只是 header。
關於 ScrapingBee vs Apify vs BrightData — 針對你的用例(低流量日報摘要,只有 2 個網站需要真正爬蟲),ScrapingBee 或 Firecrawl 最乾淨。粗略比較:
{
"scrapingbee": {"per_request": "~$0.001-0.005", "fit": "低中流量、簡單、快速"},
"scraperapi": {"per_request": "~$0.001-0.003", "fit": "與 scrapingbee 相當"},
"firecrawl": {"per_request": "~$0.001", "fit": "爬蟲轉 markdown、LLM 就緒輸出"},
"brightdata": {"per_request": "~$0.005-0.02", "fit": "最難的目標、昂貴但最可靠"},
"apify": {"per_request": "varies", "fit": "複雜的多頁流程"}
}
透過 HTTP Request 呼叫 ScrapingBee 看起來像:
{
"method": "GET",
"url": "https://app.scrapingbee.com/api/v1/",
"qs": {
"api_key": "<your_key>",
"url": "https://www.getjobber.com/academy/",
"render_js": "true",
"premium_proxy": "true"
}
}
最終狀態是一個 HouseCall Pro 的 RSS Feed Read,兩個 Jobber 和 HVAC Informed 的 ScrapingBee HTTP 呼叫,然後解析 → 摘要。遠比把全部 3 個當成 Cloudflare 保護的便宜,特別是只有一個真的在 Cloudflare 上。
我想補充一點,Apify 有**智能文章提取器**,它可以(通常)自動檢測頁面上的文章連結,然後逐一抓取。
它還會模擬瀏覽器,因此可以繞過 Cloudflare。它也是一個僅使用量計費的 actor,所以每天對三個部落格的一次呼叫可能符合他們的免費額度。
對於更通用的(非文章)情況,你可以使用他們的**網站內容爬蟲**取得結構化內容,然後通過小型 AI 模型(如 haiku 或 gpt minis)進行處理。
對於像 Jobber 和 HVAC Informed 這樣較複雜的網站,如果你使用 n8n 自託管 Docker 實例 並且想避免付費方案,一個有趣的替代方案是在獨立的 Docker 容器中運行 SeleniumBase Undetected ChromeDriver (UC) 模式。SeleniumBase 的 UC (Undetected ChromeDriver) 模式目前是繞過 Cloudflare 保護最強大的開源工具之一。
然後,你可以配置此容器來執行你的爬蟲腳本,然後直接從 n8n 通過 Execute Command 節點觸發它們(或者如果你圍繞 n8n AI Agent 結構化了工作流,可以使用 自訂工具)。
有一件事還沒人提到:在使用抓取服務之前,檢查一下這些網站是否在非標準路徑後面隱藏著網站地圖或 Atom feed。
在花錢之前先試試這些:
- /sitemap.xml
- /sitemap_index.xml
- /feed/
- /atom.xml
- /blog/feed/ 或 /blog/rss/
許多由 WordPress/HubSpot 驅動的部落格(HouseCall Pro 和 Jobber 都使用)已啟用 feed,但未在 中連結。如果你點擊正確的 URL,RSS Read 節點就能正常運作。
對於那些確實沒有 feed 且 Cloudflare 阻止你的網站:
最便宜的生產級方案:透過 HTTP Request 節點使用 Firecrawl 或 ScrapingBee。
HTTP Request → POST https://api.firecrawl.dev/v1/scrape
標頭:Authorization: Bearer <your_key>
本體:{ “url”: “https://target.com/blog”, “formats”: [“markdown”] }
返回乾淨的 markdown — 無需 HTML 解析。在 Schedule Trigger 上每天執行一次。在 3 個網站 × 1 個請求/天的情況下,你將輕鬆保持在任何免費層級以內。
生產硬化(大多數教學都跳過的部分):
- 在 HTTP Request 節點上新增帶退避的重試 — 抓取 API 有時會給你 429
- 將最後抓取的 URL 雜湊儲存在 Postgres 表中,這樣你明天就不會重新處理同一篇文章(冪等性)
- 新增 Error Trigger 工作流 — 如果你的摘要在星期一無聲地失敗,你想在星期五之前知道
「直接抓取」的部分很簡單。讓它每天可靠地執行 6 個月而不會出現無聲故障,這是大多數每日摘要工作流中斷的地方。
嘿,真的很感謝詳細的說明,achamm. 這非常有幫助,我最後也跟著大部分的做法。
我確實完全按照的做法:
-
HouseCall Pro - 確認不是 Cloudflare,在 HTTP Request 節點添加了 User-Agent 標頭,指向 /feed/,通過 XML 節點解析,然後是 Code 節點。現在運作完美。
-
Jobber 和 HVAC Informed - 使用了 Firecrawl 而不是 ScrapingBee(和你提到的同價位層級,只是更喜歡 markdown 輸出來傳給 Claude)。
兩步驟方法:先爬取列表頁面來提取文章 URL,然後通過第二個 HTTP Request 個別爬取每篇文章。
我們在你的建議之外還添加的:
- 在每個 Firecrawl 請求之後添加一個 Parse Code 節點,用來將所有來源標準化為相同的字段(title、content、link、feedSource),然後再合併
非常感謝,很高興在社區裡有你
如果你有機會自行託管某個工具(例如透過 Docker 在與 n8n 相同的伺服器上),並且想要避免付費平台的配額限制(如 Apify、ScrapingBee 或 Firecrawl),你應該考慮使用 FlareSolverr。
這是一個開源代理,在背景啟動一個無頭瀏覽器來解決 Cloudflare 挑戰,並將頁面的原始 HTML 返回給你。你只需要在 n8n 中使用簡單的 HTTP Request 節點在本地查詢它。
不過 FlareSolverr 會返回原始 HTML 程式碼。因此你需要在之後添加一個 HTML 節點或 Code 節點,自己解析並將資料結構化為 JSON。
一個簡單的例子:
{
“nodes”: [
{
“parameters”: {
“jsCode”: “const html = $json.solution.response;\n\nfunction getText(html, startMarker, endMarker) {\n const start = html.indexOf(startMarker);\n if (start === -1) return ‘’;\n const end = html.indexOf(endMarker, start);\n if (end === -1) return ‘’;\n return html.substring(start + startMarker.length, end).trim();\n}\n\nfunction getMetaContent(html, name) {\n const regex = new RegExp(<meta name=\"${name}\" content=\"([^\"]+)\", ‘i’);\n const match = html.match(regex);\n return match ? match[1] : ‘’;\n}\n\nlet title = getText(html, ‘
’, ‘’);\n// 清理標題中的標籤\ntitle = title.replace(/<a[^>]>([^<])<\/a>/, ‘$1’).trim();\n\nconst description = getMetaContent(html, ‘description’);\n\nlet content = getText(html, ‘’, ‘
’);\n// 清理內容\ncontent = content\n .replace(/<script[^>]
>[\s\S]?<\/script>/gi, ‘’)\n .replace(/<style[^>]
>[\s\S]?<\/style>/gi, ‘’)\n .replace(/<[^>]+>/g, ’ ')\n .replace(/\s+/g, ’ ')\n .trim();\n\nreturn [{\n title: title,\n description: description,\n content: content\n}];”
},
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [
-912,
4480
],
“id”: “a6275095-c4ed-427f-a791-620bcd83710b”,
“name”: “Extract from HTML”
},
{
“parameters”: {
“method”: “POST”,
“url”: “=
http://flaresolverr:8191/v1”,
“sendHeaders”: true,
“headerParameters”: {
“parameters”: [
{
“name”: “Content-Type”,
“value”: “application/json”
}
]
},
“sendBody”: true,
“bodyParameters”: {
“parameters”: [
{
“name”: “cmd”,
“value”: “request.get”
},
{
“name”: “url”,
“value”: “={{ $json.url }}”
},
{
“name”: “maxTimeout”,
“value”: 90000
}
]
},
“options”: {
“timeout”: 120000
}
},
“type”: “n8n-nodes-base.httpRequest”,
“typeVersion”: 4.4,
“position”: [
-1120,
4480
],
“id”: “4c884f8a-09c7-4c13-b7e8-ac38098b00f6”,
“name”: “FlareSolverr”,
“retryOnFail”: true
}
],
“connections”: {
“Extract from HTML”: {
“main”: [
]
},
“FlareSolverr”: {
“main”: [
[
{
“node”: “Extract from HTML”,
“type”: “main”,
“index”: 0
}
]
]
}
},
“pinData”: {},
“meta”: {
“templateCredsSetupCompleted”: true,
“instanceId”: “03428eda2f2a76af33393f7fa59419dea9e2e74e0d630500b36df8cf5b8e161d”
}
}