1、新增博客类型:新增了wordpress我的博客 xml文件上传导入oschina的功能。登录osc后只有点击右下wordpress图标便可选择从本身wordpress站点导出的xml文件上传。再点击抓取就能够获取文件中的博客列表,可选地导入osc便可。 javascript
2、程序结构调整:本次更新最主要是将各个博客网站的抓取规则写到同一个配置文件中,将抓取逻辑封装在同一个类里面,而后根据不一样的url从配置文件中读取不一样网站的抓取规则(读取xml用dom4j)。主要是为了方便扩展,也让结构更加清晰一点。 html
/** * 博客爬虫抓取逻辑 * @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文件: java
<!-- 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>
4、源码: node
程序已更新到git git
更多代码请看git地址:http://git.oschina.net/yashin/MoveBlog 正则表达式
欢迎各位OSCer 提交代码或BUG或提出宝贵意见。 c#
因为本人资历尚浅,若有不足,敬请各位不吝赐教。 dom
PS: 如各位OSCer 有须要从上述博客网站以外的站点搬家博客,能够给我留言,尽可能知足各位的要求增长上去。 ide
或者更欢迎您提交git 上的 pull Requests wordpress
如抓取的内容或者列表有误,请留言提供您要抓取的连接并简单说明bug,我会尽快给您答复。