« Back to Posts

How to Import Xanga into Wordpress.com

Jun 18, 2008

I told a friend that I would help her move all her old Xanga posts to her new WordPress.com account. Little did I know how much of a difficulty that would be. After extensive searching via Google, I finally found what I believe to be the best way. For the benefit of those coming after me, blow-by-blow instructions are below.

NOTE: This article is now nearly 2 years old. WordPress has moved on significantly since then, as have some of the other tools employed (such as XAMPP). In theory the process still works, but the instructions may not be exact anymore.

I told a friend that I would help her move all her old Xanga posts to her new WordPress.com account. Little did I know how much of a difficulty that would be. After extensive searching via Google, I finally found what I believe to be the best way. For the benefit of those coming after me, blow-by-blow instructions are below.

Before I begin, you may want to look at Tim Wylie's Xanga to WordPress Exporter. I could not get this to work on my friend's blog, but it did work on a few random strangers' blogs that I tried. Please let me know if you get this to work.

Now, on to my method. This is a bit complicated (ok, it's really complicated), but I've tried to lay out everything clearly. The problem is that WordPress.com doesn't let us import from Xanga directly so we have to install WordPress locally, import our Xanga archives, then export to a WordPress file and upload it to WordPress.com. So here we go:

  1. Upgrade your Xanga account to premium at https://register.xanga.com/premium/. This costs $4 for a one month subscription (make sure to uncheck the "Automatically rebill me!" box) and it's worth it for all the time you're going to save. Just take my word on that and do it.
  2. Go to http://www.xanga.com/private/Archives.aspx and get your Xanga Archives. If this is your first time getting your archives you may have to wait overnight for Xanga to prepare them. Once you've downloaded them, unzip the "archive.zip" file to your desktop. You should now have a folder called "archive" on your desktop. Inside that is a folder called "xanga" and inside that are HTML files for each month of posts on Xanga.
  3. Open a command prompt window by going to the Start Menu -> Run and typing in "cmd" and clicking "OK". You should see a black window open up that says something along the lines of: "C:\Documents and Settings\Username\".
  4. Move into your Xanga folder by typing "cd Desktop\archive\xanga" and pressing the enter key.
  5. Now combine all of the Xanga archive files by typing "copy *.htm XangaArchive.htm" and pressing the enter key. (You can now close the command prompt by typing "exit" and pressing enter.)
  6. Download XAMPP, a wonderful Apache, MySQL, PHP (if you don't know what these are, don't worry) installer for Windows at http://www.apachefriends.org/en/xampp-windows.html#641. Choose the "Installer" option when you go to download.
  7. When it's downloaded, run the installer and choose the default options. This will install XAMPP into the C: drive of your computer. When the installation finishes click "Yes" when it asks you if you want to run the Control Panel.
  8. Go to the folder C:\xampp\htdocs\php\ (you can navigate there by going to "My Computer", selecting the "C:" drive and then browsing to the "xampp\htdocs\php\" folder). Open the file "php5.ini"; it should open in Notepad by default.
  9. Go to Edit -> Find and type in "max_execution_time". Change the value from "60″ to "600″ and save the file. Close Notepad.
  10. On the XAMPP Control Panel, click the "Start" buttons beside "Apache" and "MySql". If they start correctly a little green box that says "Running" will appear beside each. If you are asked to unblock them by the Windows Firewall, do so.
  11. Assuming you are allowed to start both of them, try going to http://localhost/ in your web browser. If a page saying "XAMPP" loads then you're good to continue.
  12. Download WordPress 2.5 from http://www.wordpress.org/download/. When it is downloaded, extract the ZIP file to your Destkop. This will create a folder on your desktop called something like "wordpress-2.5.1″ and inside that will be another folder called "wordpress". Move the second folder (the one just called "wordpress") into C:\xampp\htdocs\. (Do this by opening a new "My Computer" window, selecting the "C:" drive and then the browsing to the xampp\htdocs folder. Drag the "wordpress" folder into it.)
  13. Now go into the "wordpress" folder and click on the "wp-config-sample.php" file. When it tells you that it cannot open the file, choose "Select the program from a list" and click "OK". Scroll down and choose "WordPad" and click "OK".
  14. At the very top of this file you will see something that looks like this
    1 define('DB_NAME', 'putyourdbnamehere');    // The name of the database
    2 define('DB_USER', 'usernamehere');     // Your MySQL username
    3 define('DB_PASSWORD', 'yourpasswordhere'); // ...and password
    
    Change this to look like this:
    1 define('DB_NAME', 'wordpress'); // The name of the database
    2 define('DB_USER', 'root'); // Your MySQL username
    3 define('DB_PASSWORD', "); // ...and password
    
    As you can see, you are changing "putyourdbnamehere" to "wordpress", changing "usernamehere" to "root", and changing "yourpasswordhere" to be blank.
  15. Choose File -> Save As in the menu and save the file as "wp-config.php" in the same directory as "wp-config-sample.php".
  16. Download phpMyAdmin from http://www.phpmyadmin.net/home_page/downloads.php. Choose the "all-languages-utf-8-only.zip" option.
  17. Once it has downloaded, you will have a file named something like "phpMyAdmin-2.11.7-rc1-all-languages-utf-8-only.zip". Extract that to your desktop and it will create a folder called "phpMyAdmin-2.11.7-rc1-all-languages-utf-8-only" with another folder of the same name inside of that. Right click on that inner folder and choose "Rename". Then rename the folder to "phpmyadmin" and move it into the C:\xampp\htdocs\ folder just like we moved the "wordpress" folder there.
  18. Now use your web browser to go to http://localhost/phpmyadmin/. If that page loads, we are good to continue.
  19. Look for the "Create new database" option. Fill in "wordpress" in the box and click "Create." It should tell you that "Database wordpress has been created." If so, continue.
  20. Go to http://localhost/wordpress/. We are now at the WordPress installer.
  21. Give your blog a title like "My Xanga" and fill in your email address. Click "Install WordPress".
  22. On the next page right down the username and password that it gives you. Then click "Log In" and log into your WordPress. Leave this window open and we will come back to it.
  23. Now we need to download the Xanga importer for WordPress. The original importer comes from Daniel Kozlowski, but I had to modify it to make comments come in correctly. You can use either version. See update at end of post for the PHP code.
  24. Go back to your WordPress web browser window. Click on "Manage".
  25. Select the one post on that page (entitled "Hello world!") and delete it. Then click on "Import".
  26. Choose "Xanga" (all the way at the bottom).
  27. Click "Browse..." and browse to the "XangaArchive.htm" file we created in step 5. Select it and click "Open". Then click "Upload file and import". This may take a while.
  28. When that page is done loading scroll all the way to the bottom and see if it says "All done." If so, it worked, scroll back to the top and click on "Export". Then select "Download Export File" and save the XML file it gives you.
  29. Now go to your WordPress.com account and log in. Then click on "Manage" and on "Import". Choose "WordPress".
  30. Browse to your WordPress XML file that you just saved and select it. Click "Upload file and import".
  31. After it uploads, tell it what author to map your import author to. Click "Import". There you go, you imported Xanga into WordPress.
  32. Go back to the XAMPP Control Panel and click "Stop" beside "Apache" and "MySql". Then click "Exit". Now you can go to "Add or Remove Programs" in the Control Panel and remove XAMPP.
  33. You can also delete all the files you downloaded, though you may want to keep the Xanga archives around just in case.

You're done! That wasn't so bad, was it? Actually, I'll admit, there are a bunch of places in there that you can get confused. Feel free to contact me or leave a comment if you need help.

Update: Here is the code for the Xanga importer. To install it:

  • Navigate to the C:\xampp\htdocs\wordpress\wp-admin\import folder on your computer.
  • Copy one of the files already there and rename it xanga.php. (If you can't see the ".php" at the end of the file, just rename it "xanga".)
  • Right click on the file and go to "Open With." If Wordpad is not in the list, choose to see more programs and select it from that list.
  • Replace all the text in that file with the code below. Save it. Now continue with Step 24.
  1 <?php
  2 
  3 //Xanga archive importer by Jeremy Jay
  4 //Borrows heavily from the LiveJournal import script.
  5 //
  6 //Modified for current WP (2.1+) release and for current Xanga archive style by Daniel Kozlowski
  7 //
  8 //ATTN:  unhtmlentities() has been disabled due to it's turning all '. ' into '.? '
  9 //       once the post was imported into wordpress 2.1.  Thus, it has been disabled.  If
 10 //       you're using a version of PHP earlier than 4.3, you'll need to un-comment
 11 //       calls to unhtmlentities() in this script.  They are on lines 82 and 120.
 12 //
 13 //ATTN:  I've run into some Xanga archives where the timestamp for a post or comment is
 14 //	    missing a zero.  For example, '12:08' is printed as '12:8'.  This script
 15 //	    cannot read those!  Please read through your code if some of the times are
 16 //       coming up weird once you've imported the archive files.
 17 
 18 class Xanga_Import {
 19 
 20     var $file;
 21 
 22     function header() {
 23         echo '
 24 <div>';
 25         echo '
 26 <h2>'.__('Import Xanga').'</h2>
 27 ';
 28     }
 29 
 30     function footer() {
 31         echo '
 32 ';
 33     }
 34 
 35     function unhtmlentities($string) { // From php.net for < 4.3 compatability
 36         $trans_tbl = get_html_translation_table(HTML_ENTITIES);
 37         $trans_tbl = array_flip($trans_tbl);
 38         return strtr($string, $trans_tbl);
 39     }
 40 
 41     function greet() {
 42         echo '
 43 '.__('Howdy! This importer allows you to extract posts and comments from Xanga Premium Archive files into your blog.  If you do not have Premium but have enough posts to be looking at this, just pay $4 for a month to get the archive and you will at least be supporting Xanga for all the hosting they have done for you.  Pick an archive file to upload and click Import.').'
 44 ';
 45         wp_import_upload_form('admin.php?import=xanga&amp;step=1');
 46     }
 47 
 48     function import_posts() {
 49         global $wpdb, $current_user;
 50 
 51         set_magic_quotes_runtime(0);
 52         $importdata = file($this->file); // Read the file into an array
 53         $importdata = implode('', $importdata); // squish it
 54         $importdata = str_replace(array ('\r\n', '\r'), '\n', $importdata);
 55 
 56         preg_match_all('|
 57 <div>(.*?)<hr size=1 noshade/>(
 58 <div>)*?|is', $importdata, $posts);
 59         $posts = $posts[1];
 60         unset($importdata);
 61         echo '
 62 <ol>';
 63 
 64         foreach ($posts as $post) {
 65             flush();
 66             preg_match('|^(.*?)</ol>
 67 </div>
 68 |is', $post, $post_title);
 69             $post_title = $wpdb->escape(trim($post_title[1]));
 70 
 71             preg_match('/
 72 <div>Posted (.*?)\/(.*?)\/(.*?) at (.*?) (.*?)< \/div>/is', $post, $match);
 73             list($hour,$min) = explode(':',$match[4]);
 74             switch($match[5]) {
 75             	case 'AM' : if($hour == 12) $hour = 0; break;
 76             	case 'PM': if($hour < 12) $hour += 12; break;
 77             	}
 78             $match[4] = '$hour:$min';
 79             $post_date = '$match[3]-$match[1]-$match[2] $match[4]:00';
 80             echo 'Date: $post_date';
 81 
 82             $com = split(' ', $post_title);
 83 
 84             if( $com[1]=='Comments' ) {
 85                 preg_match_all('|
 86 <div>
 87 <div>(.*?)</div>
 88 </div>
 89 |is', $post, $comments);
 90                 $comments = $comments[1];
 91 
 92                 $comment_post_ID = $post_id;
 93                 $num_comments = 0;
 94                 foreach ($comments as $comment) {
 95                     preg_match('|^(.*?)</div>
 96 <div>|is', $comment, $comment_content);
 97                     $comment_content = str_replace(array ('< ![CDATA[', ']]>'), '', trim($comment_content[1]));
 98                     //$comment_content = $this->unhtmlentities($comment_content);
 99 
100                     // Clean up content
101                     $comment_content = preg_replace('|< (/?[A-Z]+)|e', ''<' . strtolower('$1')', $comment_content);
102                     $comment_content = str_replace('
103 ', '
104 ', $comment_content);
105                     $comment_content = str_replace('<hr>', '<hr />', $comment_content);
106                     $comment_content = $wpdb->escape($comment_content);
107 
108                     preg_match('/
109 <div>Posted (.*?)\/(.*?)\/(.*?) at (.*?) (.*?) by/i', $comment, $match);
110                     list($hour,$min) = explode(':',$match[4]);
111             		switch($match[5]) {
112             		case 'AM' : if($hour == 12) $hour = 0; break;
113             		case 'PM': if($hour < 12) $hour += 12; break;
114             			}
115           	  	$match[4] = '$hour:$min';
116             	     $comment_date = '$match[3]-$match[1]-$match[2] $match[4]:00';
117 
118                   if (preg_match('|<a href='http://www\.xanga\.com/home\.aspx\?user=|', $comment) == 1)
119 			{
120 				preg_match('|<a href='http://www\.xanga\.com/home\.aspx\?user=(.*?)'>(.*?)|i', $comment, $comment_author);
121 			}
122 			else
123 			{
124 				preg_match('|<a href='http://www\.xanga\.com/(.*?)'>(.*?)</a>|i', $comment, $comment_author);
125 			}
126 
127                     $comment_author = $wpdb->escape(trim($comment_author[1]));
128                     $comment_author_url = 'http://www.xanga.com/home.aspx?user='.$comment_author;
129 
130                     $comment_approved = 1;
131                     // Check if it's already there
132                     if (!comment_exists($comment_author, $comment_date)) {
133                         $commentdata = compact('comment_post_ID', 'comment_author', 'comment_author_url', 'comment_date', 'comment_content', 'comment_approved');
134                         $commentdata = wp_filter_comment($commentdata);
135                         wp_insert_comment($commentdata);
136                         $num_comments++;
137                     }
138                 }
139                 if ( $num_comments ) {
140                     echo ' ';
141                     printf(__('(%s comments)'), $num_comments);
142                 }
143             } else {
144 
145                 preg_match('|
146 <td style='padding-left:20; padding-bottom:20'>(.*?)
147 <div>Posted (\d{1,2}/\d{1,2}/\d{4}) at (.*?)< \/div>|is', $post, $post_content);
148                 $post_content = str_replace(array ('< ![CDATA[', ']]>'), '', trim($post_content[1]));
149                 //$post_content = $this->unhtmlentities($post_content);
150 
151                 // Clean up content
152                 $post_content = preg_replace('|< (/?[A-Z]+)|e', ''<' . strtolower('$1')', $post_content);
153                 $post_content = str_replace('
154 ', '
155 ', $post_content);
156                 $post_content = str_replace('<hr>', '<hr />', $post_content);
157 
158                 //Xanga archives have some pretty crappy formatting, so this reduces the string to a single line.
159                 //THIS WILL NOT REMOVE YOUR OWN FORMATTING.  Any formatting changes you created in your posts
160                 //are tagged, and thus will not be affected by the removal.
161                 $post_content = str_replace('\n', ' ', $post_content);
162                 $post_content = $wpdb->escape($post_content);
163 
164                 $post_author = $current_user->ID;
165                 $post_status = 'publish';
166 
167                 echo '
168 	<li>';
169                 if ($post_id = post_exists($post_title, $post_content, $post_date)) {
170                     printf(__('Post <i>%s</i> already exists.'), stripslashes($post_title));
171                 } else {
172                     printf(__('Importing post <i>%s</i>...'), stripslashes($post_title));
173                     $postdata = compact('post_author', 'post_date', 'post_content', 'post_title', 'post_status');
174                     $post_id = wp_insert_post($postdata);
175                     if (!$post_id) {
176                         _e('Couldn't get post ID');
177                         echo '</li>
178 ';
179                         break;
180                     }
181                 }
182             }
183 
184             echo '
185 ';
186             flush();
187             ob_flush();
188         }
189         echo '
190 ';
191     }
192 
193     function import() {
194         $file = wp_import_handle_upload();
195         if ( isset($file['error']) ) {
196             echo $file['error'];
197             return;
198         }
199 
200         $this->file = $file['file'];
201         $this->import_posts();
202         wp_import_cleanup($file['id']);
203 
204         echo '
205 <h3>';
206         printf(__('All done. <a href='%s'>Have fun!</a>'), get_option('home'));
207         echo '</h3>
208 ';
209     }
210 
211     function dispatch() {
212         if (empty ($_GET['step']))
213             $step = 0;
214         else
215             $step = (int) $_GET['step'];
216 
217         $this->header();
218 
219         switch ($step) {
220             case 0 :
221                 $this->greet();
222                 break;
223             case 1 :
224                 $this->import();
225                 break;
226         }
227 
228         $this->footer();
229     }
230 
231     function Xanga_Import() {
232         // Nothing.
233     }
234 }
235 
236 $xanga_import = new Xanga_Import();
237 
238 register_importer('xanga', 'Xanga', __('Import posts from Xanga Archives'), array ($xanga_import, 'dispatch'));
239 ?>