使用WordPress的插件FG Drupal to WordPress可以將Drupal站點(diǎn)遷移至WordPress。使用插件本身是很容易的,煩瑣的工作在遷移前的準(zhǔn)備及遷移后的一些處理。本文從實(shí)戰(zhàn)角度講述了整個(gè)過程,一些解決問題的思路可供參考。

為什么要遷移
簡(jiǎn)單地說,是因?yàn)閃ordPress的陣營(yíng)比Drupal大得多(份額:59.8% 比 4.6%)。使用Drupal十幾年,見到許多模塊后繼無人。而我自己的站點(diǎn)只是一個(gè)圖文混排的Blog,用到Markdown、代碼語法高亮、圖片自動(dòng)水印、文檔自動(dòng)目錄(TOC)等(詳見這里)。Drupal雖然能做到,但總還有些問題,模塊成熟度不夠。表現(xiàn)在效果差一點(diǎn)或易用性不夠。

方案
基本方案是使用WordPress里的FG Drupal to WordPress插件。這個(gè)插件支持文章和圖片的導(dǎo)入。但是免費(fèi)版的不支持評(píng)論的導(dǎo)入。另外,各種版本好像都不支持Blog類型內(nèi)容的導(dǎo)入。
還有一個(gè)CMS2CMS的插件,但評(píng)價(jià)不高,而且需要注冊(cè)。試了一下就放棄了。
另外,我的服務(wù)器都是基于Docker的,所以測(cè)試的和正式的站點(diǎn)都會(huì)基于Docker,安裝和配置會(huì)比較省事。
準(zhǔn)備工作
建立測(cè)試用的WordPress站點(diǎn)
這個(gè)站點(diǎn)用于導(dǎo)入數(shù)據(jù),也是為了體驗(yàn)功能,驗(yàn)證WordPress是否能實(shí)際滿足自己的需求。
因?yàn)镕G Drupal to WordPress需要用到MySQL PDO,而官方docker image沒有安裝,所以自行修改一下,順便使用國(guó)內(nèi)的鏡像會(huì)快很多。
下面是 Dockerfile。
ARG VER=5
FROM wordpress:$VER
MAINTAINER loblab
ARG APT_MIRROR=mirrors.163.com
ARG DEBIAN_FRONTED=noninteractive
RUN find /etc/apt -type f -name "*.list" -exec sed -i s/deb.debian.org/$APT_MIRROR/ {} \;
RUN find /etc/apt -type f -name "*.list" -exec sed -i s/security.debian.org/$APT_MIRROR/ {} \;
RUN apt-get update --fix-missing && apt-get -y upgrade
#RUN apt-get -y install php-mysql
RUN docker-php-ext-install pdo pdo_mysql
創(chuàng)建一個(gè)空的數(shù)據(jù)庫以及用戶tmpwp,給站點(diǎn)使用。配置寫入 docker-compose.yml 里。
services:
wp0:
image: loblab/wordpress
hostname: wp0
container_name: lab-wp0
restart: "always"
ports:
- "8039:80"
environment:
WORDPRESS_DB_HOST: mysql5
WORDPRESS_DB_NAME: tmpwp
WORDPRESS_DB_USER: tmpwp
WORDPRESS_DB_PASSWORD: password
volumes:
- "./wordpress/php-upload.ini:/usr/local/etc/php/conf.d/php-upload.ini"
- "/var/lib/sandbox/wordpress:/var/www/html"
WordPress的安裝確實(shí)很容易,從 pull/build docker image 到安裝完成,總共不到10分鐘。
建立臨時(shí)Drupal站點(diǎn)
克隆一個(gè)Drupal的臨時(shí)站點(diǎn),用于導(dǎo)出。導(dǎo)出并不一定是只讀的,根據(jù)導(dǎo)入的要求,有可能對(duì)原始數(shù)據(jù)進(jìn)行修改。所以克隆的不僅是數(shù)據(jù)庫,最好將附件也進(jìn)行復(fù)制。以免因誤操作破壞原站點(diǎn)。

Blog轉(zhuǎn)Article
因?yàn)镕G Drupal to WordPress不支持Blog類型的導(dǎo)入,于是在Drupal站點(diǎn)里將Blog類型轉(zhuǎn)為Article。這需要使用Convert Bundles模塊。

但它對(duì)VBO的支持似乎不好,因?yàn)槲业腂log數(shù)量不算多,所以就手動(dòng)點(diǎn)了幾十頁,沒再折騰VBO。
恢復(fù)原圖
Drupal中呈現(xiàn)的是縮小并加水印的圖片,如果直接導(dǎo)入得到的將是這種圖片。為了把原圖導(dǎo)入到WordPress,我們可以在Drupal中去掉Large style圖片的縮放及水印處理,然后重新生成Large style的所有圖片。使用drush命令:
drush if large
數(shù)據(jù)導(dǎo)入
在WordPress站點(diǎn)里安裝FG Drupal to WordPress插件。

導(dǎo)入的設(shè)置如下:
- 把Drupal的Summary導(dǎo)入為摘要(Excerpt)
- 不設(shè)置Featured image

導(dǎo)入的結(jié)果類似這樣:

注意檢查導(dǎo)入圖片的數(shù)量和 Drupal 的 style/large 目錄的是否一致。如果發(fā)現(xiàn)有問題就重新導(dǎo)入。我實(shí)際導(dǎo)入了幾十次。所以設(shè)置為每次導(dǎo)入前就自動(dòng)全部清除。
中文文件名圖片的問題
似乎FG Drupal to WordPress并不能很好地處理中文文件名。中文字符被忽略掉了。比如"漢字abc123.jpg"將被重命名為"abc123.jpg",而全漢字的文件名將被重命名為"unnamed"。于是就會(huì)出現(xiàn)下面的問題:

如果同一個(gè)月份里有好幾個(gè)全漢字的文件名,則它們都會(huì)被最后一張覆蓋。數(shù)據(jù)庫里看每張圖片都被導(dǎo)入過,只不過文件名都變成了一樣的unnamed.

因?yàn)閿?shù)量不多,而且是后面才發(fā)現(xiàn)這個(gè)問題,所以就手動(dòng)刪除,重新上傳,手動(dòng)修改文章中的鏈接了(試過幾個(gè) replace image 類型的插件,因?yàn)楹臀矣玫钠渌腎mage縮放、水印之類的插件在一起,容易出錯(cuò),就放棄了)。
修改數(shù)據(jù)庫
因?yàn)樵璂rupal站點(diǎn)文章里的內(nèi)容,有些不是純Markdown的語法(比如插入的圖片鏈接,代碼語法高亮),于是趁這個(gè)機(jī)會(huì)一并改掉。
去分析數(shù)據(jù)庫結(jié)構(gòu)并用SQL語句修改不一定容易,我簡(jiǎn)單地導(dǎo)出整個(gè)數(shù)據(jù)庫為SQL語句,并用全文查找替換,然后導(dǎo)回。使用強(qiáng)大的正則表達(dá)式。事實(shí)證明,簡(jiǎn)單粗暴有效。因?yàn)橐鎿Q的字符串的pattern是唯一的。下面是Perl的寫法。
s#\\n\<img[^>]+?src=\\"(http://nuc.+?|/sites/default/.+?)\\".+?(alt|data-caption)=\\"(圖:)?(.+?)\\".*?\>#\\n#g;
s#\(http://nuc.loblab:8039/(.+?)\)#\(/$1\)#g;
s#\<code type=\\"(.+?)\\"\>#```$1#g;
s#\\n\</code\>#\\n```#g;
因?yàn)閃ordPress很多地方把域名hard code進(jìn)了數(shù)據(jù)庫,所以在改變域名時(shí)(測(cè)試站點(diǎn)變正式站點(diǎn)時(shí)),也要用類似的辦法處理(UI上重新設(shè)置似乎不徹底)。
鏈接重定向
考慮到搜索引擎收錄了以前的文章鏈接,而新站點(diǎn)的鏈接地址和以前的不同,所以需要使用301做重定向。
先在Drupal里,做一個(gè)View,得到Node ID和文章標(biāo)題的映射表。
1065 Redis 性能簡(jiǎn)單測(cè)試
1058 燈光控制器的硬觸發(fā)測(cè)試
1054 Docker下使用GPU版TensorFlow
在WordPress里,則可以直接查詢到ID和文章標(biāo)題(WordPress的數(shù)據(jù)庫比Drupal簡(jiǎn)單了一個(gè)數(shù)量級(jí))。
SELECT id, post_title
FROM `wp_posts`
WHERE post_status="publish" AND (post_type="post" OR post_type="page")
ORDER BY `post_date` DESC
結(jié)果類似于:
1413 Redis 性能簡(jiǎn)單測(cè)試
1405 燈光控制器的硬觸發(fā)測(cè)試
1394 Docker下使用GPU版TensorFlow
根據(jù)這兩個(gè)對(duì)應(yīng)關(guān)系,以文章標(biāo)題為橋梁,就可以得到新舊鏈接的對(duì)應(yīng)關(guān)系。類似于:
Redirect 301 /node/1065 /p/1413
Redirect 301 /node/1058 /p/1405
Redirect 301 /node/1054 /p/1394
Term/Tag 的鏈接重定向也可以用類似的辦法。
最后將這些跳轉(zhuǎn)項(xiàng)加入到web root的.htaccess中。
教訓(xùn)
遷移過程中的教訓(xùn):
- 不要使用中文文件名,即使你現(xiàn)在的系統(tǒng)支持,但很可能在某次遷移中產(chǎn)生問題。除了這次的遷移,當(dāng)我嘗試把圖片遷移到Lychee(一個(gè)圖片管理系統(tǒng))時(shí),中文文件名也產(chǎn)生了問題。
- 使用文章標(biāo)題作為永久鏈接也許是有道理的。如果兩個(gè)系統(tǒng)都支持這種鏈接策略,在遷移后就不需要做重定向了。不過,文章發(fā)布后就宜修改標(biāo)題了。