Blazor WASM SEO - 使用預渲染解決 SPA 網頁爬蟲無法讀取問題 (Prerender.io & Cloudflare Workers 操作流程教學)
Blazor WASM 前置作業(使用 Vue、React等前端框架可跳過)
🔗基本上要讓搜尋引擎正確取得資料,就得在每個頁面的<head>
新增 Open Graph。
詳細資訊可參考 The Open Graph protocol,這裡直接提供 Blazor WASM 的實作方法。為了方便取用,可以把整個 Open Graph,做成一個物件。
在 Client > Shared 新增一個 Razor component,代碼如下(可根據需求自行更改):
C#<PageTitle>@title</PageTitle>
<HeadContent>
<meta property="og:title" content="@title">
<meta property="og:type" content="website">
<meta property="og:url" content="@url">
<meta property="og:description" content="@description">
<meta property="og:image" content="@image">
<meta property="og:image:alt" content="Shop">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@your twitter">
<meta name="author" content="alvin">
<meta name="description" content="@description">
<meta name="locale" content="zh-tw">
<meta name="scope" content="Accessories">
</HeadContent>
@code {
[Parameter] public string title { get; set; } = string.Empty;
[Parameter] public string url { get; set; } = string.Empty;
[Parameter] public string description { get; set; } = string.Empty;
[Parameter] public string image { get; set; } = string.Empty;
}
建置完成後,只要在想要被搜尋到的頁面添加此物件就可以了(參數依據不同頁面而改變)。
C#<C_SEO title="@pageTitle" description="@product.Description" image="@product.ImageUrl" url="@NavigationManager.Uri" />
之後在頁面上檢視原始碼就能在<head>
看到自定義的 Open Graph。原本以為這樣就解決了,但如果透過瀏覽器搜尋,或是藉由社群媒體分享連結,卻會發現爬蟲竟然讀取不到自定義的 Open Graph !?
問題
🔗利用 Vue、React 等前端框架建立的 SPA(Single Page Application,單頁應用程式)網站,與傳統靜態網站相比,它使用 JavaScript 動態渲染內容,這讓使用者在進入網站後,不需要每次重新加載整個頁面,而是只在頁面切換時更新所需的資料和畫面。這種機制不僅提升了效能,還允許使用者在頁面之間更加流暢地互動,因為頁面切換更快速、更平滑。單頁應用程式透過在客戶端動態更新內容,有效地優化了使用者體驗。
但這種方法在瀏覽器搜尋時因爬蟲並不會等到動態渲染內容出現,而導致無法顯示該網站實際的內容。若是使用 Blazor WASM 建立網站,不管任何畫面在搜尋引擎上看到的都是 index.html 的內容。如下:
Html<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
解決方法
🔗若要解決此問題,讓爬蟲可以讀取到正確的資訊,就得使用預渲染技術,主要就是藉由預先儲存每個畫面的資訊,當爬蟲機器人在爬取該網站時,直接顯示即可。
而預渲染可以使用 Prerender.io 所提供的服務,預先快取網站的實際內容,再藉由 Middleware 去判斷讀取網站的是一般使用者還是爬蟲機器人。Middleware 則是使用 Cloudflare Workers。
流程圖:
Cloudflare Workers
🔗Cloudflare Workers 是由 Cloudflare 提供的服務,允許開發人員在 Cloudflare 的全球分佈式邊緣網路上執行和部署 JavaScript 或 WebAssembly 代碼。
而我們就可以藉由 Cloudflare Workers 實作一個 Middleware 判斷發出的 request 是爬蟲機器人還是一般使用者。
若要使用 Cloudflare Workers,得先讓網站域名讓 Cloudflare 託管,目前專案原先是使用 Godaddy 購買網領域名,需先前往 Cloudflare 新增網站,方案可以選擇免費的,若本身網站已在 Godaddy 有 DNS 紀錄即會自動入,新增成功後就能取得自訂的名稱伺服器 URL,再前往 Godaddy DNS 的名稱伺服器修改即可。
佈署 Prerender worker 在 Cloudflare
🔗-
從首頁點選左側功能列(Workers & Pages)。
-
建立 Worker 並發佈,第一次建立官網應該會提供 Hello world 的範例,可以不管它先直接建立,之後可以再修改裡面的內容。
-
接下來就可以直接複製貼上 Prerender.io 提供 Cloudflare 當 Middleware 的代碼,按下
儲存和佈屬
就完成了。 (Prerender.io 在連接的方式選擇 Cloudflare 可查看此代碼)
JS// User agents handled by Prerender
const BOT_AGENTS = [
"googlebot",
"yahoo! slurp",
"bingbot",
"yandex",
"baiduspider",
"facebookexternalhit",
"twitterbot",
"rogerbot",
"linkedinbot",
"embedly",
"quora link preview",
"showyoubot",
"outbrain",
"pinterest/0.",
"developers.google.com/+/web/snippet",
"slackbot",
"vkshare",
"w3c_validator",
"redditbot",
"applebot",
"whatsapp",
"flipboard",
"tumblr",
"bitlybot",
"skypeuripreview",
"nuzzel",
"discordbot",
"google page speed",
"qwantify",
"pinterestbot",
"bitrix link preview",
"xing-contenttabreceiver",
"chrome-lighthouse",
"telegrambot",
"integration-test", // Integration testing
"google-inspectiontool"
];
// These are the extensions that the worker will skip prerendering
// even if any other conditions pass.
const IGNORE_EXTENSIONS = [
".js",
".css",
".xml",
".less",
".png",
".jpg",
".jpeg",
".gif",
".pdf",
".doc",
".txt",
".ico",
".rss",
".zip",
".mp3",
".rar",
".exe",
".wmv",
".doc",
".avi",
".ppt",
".mpg",
".mpeg",
".tif",
".wav",
".mov",
".psd",
".ai",
".xls",
".mp4",
".m4a",
".swf",
".dat",
".dmg",
".iso",
".flv",
".m4v",
".torrent",
".woff",
".ttf",
".svg",
".webmanifest",
];
export default {
/**
* Hooks into the request, and changes origin if needed
*/
async fetch(request, env) {
return await handleRequest(request, env).catch(
(err) => new Response(err.stack, { status: 500 })
);
},
};
/**
* @param {Request} request
* @param {any} env
* @returns {Promise<Response>}
*/
async function handleRequest(request, env) {
const url = new URL(request.url);
const userAgent = request.headers.get("User-Agent")?.toLowerCase() || "";
const isPrerender = request.headers.get("X-Prerender");
const pathName = url.pathname.toLowerCase();
const extension = pathName
.substring(pathName.lastIndexOf(".") || pathName.length)
?.toLowerCase();
// Prerender loop protection
// Non robot user agent
// Ignore extensions
if (
isPrerender ||
!BOT_AGENTS.some((bot) => userAgent.includes(bot)) ||
(extension.length && IGNORE_EXTENSIONS.includes(extension))
) {
return fetch(request);
}
// Build Prerender request
const newURL = `https://service.prerender.io/${request.url}`;
const newHeaders = new Headers(request.headers);
newHeaders.set("X-Prerender-Token", env.PRERENDER_TOKEN);
return fetch(new Request(newURL, {
headers: newHeaders,
redirect: "manual",
}));
}
代碼主要為處理 HTTP 請求的服務。藉由使用者代理(User-Agent)和檔案擴展名(extensions)來判斷是否應該使用 Prerender 服務。
- 接下來返回此 worker 的預覽畫面,點選
Triggers
後,下方點選Add route
,並填上網站的路由像是*your-domain.com/*
與 Zone 是屬於哪個網站,最後點選Add route
儲存。
- 下一步就是要添加 Prerender Token 至環境變數。點選
Settings
>Variables
>Add Variable
。並複製貼上由 Prerender.io 提供的 Token。
!!變數名稱必須為 PRERENDER_TOKEN
。
按下儲存後,這個 Prerender worker 就能順利運行了。
以上步驟完成後,就能至 Prerender.io 上傳 Sitemap,供爬蟲爬取再儲存快取。
結論
🔗目前 Prerender.io 免付費額度為每月可執行1000次預渲染,對於小型或是變更不頻繁的網站已經足夠了,而使用此種方式解決 SPA 網頁 SEO 遇到的問題,也算是比較容易上手的,畢竟不用修改原本的專案代碼,不過經歷這次經驗,下次開發專案就得依據是否有需求做 SEO 而考慮選擇的框架與方法,畢竟專案一多,若之後改成要收費那就會衍生出其他問題了😶。
參考
Alvin
軟體工程師,喜歡金融知識、健康觀念、心理哲學、自助旅遊與系統設計。
相關文章
留言區 (0)
尚無留言