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,我會盡快給您答覆。