一个博客搬家项目

可用来练手,是java代码,把sina博客搬家到csdn,因为上次发现sina博客有字数限制,真蛋疼。
http://blog.csdn.net/telnetor/article/details/5556539 

1.CSDN的博客使用的是MetaWeblog Api,可以使用xml-rpc进行操作。下面的代码演示了怎样使用api发布博客

[java]  view plain copy
  1. package cn.mingyuan.baidu2csdn.core;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4. import java.net.MalformedURLException;  
  5. import java.net.URL;  
  6. import java.util.Date;  
  7. import java.util.HashMap;  
  8. import java.util.Map;  
  9. import org.apache.xmlrpc.XmlRpcException;  
  10. import org.apache.xmlrpc.client.XmlRpcClient;  
  11. import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;  
  12. /** 
  13.  * csdn博文 
  14.  * @author [email protected] 
  15.  * 
  16.  */  
  17. public class CSDNPost {  
  18.     /** 
  19.      * 博文创建日期 
  20.      */  
  21.     private Date dateCreated;  
  22.     /** 
  23.      * 博文内容 
  24.      */  
  25.     private String description;  
  26.     /** 
  27.      * 标题 
  28.      */  
  29.     private String title;  
  30.     /** 
  31.      * 博文分类 
  32.      */  
  33.     private String[] categories;  
  34.     public CSDNPost(){  
  35.           
  36.     }  
  37.     public CSDNPost(String title, String description, String[] categories, Date dateCreated) {  
  38.         this.dateCreated = dateCreated;  
  39.         this.description = description;  
  40.         this.title = title;  
  41.         this.categories = categories;  
  42.     }  
  43.     public Date getDateCreated() {  
  44.         return dateCreated;  
  45.     }  
  46.     public void setDateCreated(Date dateCreated) {  
  47.         this.dateCreated = dateCreated;  
  48.     }  
  49.     public String getDescription() {  
  50.         return description;  
  51.     }  
  52.     public void setDescription(String description) {  
  53.         this.description = description;  
  54.     }  
  55.     public String getTitle() {  
  56.         return title;  
  57.     }  
  58.     public void setTitle(String title) {  
  59.         this.title = title;  
  60.     }  
  61.     public String[] getCategories() {  
  62.         return categories;  
  63.     }  
  64.     public void setCategories(String[] categories) {  
  65.         this.categories = categories;  
  66.     }  
  67.     /** 
  68.      * xml-rpc配置 
  69.      */  
  70.     private static XmlRpcClientConfigImpl config;  
  71.     /** 
  72.      * xml-rpcClient 
  73.      */  
  74.     private static XmlRpcClient client;  
  75.       
  76.     static{  
  77.         config = new XmlRpcClientConfigImpl();  
  78.         try {  
  79.             //此处请将telnetor替换为您的用户名  
  80.             config.setServerURL(new URL("http://blog.csdn.net/telnetor/services/metablogapi.aspx"));  
  81.         } catch (MalformedURLException e) {  
  82.             System.out.println("请检查url");  
  83.         }  
  84.         client = new XmlRpcClient();  
  85.         client.setConfig(config);  
  86.     }  
  87.       
  88.     /** 
  89.      * 日志记录 
  90.      * @param log log 
  91.      */  
  92.     private void writelog(String log){  
  93.         FileOutputStream fos=null;  
  94.         try {  
  95.             fos=new FileOutputStream("post.log",true);  
  96.             fos.write((log+"/r/n").getBytes());  
  97.             fos.flush();  
  98.             fos.close();  
  99.         } catch (IOException e) {  
  100.             System.out.println("写入日志错误:"+log);  
  101.         }  
  102.     }  
  103.     /** 
  104.      * 发布 
  105.      */  
  106.     public void publish(){  
  107.         Map<String, Object> struct = new HashMap<String, Object>();  
  108.         struct.put("dateCreated", dateCreated);  
  109.         struct.put("description", description);  
  110.         struct.put("title", title);  
  111.         struct.put("categories", categories);  
  112.         Object[] params = new Object[] { "your usrname""replace it with your username""replace it with your password", struct, true };  
  113.         String blogid = null;  
  114.         try {  
  115.             blogid = (String) client.execute("metaWeblog.newPost", params);  
  116.         } catch (XmlRpcException e) {  
  117.             writelog("导入出现错误:title="+title);  
  118.             System.out.println("导入出现错误:title="+title);  
  119.         }  
  120.         writelog(title + ">> 导入完毕,生成博文id为>>" + blogid);  
  121.         System.out.println(title + ">> 导入完毕,生成博文id为>>" + blogid);  
  122.         struct.clear();  
  123.     }  
  124.     public static void main(String[] args){  
  125.         CSDNPost post=new CSDNPost();  
  126.         post.publish();  
  127.     }  
  128. }  
 

其中需要注意的是categories是一个数组。

2.知道了怎样使用api发布博客之后,我们就该进行下一步:读取百度空间的博文内容了。首先定义一个BaiduHi的class,用来存放从百度博客读取出来的数据。

[java]  view plain copy
  1. package cn.mingyuan.baidu2csdn.core;  
  2. import java.util.Date;  
  3. /** 
  4.  * 百度博客 
  5.  * @author [email protected] 
  6.  * 
  7.  */  
  8. public class BaiduHi {  
  9.     /** 
  10.      * 标题 
  11.      */  
  12.     private String title;  
  13.     /** 
  14.      * 内容 
  15.      */  
  16.     private String description;  
  17.     /** 
  18.      * 分类 
  19.      */  
  20.     private String categories;  
  21.     /** 
  22.      * 发布日期 
  23.      */  
  24.     private Date dateCreated;  
  25.     public String getTitle() {  
  26.         return title;  
  27.     }  
  28.     public String getDescription() {  
  29.         return description;  
  30.     }  
  31.     public String getCategories() {  
  32.         return categories;  
  33.     }  
  34.     public Date getDateCreated() {  
  35.         return dateCreated;  
  36.     }  
  37.     public void setTitle(String title) {  
  38.         this.title = title;  
  39.     }  
  40.     public void setDescription(String description) {  
  41.         this.description = description;  
  42.     }  
  43.     public void setCategories(String categories) {  
  44.         this.categories = categories;  
  45.     }  
  46.     public void setDateCreated(Date dateCreated) {  
  47.         this.dateCreated = dateCreated;  
  48.     }  
  49.     public BaiduHi(String title, String description, String categories, Date dateCreated) {  
  50.         this.title = title;  
  51.         this.description = description;  
  52.         this.categories = categories;  
  53.         this.dateCreated = dateCreated;  
  54.     }  
  55.     public BaiduHi() {  
  56.         // TODO Auto-generated constructor stub  
  57.     }  
  58.     /** 
  59.      * @param args 
  60.      */  
  61.     public static void main(String[] args) {  
  62.         // TODO Auto-generated method stub  
  63.     }  
  64. }  
 

3.读取百度空间博文内容,注意:我们使用的是游客权限来读取百度空间的内容,如果有私密信息,需要将其公开之后程序才能读取。另外我使用的百度空间模板为“80后青春”,如果您使用的不是此模板,html源码可能会不同,解析就可能失败。如果使用本程序请保持和我的模板一致。板式为空间装扮-板式-显示出来的第二行第二列那个

[java]  view plain copy
  1. package cn.mingyuan.baidu2csdn.core;  
  2. import java.io.BufferedReader;  
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.InputStreamReader;  
  6. import java.net.MalformedURLException;  
  7. import java.net.URL;  
  8. import java.net.URLConnection;  
  9. import java.util.ArrayList;  
  10. import java.util.Date;  
  11. import java.util.List;  
  12. import java.util.Stack;  
  13. import java.util.regex.Matcher;  
  14. import java.util.regex.Pattern;  
  15. /** 
  16.  * 百度博客数据抓取及解析 
  17.  * @author [email protected] 
  18.  * 
  19.  */  
  20. public class BaiduHiFetcher {  
  21.     /** 
  22.      * 下载页面 
  23.      * @param url url 
  24.      * @return 网页源码 
  25.      */  
  26.     private String downloadPage(String url) {  
  27.         URLConnection conn;  
  28.         InputStream in;  
  29.         BufferedReader reader = null;  
  30.         StringBuilder sb = new StringBuilder();  
  31.         String line = null;  
  32.         try {  
  33.             conn = new URL(url).openConnection();  
  34.             in = conn.getInputStream();  
  35.             reader = new BufferedReader(new InputStreamReader(in, "gb2312"));  
  36.             while ((line = reader.readLine()) != null) {  
  37.                 sb.append(line);  
  38.             }  
  39.             in.close();  
  40.             reader.close();  
  41.         } catch (MalformedURLException e) {  
  42.             System.out.println("请检查url是否规范");  
  43.         } catch (IOException e) {  
  44.             System.out.println("读取源码错误:"+url);  
  45.         }  
  46.         return sb.toString();  
  47.     }  
  48.     /** 
  49.      * 获取页面博文链接 
  50.      * @param html 网页源码 
  51.      * @return 页面中的博文链接 
  52.      */  
  53.     private List<String> getPostLinks(String html) {  
  54.         // 分析页面内容,取得页面中的文章链接  
  55.         String titleDivRegex = "<div[//s]class=/"tit/"><a[//s]href=[^<>]+?target=/"_blank/">.+?</div>";  
  56.         Pattern titleDivPattern = Pattern.compile(titleDivRegex);  
  57.         Matcher titleDivMatcher = titleDivPattern.matcher(html);  
  58.         List<String> posts = new ArrayList<String>();  
  59.         while (titleDivMatcher.find()) {  
  60.             String div = titleDivMatcher.group();  
  61.             String titleUrl = div.substring(div.indexOf("/"), div.indexOf("/" target"));  
  62.             posts.add("http://hi.baidu.com" + titleUrl);  
  63.         }  
  64.         return posts;  
  65.     }  
  66.     /** 
  67.      * <p>获取博客总页数 
  68.      * <br>我的博客内容有16页,有上一页,下一页,尾页等这样的标志,如果博文少的话可能这些标志不会出现,请修改此方法 
  69.      * @param html 源码(最好是第一页) 
  70.      * @return 博客总页数 
  71.      */  
  72.     private int getTotalPages(String html) {  
  73.         // 页码  
  74.         // <a href="/cnjsp/blog/index/16" mce_href="cnjsp/blog/index/16">[尾页]</a>  
  75.         String pageRegex = "<a[//s]href=/"/cnjsp/blog/index/[//d][//d]/">//[尾页//]</a>";  
  76.         Pattern pagePattern = Pattern.compile(pageRegex);  
  77.         Matcher pageMatcher = pagePattern.matcher(html);  
  78.         String totalPagesStr = null;  
  79.         int pages = 0;  
  80.         if (pageMatcher.find()) {  
  81.             String pagelink = pageMatcher.group();  
  82.             totalPagesStr = pagelink.replaceAll("<a[//s]href=/"/cnjsp/blog/index/", "").replaceAll("/">//[尾页//]</a>""");  
  83.             pages = Integer.parseInt(totalPagesStr);  
  84.         }  
  85.         return pages;  
  86.     }  
  87.     /** 
  88.      * <p>获取博客的所有博文的地址 
  89.      * <br>没有对url进行编码处理,如果博客地址含中文,请对url进行处理 
  90.      * @param blogUrl 博客地址 
  91.      * @return 所有博文地址,存放于栈中,使用的时候请使用pop方法取出元素,这样可以保证按照最先发表的博文最先处理 
  92.      */  
  93.     public Stack<String> getAllPostLink(String blogUrl){  
  94.         Stack<String> posts = new Stack<String>();  
  95.         // 1.下载第一页  
  96.         String firstPageHtml = downloadPage(blogUrl + "/blog/index/0");  
  97.         // 2.获取博文总页数  
  98.         int totalPages = getTotalPages(firstPageHtml);  
  99.         // 3.下载各摘要页  
  100.         posts.addAll(getPostLinks(firstPageHtml));  
  101.         if (totalPages < 1) {  
  102.             return posts;  
  103.         }  
  104.         for (int i = 1; i <= totalPages; i++) {  
  105.             String page = downloadPage(blogUrl + "/blog/index/" + i);  
  106.             posts.addAll(getPostLinks(page));  
  107.         }  
  108.         return posts;  
  109.     }  
  110.     /** 
  111.      * 解析博文,获取标题,发布时间,内容,分类等信息 
  112.      * @param postUrl 博文地址 
  113.      * @return 封装了博文信息的BaiduHi 
  114.      */  
  115.     public BaiduHi getBaiduHi(String postUrl){  
  116.         String html = downloadPage(postUrl);  
  117.         // /<div class="tit">  
  118.         String titleDivRegex = "<div[//s]id=/"m_blog/"[//s]class=/"modbox/"[//s]style=/"overflow-x:hidden;/"><div[//s]class=/"tit/">.+?</div><div[//s]class=/"date/">";  
  119.         Pattern titleDivPattern = Pattern.compile(titleDivRegex);  
  120.         Matcher titleDivMatcher = titleDivPattern.matcher(html);  
  121.         String title = null;  
  122.         if (titleDivMatcher.find()) {  
  123.             title = titleDivMatcher.group().replaceAll("<div[//s]id=/"m_blog/"[//s]class=/"modbox/"[//s]style=/"overflow-x:hidden;/"><div[//s]class=/"tit/">""").replaceAll("</div><div[//s]class=/"date/">""").trim();  
  124.         }  
  125.         String dateDivRegex = "<div[//s]class=/"date/">.+?</div>";  
  126.         Pattern dateDivPattern = Pattern.compile(dateDivRegex);  
  127.         Matcher dateMatcher = dateDivPattern.matcher(html);  
  128.         String dateStr = null;  
  129.         Date postDate = null;  
  130.         if (dateMatcher.find()) {  
  131.             dateStr = dateMatcher.group().replaceAll("<div[//s]class=/"date/">""").replaceAll("</div>""").trim();  
  132.             postDate = getDate(dateStr);  
  133.         }  
  134.         String textDivRegex = "<div[//s]id=/"blog_text/"[//s]class=/"cnt/"[//s]+>.+?</div>";  
  135.         Pattern textDivPattern = Pattern.compile(textDivRegex);  
  136.         Matcher textMatcher = textDivPattern.matcher(html);  
  137.         String text = null;  
  138.         if (textMatcher.find()) {  
  139.             text = textMatcher.group().replaceAll("<div[//s]id=/"blog_text/"[//s]class=/"cnt/"[//s]+>""").replaceAll("</div>""").trim();  
  140.         }  
  141.         String categoriesRegex = "title=/"查看该分类中所有文章/">类别:.+?</a>";  
  142.         Pattern categoriesDivPattern = Pattern.compile(categoriesRegex);  
  143.         Matcher categoriesMatcher = categoriesDivPattern.matcher(html);  
  144.         String categories = null;  
  145.         if (categoriesMatcher.find()) {  
  146.             categories = categoriesMatcher.group().replaceAll("title=/"查看该分类中所有文章/">类别:""").replaceAll("</a>""").trim();  
  147.         }  
  148.         BaiduHi hi = new BaiduHi();  
  149.         hi.setTitle(title);  
  150.         hi.setDescription(text);  
  151.         hi.setCategories(categories);  
  152.         hi.setDateCreated(postDate);  
  153.         return hi;  
  154.     }  
  155.     /** 
  156.      * 解析博文中的日期格式返回Date类型 
  157.      * @param str 博文中的日期 
  158.      * @return Date类型日期 
  159.      */  
  160.     @SuppressWarnings("deprecation")  
  161.     private Date getDate(String str) {  
  162.         String yearStr = str.substring(0, str.indexOf("年")).trim();  
  163.         String monthStr = str.substring(str.indexOf("年"), str.indexOf("月")).replace("年""").trim();  
  164.         String dayStr = str.substring(str.indexOf("月"), str.indexOf("日")).replace("月""").trim();  
  165.         String timeStr = str.substring(str.indexOf("午")).replace("午""").trim();  
  166.         String hourStr = timeStr.split(":")[0];  
  167.         String minutesStr = timeStr.split(":")[1];  
  168.         Date date = new Date();  
  169.         date.setYear(Integer.parseInt(yearStr) - 1900);  
  170.         date.setMonth(Integer.parseInt(monthStr) - 1);  
  171.         date.setDate(Integer.parseInt(dayStr));  
  172.         if (str.contains("下午")) {  
  173.             date.setHours(Integer.parseInt(hourStr) + 12);  
  174.         } else {  
  175.             date.setHours(Integer.parseInt(hourStr));  
  176.         }  
  177.         date.setMinutes(Integer.parseInt(minutesStr));  
  178.         return date;  
  179.     }  
  180. }  
 

4.我们现在完成了写CSDN博客与读取并解析百度空间博文的工作。接下来需要把它们连起来,完成导入

[java]  view plain copy
  1. package cn.mingyuan.baidu2csdn.core;  
  2. import java.util.Stack;  
  3. /** 
  4.  * 搬家 
  5.  * @author [email protected] 
  6.  * 
  7.  */  
  8. public class Transfer {  
  9.     /** 
  10.      * @param args 
  11.      */  
  12.     public static void main(String[] args) {  
  13.         // TODO Auto-generated method stub  
  14.         String postUrl = "http://hi.baidu.com/cnjsp";  
  15.         BaiduHiFetcher fetcher = new BaiduHiFetcher();  
  16.         Stack<String> urls = null;  
  17.         urls = fetcher.getAllPostLink(postUrl);  
  18.         while (!urls.isEmpty()) {  
  19.             String url = urls.pop();  
  20.             BaiduHi hi = null;  
  21.             hi = fetcher.getBaiduHi(url);  
  22.             CSDNPost post = new CSDNPost();  
  23.             post.setTitle(hi.getTitle());  
  24.             post.setDescription(hi.getDescription());  
  25.             post.setCategories(new String[] { hi.getCategories() });  
  26.             post.setDateCreated(hi.getDateCreated());  
  27.             post.publish();  
  28.             try {  
  29.                 Thread.sleep(5 * 1000);  
  30.             } catch (InterruptedException e) {  
  31.                 System.out.println("休眠出错");  
  32.             }  
  33.         }  
  34.     }  
  35. }  
 

5.至此如果一切顺利的话您的博文应该已经全部导入到CSDN博客了,如果有一些小问题,如网络超时,操作太频繁并CSDN暂时封锁的话,在再次执行导入之前就需要将原来导入的数据删除,或者是跳过才能保证CSDN博文不会重复。下面是一个删除CSDN已有博文的方法:

[java]  view plain copy
  1. package cn.mingyuan.baidu2csdn.core;  
  2. import java.io.BufferedReader;  
  3. import java.io.FileInputStream;  
  4. import java.io.FileNotFoundException;  
  5. import java.io.IOException;  
  6. import java.io.InputStreamReader;  
  7. import java.net.MalformedURLException;  
  8. import java.net.URL;  
  9. import org.apache.xmlrpc.XmlRpcException;  
  10. import org.apache.xmlrpc.client.XmlRpcClient;  
  11. import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;  
  12. public class DeletePostById {  
  13.     private static XmlRpcClientConfigImpl config;  
  14.     private static XmlRpcClient client;  
  15.     static{  
  16.         config = new XmlRpcClientConfigImpl();  
  17.         try {  
  18.             config.setServerURL(new URL("http://blog.csdn.net/telnetor/services/metablogapi.aspx"));  
  19.         } catch (MalformedURLException e) {  
  20.             System.out.println("请检查url");  
  21.         }  
  22.         client = new XmlRpcClient();  
  23.         client.setConfig(config);  
  24.     }  
  25.     /** 
  26.      * 删除帖子 
  27.      * @param appkey appkey,可以任意,这是一个忽略的值 
  28.      * @param postid 帖子id 
  29.      * @param username 用户名 
  30.      * @param password 密码 
  31.      * @param publish 博客在帖子被删除之后是否重新发布 
  32.      */  
  33.     public static void delete(String appkey,String postid,String username,String password,boolean publish){  
  34.       
  35.         Object[] params = new Object[] { "ignored value", postid,username,password, true };  
  36.         try {  
  37.             client.execute("blogger.deletePost", params);  
  38.         } catch (XmlRpcException e) {  
  39.             System.out.println("删除出错,postid="+postid);  
  40.         }  
  41.         System.out.println(postid+"删除完毕");  
  42.           
  43.     }  
  44.     /** 
  45.      * @param args 
  46.      * @throws InterruptedException  
  47.      */  
  48.     public static void main(String[] args) throws InterruptedException {  
  49.         BufferedReader reader = null;  
  50.         String line;  
  51.         try {  
  52.             reader = new BufferedReader(new InputStreamReader(new FileInputStream("content")));  
  53.             while((line=reader.readLine())!=null){  
  54.                 line=line.split("生成博文id为:")[1];  
  55.                 delete("ignored",line,"your username","your password",true);  
  56.                 Thread.sleep(1000*10);  
  57.             }  
  58.         } catch (FileNotFoundException e1) {  
  59.             System.out.println("文件没找到");  
  60.         } catch (IOException e) {  
  61.             System.out.println("读取文件失败");  
  62.         }  
  63.           
  64.     }  
  65. }  
 

6.好了,经过耐心操作我们的导入工作应该已经完成了。

本程序有不足的地方:

  • 不支持图片导入
  • 只是针对笔者自己博客量身定做的程序,里面许多情况如url中包含汉字,模板的通用性等问题没有加以考虑,在使用的时候应该根据实际情况加以修正
  • 有少数博客导入失败,笔者130多篇博客有4篇导入失败,不过由于比较少,可以用手动发帖来弥补
  • 速度问题 由于CSDN会短暂屏蔽频繁的访问,所以本程序在导入一篇博客后采取了休眠5秒钟的方法,这样会降低速度。另外如果百度上面的博文过多的话建议采集百度数据的时候也加上休眠时间,防止封锁

7.使用到的包为xml-rpc,您可以在这里下载到最新版本。CSDN博客API在这里可以找到。程序中涉及的发帖,删帖所需的参数均在api中有详细说明


你可能感兴趣的:(一个博客搬家项目)