保存知乎收藏夹功能的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 ...
随机推荐
- 信息安全系统设计基础第一次实验报告 20135201&&20135306&&20135307
信息安全系统设计基础实验 班级: 201353 姓名:张忻 张嘉琪 黄韧 学号:20135301 20135307 20135306 实验日期:2015.11.10 实验名称: S3C2410的lin ...
- 实验楼实验——LINUX基础入门
第一节 Linux简介 一.Linux的历史: 1965 年,Bell 实验室.MIT.GE(通用电气公司)准备开发 Multics 系统,为了同时支持 300 个终端访问主机,但是 1969 年失败 ...
- 接收content-type:multipart/form-data类型的参数
一.问题描述 最近在写接口程序,该接口需要与其他公司的程序对接.对方发送content-type:multipart/form-data类型的参数,结果通过request.getParameter(p ...
- JavaScript原型链和instanceof运算符的暧昧关系
时间回到两个月前,简单地理了理原型链.prototype以及__proto__之间的乱七八糟的关系,同时也简单了解了下typeof和instanceof两个运算符,但是,anyway,试试以下两题: ...
- Scala学习笔记(六):Scala程序
想要编写能够独立运行的Scala程序,就必须创建有main方法(仅带一个参数Array[String],且结果类型为Unit)的单例对象. 任何拥有合适签名的main方法的单例对象都可以用来作为程序的 ...
- JavaScript实现Ajax小结
置顶文章:<纯CSS打造银色MacBook Air(完整版)> 上一篇:<TCP的三次握手和四次挥手> 作者主页:myvin 博主QQ:851399101(点击QQ和博主发起临 ...
- 『.Net』微软家的女儿们
在博客园看到了 一篇文章 <.NET4.0框架退休日期逐渐临近> 写下了这篇文章 —— 记录我和 .Net Framework 的 那些日子. 微软 家有几个 女儿. 2008年,我遇到他 ...
- jQuery UI dialog
初始化参数 对于 dialog 来说,首先需要进行初始化,在调用 dialog 函数的时候,如果没有传递参数,或者传递了一个对象,那么就表示在初始化一个对话框. 没有参数,表示按照默认的设置初始化对话 ...
- [bzoj 1004][HNOI 2008]Cards(Burnside引理+DP)
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1004 分析: 1.确定方向:肯定是组合数学问题,不是Polya就是Burnside,然后题目上 ...
- go语言指针符号的*和&
先看一段代码 先放一段代码,人工运行一下,看看自己能做对几题? package main import "fmt" func main() { var a int = 1 var ...