Blazor WASM SEO - 使用預渲染解決 SPA 網頁爬蟲無法讀取問題 (Prerender.io & Cloudflare Workers 操作流程教學)


December 19, 2023 程式語言

Blazor WASM SEO - 使用預渲染解決 SPA 網頁爬蟲無法讀取問題 (Prerender.io & Cloudflare Workers 操作流程教學)
紀錄如何藉由 Prerender.io 與 Cloudflare Workers 解決 SPA 網站在搜尋引擎爬取不到內容的問題。

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。

流程圖:

Prerender Solution Architecture

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

🔗
  1. 從首頁點選左側功能列(Workers & Pages)。

  2. 建立 Worker 並發佈,第一次建立官網應該會提供 Hello world 的範例,可以不管它先直接建立,之後可以再修改裡面的內容。

  3. 接下來就可以直接複製貼上 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 服務。

  1. 接下來返回此 worker 的預覽畫面,點選 Triggers 後,下方點選 Add route,並填上網站的路由像是 *your-domain.com/* 與 Zone 是屬於哪個網站,最後點選 Add route 儲存。
Triggers
  1. 下一步就是要添加 Prerender Token 至環境變數。點選 Settings> Variables > Add Variable。並複製貼上由 Prerender.io 提供的 Token。
Prerender Token
Add a Variable

!!變數名稱必須為 PRERENDER_TOKEN

按下儲存後,這個 Prerender worker 就能順利運行了。

以上步驟完成後,就能至 Prerender.io 上傳 Sitemap,供爬蟲爬取再儲存快取。

結論

🔗

目前 Prerender.io 免付費額度為每月可執行1000次預渲染,對於小型或是變更不頻繁的網站已經足夠了,而使用此種方式解決 SPA 網頁 SEO 遇到的問題,也算是比較容易上手的,畢竟不用修改原本的專案代碼,不過經歷這次經驗,下次開發專案就得依據是否有需求做 SEO 而考慮選擇的框架與方法,畢竟專案一多,若之後改成要收費那就會衍生出其他問題了😶。

參考

BlazorWebSEO



Avatar

Alvin

軟體工程師,喜歡金融知識、健康觀念、心理哲學、自助旅遊與系統設計。

相關文章