Pada halaman ini
Memigrasikan 500 halaman daripada WordPress ke headless CMS sepatutnya mengambil masa hujung minggu. Pada hakikatnya, ia mengambil masa 3-6 minggu -- kerana seseorang perlu menyalin kandungan secara manual, membetulkan pemformatan, memaut semula imej, dan mengesahkan setiap halaman. Migrasi kandungan ialah tugas yang paling digeruni dalam pembangunan web, dan ia hampir sepenuhnya boleh diautomasikan.
CrawlForge mengekstrak keseluruhan kandungan tapak anda secara berprogram: halaman, metadata, imej, pautan, dan struktur dokumen. Panduan ini menunjukkan kepada anda cara membina saluran paip migrasi yang memindahkan beribu-ribu halaman antara mana-mana dua platform dalam beberapa jam, bukan beberapa minggu.
Jadual Kandungan
- Mengapa Migrasi Kandungan Menyakitkan
- Gambaran Keseluruhan Seni Bina
- Langkah 1: Inventori Tapak Sumber Anda
- Langkah 2: Ekstrak Kandungan dan Metadata
- Langkah 3: Kekalkan Struktur Dokumen
- Langkah 4: Ubah untuk Platform Sasaran
- Langkah 5: Sahkan Migrasi
- Analisis Kos Credit
- Hasil dan Manfaat
- Soalan Lazim
Mengapa Migrasi Kandungan Menyakitkan
Migrasi kandungan gagal atas tiga sebab:
- Volum: Walaupun tapak perniagaan kecil mempunyai 200-500 halaman. Setiap halaman memerlukan kandungan, metadata, imej, dan pautan dalaman dikekalkan
- Ketidakpadanan format: CMS sumber dan sasaran menggunakan model kandungan yang berbeza (blok WordPress vs. MDX vs. teks kaya Contentful)
- Kerumitan tersembunyi: Shortcode, media terbenam, medan tersuai, ubah hala -- semuanya memerlukan pengendalian
Migrasi manual berkos lebih kurang $5-15 setiap halaman dalam masa penganalisis. Migrasi 500 halaman pada $10/halaman berkos $5,000 dalam buruh sahaja. Migrasi automatik dengan CrawlForge berkos di bawah $50 dalam credits.
| Kaedah Migrasi | Kos (500 halaman) | Masa | Kadar Ralat |
|---|---|---|---|
| Salin-tampal manual | $5,000-7,500 | 3-6 minggu | 5-10% |
| Separa automatik (skrip) | $2,000-3,000 | 1-2 minggu | 2-5% |
| Saluran paip CrawlForge | $20-50 | 2-4 jam | <1% |
Gambaran Keseluruhan Seni Bina
Saluran paip migrasi menggunakan lima alat CrawlForge:
| Peringkat | Alat | Credits | Tujuan |
|---|---|---|---|
| Inventori | map_site | 3 | Temui semua halaman dan strukturnya |
| Pengekstrakan kandungan | extract_content | 2 | Tarik kandungan bersih daripada setiap halaman |
| Tangkapan metadata | extract_metadata | 1 | Kekalkan teg SEO dan data Open Graph |
| Pemetaan pautan | extract_links | 1 | Petakan pautan dalaman untuk ditulis semula |
| Pemprosesan kelompok | batch_scrape | 5 | Proses beratus-ratus halaman dengan cekap |
Langkah 1: Inventori Tapak Sumber Anda
Petakan setiap halaman pada tapak sumber anda, termasuk halaman yang mungkin tiada dalam navigasi.
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
const client = new Client({
name: 'content-migrator',
version: '1.0.0',
});
interface SiteInventory {
pages: Array<{
url: string;
path: string;
depth: number;
type: 'page' | 'post' | 'category' | 'media' | 'other';
}>;
totalPages: number;
pathGroups: Record<string, number>;
}
async function inventorySite(siteUrl: string): Promise<SiteInventory> {
const mapResult = await client.callTool({
name: 'map_site',
arguments: {
url: siteUrl,
max_urls: 2000,
include_sitemap: true,
group_by_path: true,
include_metadata: true,
},
});
const result = JSON.parse(mapResult.content[0].text);
// Classify pages by URL pattern
const pages = result.urls.map((url: string) => {
const path = new URL(url).pathname;
let type: 'page' | 'post' | 'category' | 'media' | 'other' = 'other';
if (path.match(/\/\d{4}\/\d{2}\//)) type = 'post'; // WordPress date URLs
else if (path.match(/\/blog\//)) type = 'post';
else if (path.match(/\/category\//)) type = 'category';
else if (path.match(/\.(pdf|doc|jpg|png)/)) type = 'media';
else type = 'page';
return { url, path, depth: path.split('/').length - 1, type };
});
return {
pages,
totalPages: pages.length,
pathGroups: result.groupedByPath || {},
};
}Langkah 2: Ekstrak Kandungan dan Metadata
Ekstrak kandungan bersih dan semua metadata daripada setiap halaman, mengekalkan struktur tajuk dan pemformatan.
interface MigratedPage {
sourceUrl: string;
slug: string;
title: string;
content: string; // Clean markdown or HTML
excerpt: string;
metadata: {
title: string;
description: string;
ogImage: string;
canonical: string;
publishedAt: string;
author: string;
};
internalLinks: string[];
images: string[];
}
async function extractPage(url: string): Promise<MigratedPage> {
// Extract clean content (readability-optimized)
const contentResult = await client.callTool({
name: 'extract_content',
arguments: { url },
});
const content = JSON.parse(contentResult.content[0].text);
// Extract metadata separately for completeness
const metaResult = await client.callTool({
name: 'extract_metadata',
arguments: { url },
});
const meta = JSON.parse(metaResult.content[0].text);
// Extract links for internal link mapping
const linksResult = await client.callTool({
name: 'extract_links',
arguments: { url },
});
const links = JSON.parse(linksResult.content[0].text);
const internalLinks = (links.internal || []).map(
(l: { href: string }) => l.href
);
// Extract image URLs from content
const imageRegex = /!\[.*?\]\((.*?)\)/g;
const images: string[] = [];
let match;
while ((match = imageRegex.exec(content.content)) !== null) {
images.push(match[1]);
}
const slug = new URL(url).pathname
.replace(/^\//,'')
.replace(/\/$/,'')
.replace(/\//g, '-');
return {
sourceUrl: url,
slug,
title: meta.title || content.title || '',
content: content.content,
excerpt: meta.description || content.excerpt || '',
metadata: {
title: meta.title || '',
description: meta.description || '',
ogImage: meta.ogImage || '',
canonical: meta.canonical || url,
publishedAt: meta.publishedDate || '',
author: meta.author || '',
},
internalLinks,
images,
};
}Langkah 3: Kekalkan Struktur Dokumen
Untuk tapak besar, gunakan pemprosesan kelompok dan bina peta pautan lengkap untuk penulisan semula URL.
type UrlMap = Map<string, string>; // old URL -> new slug
async function batchExtract(
inventory: SiteInventory
): Promise<{ pages: MigratedPage[]; urlMap: UrlMap }> {
const pages: MigratedPage[] = [];
const urlMap: UrlMap = new Map();
// Process in batches of 20
const batchSize = 20;
const urls = inventory.pages
.filter(p => p.type === 'page' || p.type === 'post')
.map(p => p.url);
for (let i = 0; i < urls.length; i += batchSize) {
const batch = urls.slice(i, i + batchSize);
console.log(
`Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(urls.length / batchSize)}`
);
// Extract each page in the batch
for (const url of batch) {
try {
const page = await extractPage(url);
pages.push(page);
urlMap.set(url, page.slug);
} catch (error) {
console.error(`Failed to extract ${url}:`, error);
}
}
}
return { pages, urlMap };
}Langkah 4: Ubah untuk Platform Sasaran
Tulis semula pautan dalaman dan ubah kandungan agar sepadan dengan format CMS sasaran anda.
function transformForTarget(
page: MigratedPage,
urlMap: UrlMap,
targetFormat: 'mdx' | 'contentful' | 'sanity'
): Record<string, unknown> {
// Rewrite internal links to new URL structure
let content = page.content;
for (const [oldUrl, newSlug] of urlMap) {
content = content.replace(
new RegExp(oldUrl.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'),
`/${newSlug}`
);
}
switch (targetFormat) {
case 'mdx':
return {
slug: page.slug,
title: page.title,
description: page.metadata.description,
date: page.metadata.publishedAt || new Date().toISOString(),
content: content, // Already markdown from extract_content
};
case 'contentful':
return {
fields: {
title: { 'en-US': page.title },
slug: { 'en-US': page.slug },
body: { 'en-US': content },
description: { 'en-US': page.metadata.description },
publishedAt: { 'en-US': page.metadata.publishedAt },
},
contentType: 'blogPost',
};
case 'sanity':
return {
_type: 'post',
title: page.title,
slug: { current: page.slug },
body: content,
excerpt: page.excerpt,
publishedAt: page.metadata.publishedAt,
};
default:
return { ...page, content };
}
}Langkah 5: Sahkan Migrasi
Selepas mengubah, sahkan bahawa setiap halaman telah dimigrasikan dengan betul.
interface ValidationResult {
totalPages: number;
successCount: number;
failedCount: number;
warnings: Array<{
slug: string;
issue: string;
}>;
}
function validateMigration(
original: SiteInventory,
migrated: MigratedPage[]
): ValidationResult {
const warnings: Array<{ slug: string; issue: string }> = [];
for (const page of migrated) {
// Check for empty content
if (!page.content || page.content.trim().length < 50) {
warnings.push({
slug: page.slug,
issue: 'Content appears empty or very short',
});
}
// Check for missing metadata
if (!page.metadata.title) {
warnings.push({ slug: page.slug, issue: 'Missing title' });
}
if (!page.metadata.description) {
warnings.push({ slug: page.slug, issue: 'Missing meta description' });
}
// Check for broken internal links
for (const link of page.internalLinks) {
const isLinkMigrated = migrated.some(
p => p.sourceUrl === link || link.includes(p.slug)
);
if (!isLinkMigrated) {
warnings.push({
slug: page.slug,
issue: `Internal link may be broken: ${link}`,
});
}
}
}
const contentPages = original.pages.filter(
p => p.type === 'page' || p.type === 'post'
);
return {
totalPages: contentPages.length,
successCount: migrated.length,
failedCount: contentPages.length - migrated.length,
warnings,
};
}Analisis Kos Credit
Untuk migrasi tapak web 500 halaman:
| Operasi | Alat | Credits | Kuantiti | Subjumlah |
|---|---|---|---|---|
| Inventori tapak | map_site | 3 | 1 | 3 |
| Pengekstrakan kandungan | extract_content | 2 | 500 halaman | 1,000 |
| Pengekstrakan metadata | extract_metadata | 1 | 500 halaman | 500 |
| Pengekstrakan pautan | extract_links | 1 | 500 halaman | 500 |
| Jumlah | 2,003 credits |
Migrasi 500 halaman yang lengkap berkos kira-kira 2,000 credits. Pelan Hobby ($19/bulan, 5,000 credits) mengendalikan ini dengan ruang yang lega. Untuk tapak yang lebih besar (1,000+ halaman), pelan Professional ($99/bulan, 50,000 credits) menyediakan ruang yang banyak.
Hasil dan Manfaat
Migrasi kandungan automatik menyampaikan:
- Kelajuan: Migrasikan 500 halaman dalam 2-4 jam dan bukannya 3-6 minggu
- Ketepatan: Tiada ralat salin-tampal, pemformatan rosak, atau halaman terlepas
- Kelengkapan: Setiap halaman, setiap teg meta, setiap pautan dalaman ditangkap
- Penjimatan kos: $5,000+ dalam buruh manual digantikan dengan $19-99 dalam tool credits
CrawlForge adalah paling sesuai untuk migrasi kandungan apabila anda perlu mengekalkan ekuiti SEO -- teg meta, pautan dalaman, URL kanonikal, dan struktur kandungan semuanya berpindah dengan bersih.
Soalan Lazim
Bolehkah CrawlForge mengendalikan shortcode WordPress?
Alat extract_content CrawlForge memproses HTML yang dirender, bukan sumber WordPress mentah. Shortcode sudah diperluaskan kepada output HTMLnya apabila CrawlForge mengekstraknya. Anda mendapat kandungan yang dirender, yang merupakan apa yang anda mahukan untuk migrasi.
Bagaimana pula dengan imej dan fail media?
CrawlForge mengekstrak URL imej daripada kandungan. Anda akan memerlukan langkah berasingan untuk memuat turun dan menghos semula imej pada platform sasaran anda. Alat fetch_url (1 credit) boleh memuat turun fail media individu.
Bagaimanakah saya mengendalikan ubah hala selepas migrasi?
urlMap yang dijana dalam Langkah 3 memberi anda pemetaan URL-lama-ke-slug-baharu yang lengkap. Eksport ini sebagai peta ubah hala untuk platform pengehosan anda (Vercel vercel.json, Netlify _redirects, atau konfigurasi nginx).
Migrasikan tapak anda hujung minggu ini. Mulakan secara percuma dengan 1,000 credits -- cukup untuk memigrasikan 250+ halaman. Tiada kad kredit diperlukan.
Sumber berkaitan:
Cuba sendiri — tiada pendaftaran diperlukan
Jalankan mana-mana daripada 27 alat scraping dan pengekstrakan CrawlForge dalam playground, kemudian mula secara percuma dengan 1,000 credits.
1,000 credits percuma • Isi semula setiap bulan • Tiada kad kredit diperlukan
Tag
Tentang Penulis
Kekal dikemas kini dengan pandangan terkini
Dapatkan tutorial, kemas kini produk dan petua web scraping terus ke peti masuk anda.
Tiada spam. Berhenti melanggan bila-bila masa.