oschina博客搬家更新 http://move.oschina.net/
oschina博客搬家更新 http://move.oschina.net/
Yason_Luo 发表于4年前
oschina博客搬家更新 http://move.oschina.net/
  • 发表于 4年前
  • 阅读 3436
  • 收藏 59
  • 点赞 4
  • 评论 11
摘要: 本文介绍oschina 博客搬家应用的更新情况。应用地址:http://move.oschina.net/ 原博客: oschina openapi 应用:博客搬家 http://my.oschina.net/oscfox/blog/194507

一、新增博客类型:新增了wordpress个人博客 xml文件上传导入oschina的功能。登陆osc后只有点击右下wordpress图标即可选择从自己wordpress站点导出的xml文件上传。再点击抓取就可以获取文件中的博客列表,可选地导入osc即可。


二、程序结构调整:本次更新最主要是将各个博客网站的抓取规则写到同一个配置文件中,将抓取逻辑封装在同一个类里面,然后根据不同的url从配置文件中读取不同网站的抓取规则(读取xml用dom4j)。主要是为了方便扩展,也让结构更加清晰一点。

/**
 * 博客爬虫抓取逻辑
 * @author oscfox
 * @date 20140124
 */
public class BlogPageProcessor implements PageProcessor{
	
	public class LinkXpath{
		public String linksXpath;	//链接列表过滤表达式
		public String titlesXpath;	//title列表过滤表达式
	}
	
	public class ArticleXpath{
		public String contentXpath;	//内容过滤表达式
		public String titleXpath;	//title过滤表达式
		public String tagsXpath; 	//tags过滤表达式
	}
	
	private Site site = new Site();
	private String url;
	private String blogFlag;							//博客url的内容标志域
	private List<String> codeBeginRex; 					//代码过滤正则表达式
	private List<String> codeEndRex; 					//代码过滤正则表达式
	private List<LinkXpath> linkXpaths;					//获取链接表达式
	private List<ArticleXpath> articleXpaths;			//获取文件表达式
	private List<String> PagelinksRex;					//类别页列表过滤表达式
	private Hashtable<String, String> codeHashtable;	//代码class映射关系
	private SpiderConfigTool spiderConfig; 

	public BlogPageProcessor(String url) throws Exception{
		if(url.endsWith("/")){
			url = url.substring(0, url.length()-1);
		}
		this.url=url;
		
		String spiderName="";	//切割域名 :类似:csdn.net, 51cto.com, cnblogs.com, iteye.com
		
		Pattern p=Pattern.compile("\\.([a-zA-Z0-9]+\\.[a-zA-Z]+)");
		Matcher m=p.matcher(url);
		
		if(m.find()){
			spiderName = m.group(1);
		} else {
			throw new Exception("不支持的网站!");
		}
		
		spiderConfig = new SpiderConfigTool(spiderName);
		
		init();
	}
	
	/**
	 * 初始化
	 */
	private void init(){
		String domain = spiderConfig.getSpiderNode().selectSingleNode("domain").getText();
		site = Site.me().setDomain(domain);
		String charset = spiderConfig.getSpiderNode().selectSingleNode("charset").getText();
		site.setCharset(charset);
		site.setSleepTime(1);
		
		blogFlag = spiderConfig.getSpiderNode().selectSingleNode("blog-flag").getText();
		
		initPageRex();
		initCodeRex();
		initLinkXpath();
		initArticleXpath();
		initCodeHash();
	}
	
	/**
	 * 初始化 代码替换正则
	 */
	@SuppressWarnings("unchecked")
	private void initCodeRex(){
		codeBeginRex = new ArrayList<String>(); 		//代码过滤正则表达式
		codeEndRex  = new ArrayList<String>(); 			//代码过滤正则表达式
		
		List<Node> list = spiderConfig.getSpiderNode().selectNodes("code-begin-rex");
		for(Node n:list){
			codeBeginRex.add(n.getText());
		}
		
		list = spiderConfig.getSpiderNode().selectNodes("code-end-rex");
		for(Node n:list){
			codeEndRex.add(n.getText());
		}
	}
	
	/**
	 * 初始化 分页链接
	 */
	@SuppressWarnings("unchecked")
	private void initPageRex(){
		PagelinksRex = new ArrayList<String>();
		//page-links-rex
		List<Node> list = spiderConfig.getSpiderNode().selectNodes("page-links-rex");
		for(Node pagelink:list){
			String page = pagelink.getText();
			String string=url.replaceAll("\\.", "\\\\\\.");
			String temString= string+page;
			PagelinksRex.add(temString);
		}
	}
	
	/**
	 * 初始化 获取链接列表xpath
	 */
	@SuppressWarnings("unchecked")
	private void initLinkXpath(){
		linkXpaths = new ArrayList<LinkXpath>();		//获取链接表达式
		
		List<Node> list = spiderConfig.getSpiderNode().selectNodes("link-xpath");
		for(Node node : list){
			String link = node.selectSingleNode("links-xpath").getText();
			String title = node.selectSingleNode("titles-xpath").getText();
			LinkXpath linkXpath = new LinkXpath();
			linkXpath.linksXpath=link;
			linkXpath.titlesXpath=title;
			linkXpaths.add(linkXpath);
		}
	}
	
	/**
	 * 初始化 文章规则
	 */
	@SuppressWarnings("unchecked")
	private void initArticleXpath(){
		articleXpaths = new ArrayList<ArticleXpath>();	//获取文件表达式
		
		List<Node> list = spiderConfig.getSpiderNode().selectNodes("article-xpath");
		for(Node node : list){
			String content = node.selectSingleNode("content-xpath").getText();
			String title = node.selectSingleNode("title-xpath").getText();
			String tags = node.selectSingleNode("tags-xpath").getText();
			ArticleXpath articleXpath = new ArticleXpath();
			articleXpath.contentXpath=content;
			articleXpath.titleXpath=title;
			articleXpath.tagsXpath = tags;
			
			articleXpaths.add(articleXpath);
		}
	}
	
	/**
	 * 初始化代码类型映射
	 */
	@SuppressWarnings("unchecked")
	private void initCodeHash(){
		codeHashtable = new Hashtable<String, String>();
		
		List<Node> list = spiderConfig.getSpiderNode().selectNodes("code-hashtable");
		for(Node node : list){
			String key = node.selectSingleNode("key").getText();
			String osc = node.selectSingleNode("osc").getText();
			codeHashtable.put(key, osc);
		}
	}

	/**
	 * 抓取博客内容等,并将博客内容中有代码的部分转换为oschina博客代码格式
	 */
	@Override
    public void process(Page page) {
		
		Pattern p=Pattern.compile(blogFlag);
		Matcher m=p.matcher(url);
		boolean result=m.find();
		
        if(result){
        	getPage(page);
        	page.putField("getlinks", false);
        } else {
			getLinks(page);
			page.putField("getlinks", true);
		}
	}
	
	/**
	 * 抓取链接列表
	 * @param page
	 */
	private void getLinks(Page page) {
		List<String> links = page.getHtml().xpath(linkXpaths.get(0).linksXpath).all();
        List<String> titles = page.getHtml().xpath(linkXpaths.get(0).titlesXpath).all();
        
        for(int i=1; i < linkXpaths.size() && titles.size() == 0; ++i){
        	links = page.getHtml().xpath(linkXpaths.get(i).linksXpath).all();
            titles = page.getHtml().xpath(linkXpaths.get(i).titlesXpath).all();
        }
        
        page.putField("titles", titles);
        page.putField("links", links);
        
        List<String> Pagelinks = page.getHtml().links().regex(PagelinksRex.get(0)).all();
        
        for(int i=1; i < PagelinksRex.size() && Pagelinks.size() == 0; ++i){
        	Pagelinks = page.getHtml().links().regex(PagelinksRex.get(i)).all();
        }
        
        page.addTargetRequests(Pagelinks);
	}

	/**
	 * 抓取博客内容
	 * @param page
	 */
	private void getPage(Page page){
        
        String title = page.getHtml().xpath(articleXpaths.get(0).titleXpath).toString();
        String content = page.getHtml().xpath(articleXpaths.get(0).contentXpath).toString();
        String tags = page.getHtml().xpath(articleXpaths.get(0).tagsXpath).all().toString();
        
        for(int i=1; i < articleXpaths.size() && null == title; ++i){
        	title = page.getHtml().xpath(articleXpaths.get(i).titleXpath).toString();
        	content = page.getHtml().xpath(articleXpaths.get(i).contentXpath).toString();
            tags = page.getHtml().xpath(articleXpaths.get(i).tagsXpath).all().toString();
        }
        
        if(StringUtils.isBlank(content) || StringUtils.isBlank(title)){
        	return;
        }
        
        if(!StringUtils.isBlank(tags)){
        	tags = tags.substring(tags.indexOf("[")+1,tags.indexOf("]"));
        }

        OscBlogReplacer oscReplacer= new OscBlogReplacer(codeHashtable);	//设置工具类映射关系
    	String oscContent = oscReplacer.replace(codeBeginRex, codeEndRex, content);		//处理代码格式
    	
        page.putField("content", oscContent);
        page.putField("title", title);
        page.putField("tags", tags);
	}
	
    @Override
    public Site getSite() {
        return site;
    }
}

部分xml文件:

<!-- CSND -->
	<spider-cofig>
		<domain>blog.csdn.net</domain>
		<charset>utf-8</charset>
		<name>csdn.net</name>							<!--//博客域名: 用于匹配博客spider配置  -->
		<blog-flag>/article/details/</blog-flag>			<!--//博客url的内容标志域  -->
		
		<link-xpath>							<!--//获取链接表达式  -->
			<links-xpath><![CDATA[//div[@class='list_item article_item']/div[@class='article_title']/h3/span/a/@href]]></links-xpath>
			<titles-xpath><![CDATA[//div[@class='list_item article_item']/div[@class='article_title']/h3/span/a/text()]]></titles-xpath>
		</link-xpath>
		
		<article-xpath>							<!--//获取文章表达式  -->
			<content-xpath><![CDATA[//div[@class='article_content']/html()]]></content-xpath>
			<title-xpath><![CDATA[//div[@class='details']/div[@class='article_title']/h3/span/a/text()]]></title-xpath>
			<tags-xpath><![CDATA[//div[@class='tag2box']/a/text()]]></tags-xpath>
		</article-xpath>
		
		<code-begin-rex><![CDATA[<pre.*?class=\"(.+?)\".*?>]]></code-begin-rex>		<!--//代码过滤正则表达式-头  -->
		<code-begin-rex><![CDATA[<textarea.*?class=\"(.+?)\".*?>]]></code-begin-rex>
		<code-end-rex><![CDATA[</textarea>]]></code-end-rex>			<!--//代码过滤正则表达式-尾  -->
		
		<!--//列表页url过滤表达式 -->
		<page-links-rex><![CDATA[/article/list/\d+]]></page-links-rex>		
		
		<!--//代码class映射关系 -->
		<code-hashtable>						
			<key>csharp</key>
			<osc>c#</osc>
		</code-hashtable>
		<code-hashtable>						
			<key>javascript</key>
			<osc>js</osc>
		</code-hashtable>
		<code-hashtable>						
			<key>objc</key>
			<osc>cpp</osc>
		</code-hashtable>
	</spider-cofig>



四、源码

程序已更新到git 

更多代码请看git地址:http://git.oschina.net/yashin/MoveBlog

欢迎各位OSCer 提交代码或BUG或提出宝贵意见。

由于本人资历尚浅,如有不足,敬请各位不吝赐教。


PS: 如各位OSCer 有需要从上述博客网站之外的站点搬家博客,可以给我留言,尽量满足各位的要求增加上去。

或者更欢迎您提交git 上的 pull Requests

如抓取的内容或者列表有误,请留言提供您要抓取的链接并简单说明bug,我会尽快给您答复。



共有 人打赏支持
Yason_Luo
粉丝 247
博文 45
码字总数 5378
评论 (11)
叶秀兰
itfanr

引用来自“叶秀兰”的评论

@itfanr

收到 谢谢
苗哥
很棒的功能,不过我的CSDN和ItEye里有的文章,OSC里全都有。
FoxHu
这个不错,很实用!
Yason_Luo

引用来自“苗哥”的评论

很棒的功能,不过我的CSDN和ItEye里有的文章,OSC里全都有。

谢谢支持
Yason_Luo

引用来自“itfanr”的评论

引用来自“叶秀兰”的评论

@itfanr

收到 谢谢

欢迎使用,如有bug,请艾特我,会尽快解决0
Yason_Luo

引用来自“FoxHu”的评论

这个不错,很实用!

欢迎使用,感谢支持0
feiandxs
所有的提供搬家程序的BSP的有趣的地方。。
“导入我们这可方便啦”
但没有人提导出。生怕用户学会了就拔腿走人。
feiandxs
只是有感而发,并非说OSC的blog。 仅仅是因为想起了当年搬家来搬家去的痛苦。
Yason_Luo

引用来自“feiandxs”的评论

只是有感而发,并非说OSC的blog。 仅仅是因为想起了当年搬家来搬家去的痛苦。

osc openapi 有个人博客获取的api的,欢迎查阅使用:http://www.oschina.net/openapi/docs/user_blog_list0
shitalpig
@Yashin 博客搬家能不能增加一个 对google blogger blogspot.com的支持
×
Yason_Luo
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: