保存知乎收藏夹功能的NodeJS版本
前两天发现知乎收藏夹中的答案正在不断减少。。看来需要保存一下了,但之前别人的方式是用chrome插件(浏览器无法自动保存本地文件)+wget前后端配合来完成这个工作的,而且还有一些缺点(比如保存的html无法更名),十分麻烦,所以打算用后端程序来抓一下网页,最终还是选择了Node来实现,是因为想借此机会学习一下Node。
结果:好端端的异步编程被窝写成了同步编程。。这次就放同步编程的代码好了,下次再放异步的。。(而且发现在一个月前就有人用python和c#实现了一个足够好的版本。。一下就没动力了)
废话少说,放代码。(Node的基本知识不用我说了吧)
//自己用的小品代码,所以没有拆成多个模块,而是塞到一起了。
var http = require('http'),
fs = require('fs'),
path = require('path'); //自己封装的一个创建目录的工具函数,因为要创建的目录的父目录不存在时,不会像shell指令一样连父目录一块创建了,而是会报错。
//所以用try-catch,出错的话则把目录拆成多个部分,一步步检查。
function myMkdir(dir) {
var nowPath = '';
dir = path.normalize(dir.toString());
try{
if (fs.existsSync(dir) !== true) {
fs.mkdirSync(dir);
} else {
console.log('该目录或文件已存在');
return true;
}
} catch (e) {
dir = path.normalize('/') === '\\'? dir.split('\\'):dir.split('/');
while (dir.length > 0) {
nowPath += dir.shift()+'\\';
if (fs.existsSync(nowPath) === true && fs.statSync(nowPath).isDirectory() === false) {
console.log(nowPath+'处已有文件存在,请指定其他目录。');
return false;
} else if (fs.existsSync(nowPath) === true && fs.statSync(nowPath).isDirectory() === true) {
continue;
}else {
try {
fs.mkdirSync(nowPath);
} catch (e) {
console.log('创建目录'+nowPath+'失败,可能是权限不足,请指定其他目录。');
return false;
}
} }
}
console.log('创建目录成功');
return true;
}
//从module.paths获取程序运行的当前目录
function getNowPath () {
//不同系统的斜杠不同,需要这样处理一下。
var slash = path.normalize('/');
return module.paths[0].split(slash).slice(0,-1).join(slash);
} //src,dir
//传入两个参数,src和dir,src是指定知乎上收藏夹的url,dir是指定保存目录
//我的做法是,先检查目录dir,然后遍历src的目录页获得所有文章的链接,最后再一一下载
function main(argv) {
var waitingList = [],nowBuffer,
slash = path.normalize('/'),
src = argv[0],
dir = argv[1],
links = [],
nowPage = 0, //当前页码
totalPage, //总页码
downloading = -1,
whitespace = "[\\x20\\t\\r\\n\\f]",
//node不能用jQuery和DOM的API很蛋疼。。所以正则只能写长一点。。
rgetTotalPage = new RegExp('<span>'+whitespace+'*<a href="\\?page=(\\d+)">\\1<\\/a>'+whitespace+'*<\\/span>'+whitespace+'*<span>'+
whitespace+'*<a href="\\?page=2">下一页<\\/a>'+whitespace+'*<\\/span>'+whitespace+'*<\\/div>');
var getAllLinkThisPage = function () {
nowPage++;
var contentTmp = [];
var reg = /href="(\/question\/\d+\/answer\/\d+)">/g,
match = true; if (nowPage <= totalPage) {
console.log('正在获取第'+nowPage+'页的链接');
http.get(src+'?page='+nowPage,function (response) {
response.on('data',function (chunk) {
contentTmp.push(chunk);
});
response.on('end',function () {
console.log('第'+nowPage+'页链接获取完毕'); contentTmp = Buffer.concat(contentTmp).toString();
while (reg.lastIndex < contentTmp.length && match) {
match = reg.exec(contentTmp);
if (match) {
var tmp = {type:'html',address:'http://www.zhihu.com'+match[1],dir:dir};
links.push(tmp);
}
}
contentTmp = [];
getAllLinkThisPage();
});
});
} else {
console.log('链接获取完毕,开始下载页面,总共有'+links.length+'个页面');
download();
}
};
var download = function () {
downloading++;
var rgetTitle = /<title>([\s\S]+)<\/title>/,
match = true,
title = '',
fileName = '';
if (downloading < links.length) {
http.get(links[downloading].address,function (response) {
console.log('开始下载第'+downloading+'个页面');
console.log(links[downloading].address);
var contentTmp = [];
response.on('data',function (chunk) {
contentTmp.push(chunk);
});
response.on('end',function () {
contentTmp = Buffer.concat(contentTmp).toString();
title = rgetTitle.exec(contentTmp)[1].replace(/[\\\/:*?"<>|]/g,'');
fileName = links[downloading].dir+path.normalize('/')+title +'.'+links[downloading].type;
fs.writeFile(fileName,contentTmp,function (err) {
if (err) {
console.log('创建'+fileName+'文件失败,原因:');
console.log(err);
} else {
console.log('已成功保存'+fileName);
}
});
contentTmp = [];
download();
});
});
} else {
console.log(links.length+'个文件已下载完毕');
}
};
if (src === undefined || src.indexOf('www.zhihu.com') < 0) {
console.log('获取失败!请输入正确的知乎收藏夹网址,http://www.zhihu.com/collection/20026124');
return false;
}
if (dir === undefined) {
dir = getNowPath()+slash+'zhihu'+slash+src.split('/').slice(-1);
console.log('没有指定存放位置,将使用默认位置:'+dir);
}
//创建目录
if (fs.existsSync(dir) === false) {
console.log('该目录不存在,开始创建目录');
if(myMkdir(dir) === false) {
console.log('创建目录出错,请看错误信息,若不能解决,请联系作者 Aeolia yiaolia@gmail.com');
return false;
}
} else if (fs.existsSync(dir) === true && fs.statSync(dir).isDirectory() === false) {
console.log('该目录已被文件占领,请移除该文件');
return false;
}
console.log('开始连接'+src);
http.get(src,function (response) {
var content = [];
console.log(response.statusCode);
response.on('data',function (chunk) {
content.push(chunk);
});
response.on('end',function () {
content = Buffer.concat(content).toString();
console.log('拿到目录文件');
totalPage = rgetTotalPage.exec(content.toString())[1];
console.log(totalPage);
getAllLinkThisPage();
}); })
} main(process.argv.slice(2));
保存知乎收藏夹功能的NodeJS版本的更多相关文章
- 基于Metronic的Bootstrap开发框架经验总结(12)--页面链接收藏夹功能的实现
在一个系统里面,往往有很多菜单项目,每个菜单项对应一个页面,一般用户只需要用到一些常用的功能,如果每次都需要去各个层次的菜单里面去找对应的功能,那确实有点繁琐.特别是在菜单繁多,而客户又对系统整体不熟 ...
- 基于Metronic的Bootstrap开发框架经验总结(13)--页面链接收藏夹功能的实现2(利用Sortable进行拖动排序)
在上篇随笔<基于Metronic的Bootstrap开发框架经验总结(12)--页面链接收藏夹功能的实现>上,我介绍了链接收藏夹功能的实现,以及对收藏记录的排序处理.该篇随笔主要使用功能按 ...
- JQUERY 实现加入收藏夹功能
关于"加入收藏"的代码,很多人都不会重视,一般情况是随便在网上搜一个代码放在页面里就草草了事了.可是都没有做到主流浏览器的兼容.下面分享一段使用 jQuery 实现加入收藏夹的功能 ...
- “添加到收藏夹”功能(share)
以下分享自: 如何给网站增加“添加到收藏夹” 给网站添加“添加到收藏夹”理论上应该是很简单的事情,但是受到各种浏览器和操作系统的不一致的问题,使得这个问题异常的繁琐啊. 下面是梳理的一些资料,仅供参考 ...
- js_加入收藏夹功能
<script type="text/javascript">function addToFavorite(obj) { var url = "http ...
- microsoft help viewer 收藏夹功能
平时重装系统比较多,重装后,microsoft help viewer 2.0里面的收藏就丢失了,要恢复以前的收藏,可以直接在C:\Users\ZR\AppData\Local\Microsoft\H ...
- 基于CefSharp开发浏览器(八)浏览器收藏夹栏
一.前言 上一篇文章 基于CefSharp开发(七)浏览器收藏夹菜单 简单实现了部分收藏夹功能 如(添加文件夹.添加收藏.删除.右键菜单部分功能) 后续代码中对MTreeViewItem进行了扩展,增 ...
- python scrapy爬取知乎问题和收藏夹下所有答案的内容和图片
上文介绍了爬取知乎问题信息的整个过程,这里介绍下爬取问题下所有答案的内容和图片,大致过程相同,部分核心代码不同. 爬取一个问题的所有内容流程大致如下: 一个问题url 请求url,获取问题下的答案个数 ...
- Django Web开发【3】创建网络收藏夹
这一节我们将继续一个创建网络收藏夹应用,并学习视图.模型以及模板的处理过程. Django是一个MVC开发框架,但是它的控制器对应的为view,而视图对应为模板(template),模型对应model ...
随机推荐
- 用virtualenv管理python3运行环境
1. 简介 virtualenv可以用来管理互不干扰的独立python虚拟环境,在有些场景下非常有用,例如: 你有两个python项目,一个是python2.7的,另一个是python3的,可以创建两 ...
- ASP.NET MVC 5 入门教程 (1) 新建项目
文章来源: Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc-5-get-started-create-project.html 下一节:ASP.NET ...
- php模式设计之 观察者模式
这是我写的<php模式设计>的第五篇.前面的四篇在不断学习不断加深认识,到了今天再看观察者模式,觉得非常容易理解.这也许就是我们积少成多的结果吧.希望还是能够不断进步. 开篇还是从名字说起 ...
- css编写的时候注意什么
1.尽量少写div.别没事干就加一个div层. 我们尽量做到代码清晰,结构清晰. 2.css的定位,漂浮,容量,margin,padding我们用的时候尽量. 写的时候,有很多种,但是我们必须要求自己 ...
- [设计模式]第三回:抽象工厂模式(Abstract Factory)
概述 在系统中往往会有这种需求,客户端会用到很多对象,而且根据需求变化很可能会切换成另外一套对象.抽象工厂模式可以提供一种封装机制来面对这种需求. 实践 物理模型: 皮肤主题:设计一个可以切换皮肤主题 ...
- iOS -- 给model赋值时走了[self setValuesForKeysWithDictionary:dic]不走setvalue: forked:
这是一个小坑, 看看你的BaseModel的便利构造器的方法: + (__kindof BaseModel *)modelWithDic:(NSDictionary *)dic { return [[ ...
- c#中的protected和internal
protected限制子类访问,可以跨程序集 internal 限制此程序集访问,可以跨类 protected internal 限制此程序集的子类中访问
- beta发布排序结果
排序结果 序号 组名 组长 项目简称 1 飞天小女警 沈柏杉 选礼物 1 奋斗吧兄弟 黄兴 食物链 3 天天向上 王森 连连看 4 金洲勇士 尹良亮 考试 5 新蜂 武志远 俄罗斯 6 nice! 李 ...
- python 元祖(tuple)
元祖和列表几乎相同,但是元祖一旦初始化是不可变更内容的 元祖的表示方式是caassmates=(), 要记住所有列表能用的.元祖都能用,但是就是不能变内容 注:记住,在python中的元祖,为了引起不 ...
- 【BZOJ 3674】可持久化并查集加强版&【BZOJ 3673】可持久化并查集 by zky 用可持久化线段树破之
最后还是去掉异或顺手A了3673,,, 并查集其实就是fa数组,我们只需要维护这个fa数组,用可持久化线段树就行啦 1:判断是否属于同一集合,我加了路径压缩. 2:直接把跟的值指向root[k]的值破 ...