本程序可以完成的工作:转移csdn上面的文章(限于文本内容)到wordpress;不能完成的工作:1、不支持在wordpress上创建分类,所以需要提前在wordpress上手工创建分类(保持与csdn一致);2、不能以很好的格式转移文章,转移之后文章格式需要调整。

 

程序由采集、解析、发帖三部分构成。采集负责将指定url的内容下载下来,解析负责从网页内容中解析出正文链接、标题、发布时间、分类信息,发帖部分负责将解析出来的数据通过rpc发送给wordpress,生成博文。

 

本程序用到的jar包及其版本如下:

-rw-r--r-- 1 mingyuan mingyuan  46725 2011-09-03 23:05 commons-codec-1.3.jar
-rw-r--r-- 1 mingyuan mingyuan 279781 2011-09-03 23:05 commons-httpclient-3.0.1.jar
-rwxrwxrwx 1 mingyuan mingyuan  52915 2010-05-03 03:39 commons-logging-1.1.jar
-rw-r--r-- 1 mingyuan mingyuan 281579 2011-09-04 01:40 jsoup-1.6.1.jar
-rwxrwxrwx 1 mingyuan mingyuan  34407 2010-05-03 03:39 ws-commons-util-1.0.2.jar
-rwxrwxrwx 1 mingyuan mingyuan  58573 2010-05-03 03:39 xmlrpc-client-3.1.3.jar
-rwxrwxrwx 1 mingyuan mingyuan 109131 2010-05-03 03:39 xmlrpc-common-3.1.3.jar
-rwxrwxrwx 1 mingyuan mingyuan  81555 2010-05-03 03:39 xmlrpc-server-3.1.3.jar

 

代码很简单,就不解释了,大伙看看即可明白。程序的入口函数是Mover.main

 

下面先给出主要的类Mover.java

  1. package cn.mingyuan.csdn2wordpress;
  2.  
  3. import java.io.IOException;
  4. import java.net.MalformedURLException;
  5. import java.net.URL;
  6. import java.text.ParseException;
  7. import java.text.SimpleDateFormat;
  8. import java.util.Date;
  9. import java.util.HashMap;
  10. import java.util.LinkedList;
  11. import java.util.List;
  12. import java.util.Map;
  13. import java.util.concurrent.TimeUnit;
  14.  
  15. import org.apache.xmlrpc.XmlRpcException;
  16. import org.apache.xmlrpc.client.XmlRpcClient;
  17. import org.apache.xmlrpc.client.XmlRpcClientConfigImpl;
  18. import org.jsoup.Jsoup;
  19. import org.jsoup.nodes.Document;
  20. import org.jsoup.nodes.Element;
  21. import org.jsoup.select.Elements;
  22.  
  23. /**
  24.  * 采集、解析、转移
  25.  * 
  26.  * @author mingyuan
  27.  * 
  28.  */
  29. public class Mover {
  30.     private int totalPages;
  31.     private XmlRpcClientConfigImpl config;
  32.     private XmlRpcClient client;
  33.     private String baseUrl;
  34.     private Object userName;
  35.     private Object password;
  36.     private String csdnUserName;
  37.  
  38.     public Mover(int totalPages, String blogRpcUrl, String csdnUrl, String csdnUserName, String userName,
  39.             String password) {
  40.         this.totalPages = totalPages;
  41.         this.baseUrl = csdnUrl;
  42.         this.csdnUserName = csdnUserName;
  43.         this.userName = userName;
  44.         this.password = password;
  45.         config = new XmlRpcClientConfigImpl();
  46.         try {
  47.             config.setServerURL(new URL(blogRpcUrl));
  48.         } catch (MalformedURLException e) {
  49.             System.out.println(“请检查url”);
  50.         }
  51.         client = new XmlRpcClient();
  52.         client.setConfig(config);
  53.     }
  54.  
  55.     private List<String> getlinks() {
  56.         List<String> list = new LinkedList<String>();
  57.         for (int i = 1; i <= totalPages; i++) {
  58.             System.out.println(“processing page ” + i);
  59.             Downloader downloader = new Downloader();
  60.             String content = downloader.download(baseUrl + “/” + csdnUserName + “/article/list/” + i);
  61.             if (content == null)
  62.                 continue;
  63.             Document doc = Jsoup.parse(content);
  64.             Elements first = doc.select(“.link_title”);
  65.             for (int j = 0; j < first.size(); j++) {
  66.                 Element first2 = first.get(j).select(“a”).first();
  67.                 String link = baseUrl + first2.attr(“href”);
  68.                 list.add(link);
  69.                 System.out.println(“get link\t” + link);
  70.             }
  71.             System.out.println(“page ” + i + “ extractor done,sleep 2s”);
  72.             try {
  73.                 TimeUnit.SECONDS.sleep(1);
  74.             } catch (InterruptedException e) {
  75.                 e.printStackTrace();
  76.             }
  77.         }
  78.         return list;
  79.     }
  80.  
  81.     public List<CSDNPost> getPosts() {
  82.         List<String> links = getlinks();
  83.         List<CSDNPost> posts = new LinkedList<CSDNPost>();
  84.         for (String link : links) {
  85.             CSDNPost post = getPost(link);
  86.             if (post != null) {
  87.                 posts.add(post);
  88.             }
  89.         }
  90.         return posts;
  91.     }
  92.  
  93.     private CSDNPost getPost(String url) {
  94.         System.out.println(“url\t” + url);
  95.         Downloader downloader = new Downloader();
  96.         String html = downloader.download(url);
  97.         if (html == null)
  98.             return null;
  99.         Document doc = Jsoup.parse(html);
  100.         String title = doc.select(“.article_title”).first().text();
  101.         String categroy = “Uncategorized”;
  102.         Elements link_categories = doc.select(“.article_manage .link_categories”);
  103.         if (link_categories != null) {
  104.             Element first = link_categories.first();
  105.             if (first != null) {
  106.                 Elements href = first.select(“a”);
  107.                 if (href != null) {
  108.                     categroy = href.text();
  109.                 }
  110.             }
  111.         }
  112.         String postdate = doc.select(“.article_manage .link_postdate”).first().text();
  113.         String content = doc.select(“.details .article_content”).first().text();
  114.         SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm”);
  115.         CSDNPost post = new CSDNPost();
  116.         post.setCategories(new String[] { categroy });
  117.         post.setTitle(title);
  118.         try {
  119.             post.setDateCreated(sdf.parse(postdate));
  120.         } catch (ParseException e) {
  121.             post.setDateCreated(new Date());
  122.         }
  123.         post.setDescription(content);
  124.         return post;
  125.     }
  126.  
  127.     public void publish(CSDNPost post) {
  128.         Map<String, Object> struct = new HashMap<String, Object>();
  129.         struct.put(“dateCreated”, post.getDateCreated());
  130.         struct.put(“description”, post.getDescription());
  131.         struct.put(“title”, post.getTitle());
  132.         struct.put(“categories”, post.getCategories());
  133.         Object[] params = new Object[] { userName, userName, password, struct, true };
  134.         String blogid = null;
  135.         try {
  136.             blogid = (String) client.execute(“metaWeblog.newPost”, params);
  137.         } catch (XmlRpcException e) {
  138.             e.printStackTrace();
  139.             System.out.println(“导入出现错误:title=” + post.getTitle());
  140.         }
  141.         System.out.println(post.getTitle() + “>> 导入完毕,生成博文id为>>” + blogid);
  142.         struct.clear();
  143.     }
  144.  
  145.     public static void main(String[] args) throws IOException {
  146.         Mover extractor = new Mover(19, “http://youthmemo.com/xmlrpc.php”, “http://blog.csdn.net”, “telnetor”, “admin”,
  147.                 “xxxx”);
  148.         List<CSDNPost> posts = extractor.getPosts();
  149.         for (CSDNPost post : posts) {
  150.             extractor.publish(post);
  151.             try {
  152.                 TimeUnit.SECONDS.sleep(1);
  153.             } catch (InterruptedException e) {
  154.                 e.printStackTrace();
  155.             }
  156.             System.out.println(post.getTitle());
  157.         }
  158.         System.out.println(“done!”);
  159.     }
  160. }

 

下面给出下载类Downloader.java

  1. package cn.mingyuan.csdn2wordpress;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStreamReader;
  6.  
  7. import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
  8. import org.apache.commons.httpclient.HttpClient;
  9. import org.apache.commons.httpclient.HttpException;
  10. import org.apache.commons.httpclient.HttpMethod;
  11. import org.apache.commons.httpclient.HttpStatus;
  12. import org.apache.commons.httpclient.cookie.CookiePolicy;
  13. import org.apache.commons.httpclient.methods.GetMethod;
  14. import org.apache.commons.httpclient.params.HttpClientParams;
  15. import org.apache.commons.httpclient.params.HttpMethodParams;
  16.  
  17. /**
  18.  * downloader
  19.  * 
  20.  * @author mingyuan
  21.  * 
  22.  */
  23. public class Downloader {
  24.     private HttpClientParams params = null;
  25.     private HttpClient client = null;
  26.  
  27.     /**
  28.      * 默认构造函数,初始化一系列变量
  29.      */
  30.     public Downloader() {
  31.         // 构造HttpClientParams参数
  32.         params = new HttpClientParams();
  33.         params.setParameter(
  34.                 HttpClientParams.USER_AGENT,
  35.                 “Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3 GTBDFff GTB7.0 (.NET CLR 3.5.30729)”);
  36.         params.setParameter(HttpClientParams.ALLOW_CIRCULAR_REDIRECTS, false);
  37.         params.setParameter(HttpClientParams.MAX_REDIRECTS, 4);
  38.         params.setParameter(HttpClientParams.CONNECTION_MANAGER_TIMEOUT, (long) 60 * 1000);
  39.         params.setParameter(HttpClientParams.SO_TIMEOUT, 60 * 1000);
  40.         // 使用系统提供的默认的恢复策略
  41.         params.setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
  42.         client = new HttpClient(params);
  43.     }
  44.  
  45.     /**
  46.      * 下载网页
  47.      * 
  48.      * @param url
  49.      *            网页url
  50.      * @return String类型的网页源码
  51.      */
  52.     public String download(String url) {
  53.         HttpMethod method = new GetMethod(url);
  54.         String sourceCode = null;
  55.         method.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY);
  56.         // 读取内容
  57.         StringBuilder builder = new StringBuilder();
  58.         BufferedReader reader = null;
  59.         try {
  60.             int statusCode = client.executeMethod(method);
  61.             if (statusCode != HttpStatus.SC_OK) {
  62.                 return null;
  63.             }
  64.  
  65.             reader = new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream(), “utf8″));
  66.             String line;
  67.             while ((line = reader.readLine()) != null) {
  68.                 builder.append(line + “\r\n”);
  69.             }
  70.             sourceCode = builder.toString();
  71.         } catch (HttpException e) {
  72.             e.printStackTrace();
  73.         } catch (IOException e) {
  74.             e.printStackTrace();
  75.         } finally {
  76.             try {
  77.                 reader.close();
  78.             } catch (IOException e) {
  79.                 e.printStackTrace();
  80.             }
  81.             // 释放连接
  82.             method.releaseConnection();
  83.             client.getHttpConnectionManager().closeIdleConnections(0);
  84.         }
  85.         return sourceCode;
  86.     }
  87. }

最后发出一个pojo,CSDNPost.java

  1. package cn.mingyuan.csdn2wordpress;
  2.  
  3. import java.util.Date;
  4.  
  5. /**
  6.  * csdn post
  7.  * 
  8.  * @author mingyuan
  9.  * 
  10.  */
  11. public class CSDNPost {
  12.     /**
  13.      * 博文创建日期
  14.      */
  15.     private Date dateCreated;
  16.     /**
  17.      * 博文内容
  18.      */
  19.     private String description;
  20.     /**
  21.      * 标题
  22.      */
  23.     private String title;
  24.     /**
  25.      * 博文分类
  26.      */
  27.     private String[] categories;
  28.  
  29.     public CSDNPost() {
  30.  
  31.     }
  32.  
  33.     public CSDNPost(String title, String description, String[] categories, Date dateCreated) {
  34.         this.dateCreated = dateCreated;
  35.         this.description = description;
  36.         this.title = title;
  37.         this.categories = categories;
  38.     }
  39.  
  40.     public Date getDateCreated() {
  41.         return dateCreated;
  42.     }
  43.  
  44.     public void setDateCreated(Date dateCreated) {
  45.         this.dateCreated = dateCreated;
  46.     }
  47.  
  48.     public String getDescription() {
  49.         return description;
  50.     }
  51.  
  52.     public void setDescription(String description) {
  53.         this.description = description;
  54.     }
  55.  
  56.     public String getTitle() {
  57.         return title;
  58.     }
  59.  
  60.     public void setTitle(String title) {
  61.         this.title = title;
  62.     }
  63.  
  64.     public String[] getCategories() {
  65.         return categories;
  66.     }
  67.  
  68.     public void setCategories(String[] categories) {
  69.         this.categories = categories;
  70.     }
  71. }

 

以上是全部源码。

在文章的结尾,我愿意跟大家分享一下这个小程序的开发心得。

一开始写这个程序的时候,觉得会很快搞定,因为这个程序无非就是三个过程:采集、解析、发帖。其实也真是这样的一个过程。

这个程序耗费精力比较多的地方是在解析网页、提取链接、标题、内容、发布时间、分类方面。

一开始想用xpath解析网页,并且写xpath表达式都在chrome上测试通过xpath helper验证通过了。但在编码阶段发现现有的工具包,比如dom4j就不支持对html的解析,网上看了有通过htmlparser将html转换成xml的方法。但觉得太麻烦,最后发现了JSoup这个非常强大的工具,它可以通过类似jquery和css选取语法的表达式来提取内容。尝试了下非常方便,于是解析这个问题没有了(有个小窍门:chrome浏览器开发者工具可以看某节点的css样式,把这个样式直接传递给jsoup就能提取内容)。

 

wordpress支持MetaWeblog协议,可以通过XML-RPC进行发帖。关于它们的信息可以通过以下链接找到:

http://en.wikipedia.org/wiki/MetaWeblog

http://en.wikipedia.org/wiki/XML-RPC    (可以找到各种语言版本的api)

另外JSoup的地址是:

http://jsoup.org/

 

 

程序写的太匆忙,肯定有很多不尽人意的地方,希望各位指出。我的联系方式是:admin#youthmemo.com。