每次發布時,根據完成的公開頁面集合重新產生網站地圖,然後一併上傳兩者。這套 Mac 工作流程使用簡短的 Python 指令碼,自動納入新增頁面並移除已刪除頁面,並檢查公開網站的結果。
這裡的任務是維護從 Mac 匯出的靜態小型網站的網站地圖。範例使用關於顏色的虛構頁面,並假設你現有的發布流程可以上傳匯出資料夾,並從公開網站移除過時檔案。
選擇網站地圖重新產生流程的位置
先確認你的發布系統是否已經會產生網站地圖。Google 指出,許多內容管理系統會自動提供網站地圖。如果你的系統有,請先新增並移除測試頁面,再檢查網站地圖,然後才導入另一個產生器。瞭解網站地圖。
對於靜態匯出,請依照以下順序操作:完成頁面匯出、從匯出內容產生網站地圖,再發布產生的資料夾。這是一種建議的實作模式:它能讓網站地圖描述與發布系統收到的頁面集合相同。
在發布期間產生網站地圖,也能讓新增和移除遵循一致的規則。完成的匯出內容中有的頁面會列入;沒有的頁面則會省略。你不必另外記住要更新清單。
選擇符合現有設定的整合方式:
| 現有發布設定 | 重新產生的位置 |
|---|---|
| CMS 已維護網站地圖 | 保留該產生器,並測試它如何因應頁面變更 |
| 靜態匯出工具支援匯出後執行命令 | 在上傳前於該處執行產生器 |
| 現有命令會發布已完成的資料夾 | 透過產生器的選用發布參數執行該命令 |
| 你透過桌面介面上傳 | 先產生網站地圖,再上傳完整資料夾;若要無人值守地更新公開網站,則需要受支援的發布整合方式 |
本機指令碼本身無法變更遠端網站。以下工作流程會自動產生網站地圖,也可以啟動現有的發布命令。實際上傳仍由你既有的發布工具負責。
明確界定哪些頁面應列入網站地圖
對一般頁面 URL,Google 接受每行包含一個完整 URL 的文字格式網站地圖。此任務不必使用 XML。每個頁面都應列出其偏好的公開 URL,而不是同一內容的多種 URL 版本。建立並提交網站地圖。
文字格式需要使用 UTF-8 編碼,且只能包含 URL,不可有標題或註解。單一檔案最多可包含 50,000 個 URL,大小上限為 52,428,800 位元組。請將此範例的網站地圖放在網站根目錄,讓它涵蓋該位置下方匯出的頁面。網站地圖協定:文字檔與位置。
產生器採用刻意保持簡單的匯出慣例:
| 匯出的檔案 | 示意公開 URL |
|---|---|
public/index.html |
https://example.com/ |
public/colors/blue.html |
https://example.com/colors/blue.html |
public/tiles/index.html |
https://example.com/tiles/ |
只有在你的主機提供完全相同的 URL 時,才使用這個慣例。如果發布工具會將 blue.html 轉換為 /colors/blue/,或是在 /collection/ 之類的路徑下提供網站,請改用它的路由清單,或在發布前調整對應方式。掃描檔名無法推斷自訂路由。
只保留預定要納入匯出資料夾的頁面。此範例會略過以句點開頭的路徑和名為 404.html 的檔案;它不會檢查頁面中繼資料、標準網址宣告或重新導向規則。若網站還有這些條件,應改為從發布工具提供的合格頁面清單產生網站地圖。
也請持續維護一般導覽連結。WHATWG 將使用 a 元素和 href 屬性建立的超連結列入規範;Google 則說明,妥善連結的頁面通常可透過導覽被發現。更新網站地圖時,也應一併更新指向新增或移除頁面的連結。WHATWG HTML 標準:連結、瞭解網站地圖。
在 Mac 上建立產生器
你需要 Python 3.8 或更新版本,以及純文字編輯器。在終端機執行 python3 --version,查看你可用的直譯器。如果需要 Python,請依照 Python 軟體基金會的 macOS 安裝說明操作。其文件也說明如何在終端機使用 python3 執行指令碼。在 macOS 上使用 Python。
在桌面建立名為 sitemap-demo 的資料夾。將以下完整程式儲存於其中,檔名為 regenerate_sitemap.py。此程式使用 Python 標準函式庫,不需安裝其他套件。
import argparse
from pathlib import Path, PurePosixPath
import subprocess
import tempfile
from urllib.parse import quote, urlsplit
def make_sitemap(paths, origin):
parsed = urlsplit(origin)
if (parsed.scheme not in ("http", "https") or not parsed.hostname
or parsed.netloc != parsed.hostname
or parsed.path not in ("", "/")
or parsed.query or parsed.fragment):
raise ValueError("Use a site origin, such as https://example.com")
base = origin.rstrip("/")
urls = set()
for relative in paths:
path = PurePosixPath(relative)
if path.name == "index.html":
parent = path.parent.as_posix()
route = "" if parent == "." else parent + "/"
else:
route = path.as_posix()
urls.add(base + "/" + quote(route, safe="/"))
if not urls:
raise ValueError("No eligible pages; sitemap was not replaced")
if len(urls) > 50000:
raise ValueError("Split the sitemap: more than 50,000 URLs")
data = ("\n".join(sorted(urls)) + "\n").encode("utf-8")
if len(data) > 50 * 1024 * 1024:
raise ValueError("Split the sitemap: larger than 50 MB")
return data
def main():
parser = argparse.ArgumentParser()
parser.add_argument("folder")
parser.add_argument("origin")
parser.add_argument("publish", nargs=argparse.REMAINDER)
args = parser.parse_args()
root = Path(args.folder).resolve()
if not root.is_dir():
parser.error("The export folder must already exist")
paths = []
for file in root.rglob("*.html"):
relative = file.relative_to(root)
if (file.is_file() and file.name != "404.html"
and not any(p.startswith(".") for p in relative.parts)):
paths.append(relative.as_posix())
data = make_sitemap(paths, args.origin)
target = root / "sitemap.txt"
temporary = None
try:
with tempfile.NamedTemporaryFile(dir=root, delete=False) as handle:
temporary = Path(handle.name)
handle.write(data)
temporary.replace(target)
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)
print(f"Wrote {len(data.splitlines())} URLs to {target}", flush=True)
if args.publish:
subprocess.run(args.publish, check=True)
if __name__ == "__main__":
main()
每次執行時都會重建完整的 URL 清單。程式會排序並去除重複項目、編碼路徑中的空格等字元,並先寫入暫存檔,再取代 sitemap.txt。若產生過程發生錯誤,程式會停止,不會執行選用的發布命令。
若匯出內容為空,程式會刻意引發錯誤,並保留先前的網站地圖。這可以抓出意外選錯資料夾或資料夾內容不完整的情況。若要刻意移除所有頁面,應另外決定如何發布,而不是將空的匯出內容交給此工作流程處理。
在連接發布流程前,先測試新增與移除頁面
在終端機中,切換至示範資料夾:
cd "$HOME/Desktop/sitemap-demo"
使用以下完整設定命令建立兩個虛構頁面。請在新建的示範資料夾中執行;此命令會寫入兩個指定的範例檔案。
python3 - <<'PY'
from pathlib import Path
root = Path("public")
(root / "colors").mkdir(parents=True, exist_ok=True)
pages = {
"index.html": "<!doctype html><title>Color collection</title><p>Color collection.</p>",
"colors/blue.html": "<!doctype html><title>Blue tile</title><p>A blue tile.</p>",
}
for name, content in pages.items():
(root / name).write_text(content, encoding="utf-8")
PY
產生並檢查本機網站地圖。範例網站來源只是示意,這些命令不會將任何內容發布至該處。
python3 regenerate_sitemap.py public https://example.com
cat public/sitemap.txt
預期檔案會剛好包含以下兩行:
https://example.com/
https://example.com/colors/blue.html
現在將虛構的藍色頁面換成綠色頁面:
python3 - <<'PY'
from pathlib import Path
root = Path("public")
(root / "colors/blue.html").unlink()
(root / "colors/green.html").write_text(
"<!doctype html><title>Green tile</title><p>A green tile.</p>",
encoding="utf-8",
)
PY
python3 regenerate_sitemap.py public https://example.com
cat public/sitemap.txt
預期結果是首頁 URL,後接 https://example.com/colors/green.html。藍色頁面的 URL 會消失,因為匯出的檔案已不存在。這能檢查更新的兩種情況,不必仰賴搜尋引擎察覺變更。
將重新產生網站地圖納入每次發布
對於你的實際網站,請將示範資料夾和來源換成實際完成的匯出資料夾和公開來源。只在匯出完成後執行產生器,並在發布完成前保持該資料夾不變。各次發布請依序執行。
如果專案資料夾中已有可正常運作且名為 deploy-site.sh 的發布指令碼,以下整合命令會先產生網站地圖,再執行該指令碼。此範例需要既有指令碼;產生器不會建立該指令碼,也不會提供主機設定。
python3 regenerate_sitemap.py public https://example.com sh ./deploy-site.sh
你的發布指令碼必須上傳同一個 public 資料夾,包括 sitemap.txt,並移除公開網站上匯出內容中已不存在的檔案。它不得在產生網站地圖後重建頁面。若發布工具也會建置網站,請改在建置階段和上傳階段之間產生網站地圖。
若產生失敗,發布命令就會略過。若發布失敗,Python 會回報失敗,但遠端網站可能已部分變更。重試前請先檢查公開網站的結果。如果主機支援一次發布完整版本,請使用該功能的官方機制。
若使用桌面上傳工具,請先產生網站地圖,再選取要上傳的匯出資料夾。確認上傳工具會取代現有網站地圖,並妥善處理已移除的頁面。這仍需要執行上傳動作;單靠本機自動產生網站地圖,無法在無人值守的情況下發布至公開網站。
分別驗證公開網站地圖和已移除頁面
成功發布後,請在瀏覽器中開啟實際公開的 sitemap.txt URL。確認新增頁面有列入,已移除頁面則沒有。接著直接造訪每個受影響的頁面。這能區分正確的本機匯出內容和正確的公開版本。
刪除網站地圖項目,並不能決定該頁面舊網址的 HTTP 回應。對於沒有替代頁面的已移除頁面,請確認主機傳回適當的頁面不存在回應。RFC 9110 將 404 定義為資源目前沒有表示內容,將 410 定義為已知資源很可能永久無法使用的情況。RFC 9110:HTTP 語意。
若頁面顯示找不到頁面的訊息,卻傳回 200,Google 可能會將其視為軟性 404。Google 也說明,先前已建立索引而傳回適用 4xx 回應的 URL,會隨時間從其索引中移除。因此,從網站地圖刪除 URL 只是移除頁面的一個步驟。HTTP 狀態碼如何影響 Google 檢索工具。
發現異常時,請依照以下診斷步驟:
| 觀察到的情況 | 接下來檢查 |
|---|---|
| 本機網站地圖仍列出已移除的頁面 | 檢查是否有遺留的匯出檔案,或選錯匯出資料夾 |
| 本機網站地圖正確,但公開版本不同 | 檢查上傳的資料夾、目的地和快取 |
| 公開網站地圖正確,但已移除的頁面仍可開啟 | 檢查發布流程是否移除了遠端舊檔案 |
| 產生的 URL 開啟錯誤頁面 | 依據檔名慣例檢查主機的路由對應 |
| 新 URL 已公開,但搜尋結果中沒有 | 將搜尋引擎的發現和建立索引視為另一個流程 |
快取可能是公開網站地圖看起來沒有變更的原因。RFC 9111 定義了快取回應如何維持新鮮,以及如何重新驗證。如果主機支援設定回應標頭,Cache-Control: no-cache 會要求在重用儲存的回應前先成功驗證;只變更本機檔案並不會設定該標頭。RFC 9111:HTTP 快取。
維持固定的網站地圖網址
透過 Search Console 的 Sitemaps 報表提交公開網站地圖的固定 URL,或在網站既有的 robots.txt 檔案中參照該 URL,讓使用者和搜尋引擎能找到它。Google 說明了這兩種方式,並指出提交只是提示。建立並提交網站地圖。
以示意網站為例,探索用的那一行會是:
Sitemap: https://example.com/sitemap.txt
發布時替換網站地圖內容,但維持其網址不變。實際完成的檢查項目是:公開網站地圖與完成的匯出內容相符、新增頁面可透過列出的 URL 開啟,而已移除頁面則有預期的回應。搜尋引擎會依自己的時程處理這些變更;列入網站地圖不保證一定會檢索或建立索引。瞭解網站地圖。
來源
- 建立並提交網站地圖 — Google 搜尋中心。
- 瞭解網站地圖 — Google 搜尋中心。
- Sitemaps XML 格式 — Sitemaps.org。
- HTML 標準:連結 — WHATWG。
- 在 macOS 上使用 Python — Python 軟體基金會。
- RFC 9110:HTTP 語意 — RFC 編輯委員會/IETF。
- RFC 9111:HTTP 快取 — RFC 編輯委員會/IETF。
- HTTP 狀態碼如何影響 Google 檢索工具 — Google 檢索基礎架構。