本案例是爬的一部小说,爬取并存在本地

使用了动态浏览器头部信息,和 动态ip代理, 这2个方式可以有效的伪装自己,不至于被反爬,然后拉黑,之前已有记录,浏览器头部信息,也记录的有,

app.js

import request from 'request';
import userAgents from './common/userAgent.js';
import Promise from 'bluebird';
import cheerio from 'cheerio';//类似jquery写法
import fs from 'fs';
const Iconv = require('iconv').Iconv; const iconv = new Iconv('GBK', 'UTF-8'); const URL = 'http://www.qb5200.org/xiaoshuo/62/62493/';
let pageUrl = `${URL}.html`; //章节存放变量,初始化是第一章地址
//这里只做测试,所以用变量存,而实际应用中,应该使用数据缓存
const expiryTime = * * ;// 过期间隔时间,毫秒
let ips = null; //代理ip
let time = null;// 存储代理IP的时间,判断是否过期,如果过期重新请求
let pageNumber = ; //页码
//let info = '';//爬取到的内容存放变量
/**
* 请求免费代理,可做缓存,这里就存在变量中,只做测试
*/
const getProxyList = () => {
return new Promise((resolve, reject) => {
const nowDate = Date.now();
if( nowDate - time < expiryTime ){
resolve(ips);
return;
}
const apiURL = 'http://www.66ip.cn/mo.php?sxb=&tqsl=100&port=&export=&ktip=&sxa=&submit=%CC%E1++%C8%A1&textarea=http%3A%2F%2Fwww.66ip.cn%2F%3Fsxb%3D%26tqsl%3D100%26ports%255B%255D2%3D%26ktip%3D%26sxa%3D%26radio%3Dradio%26submit%3D%25CC%25E1%2B%2B%25C8%25A1';
const options = { method: 'GET', url: apiURL, gzip: true, encoding: null,
headers: {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding': 'gzip, deflate',
'Accept-Language': 'zh-CN,zh;q=0.8,en;q=0.6,zh-TW;q=0.4',
'User-Agent': 'Mozilla/8.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36',
'referer': 'http://www.66ip.cn/'
},
};
request(options, (error, response, body)=>{
try {
if(Buffer.isBuffer(body)){
const ret = body.toString().match(/\d{,}\.\d{,}\.\d{,}\.\d{,}:\d{,}/g);
ips = ret;
time = Date.now();
resolve(ret);
}
} catch (e) {
console.log(e);
}
});
})
}
//爬取小说
async function getPageIndex(ipList,url){
return new Promise((resolve, reject) => {
let userAgent = userAgents[parseInt(Math.random() * userAgents.length)];
let ip = ipList[parseInt(Math.random() * ips.length)];
let useIp = `http://${ip}`;const options = { method: 'GET', url: url, gzip: true, encoding: null,
headers: {
'User-Agent': userAgent, //动态设置浏览器头部信息
},
proxy: useIp, //动态设置代理ip
timeout:
};
request( options , (error, response, body)=>{
//429 Too Many Requests 这个问题貌似是服务器问题
if (error || body.toString().indexOf('429 Too Many Requests') >= ) {
console.log(`爬取页面失败,${error},正在重新寻找代理ip... ×`);
resolve(undefined);
return;
}else{
console.log('爬取页面成功, √');
}
resolve(body)
})
})
}
//获取内容
async function getChapter(body){
return new Promise((resolve, reject) => {
let result = iconv.convert(new Buffer(body, 'binary')).toString();
let $ = cheerio.load(result);
let title = $('#title').text();
title = `第${pageNumber}章 ${title}`;
let content = $('#content').text();
let list = $('#footlink>a');
console.log(title);
//这里是因为有时候拿不到下一页的按钮,就自己退出了,所以做下处理
if(list.length === ){
resolve(undefined);
return;
}
list.each(function(i,item){
let $this = $(this);
if($this.text() === '下一页'){
let path = $this.attr('href');
if( path.indexOf('http') >= ){
path = null;
}else{
path = URL + path ;
}
resolve({
content: `${title}\n ${content}\n\n\n\n`,
path: path
});
}
}) })
}
//写入到本地
async function Write(info){
return new Promise((resolve, reject) => {
const decodedText = new Buffer(info);
fs.readFile('九天剑道.txt',function(err,_buffer){
if(err){//读取失败,则直接创建
fs.writeFile('九天剑道.txt',decodedText,function(err){
if(err){
console.log(err);
}else{
console.log('写入成功---!');
resolve(true);
}
})
}
if(Buffer.isBuffer(_buffer)){//判断是否是Buffer对象
fs.writeFile('九天剑道.txt',new Buffer(_buffer.toString() + info),function(err){
if(err){
console.log(err);
}else{
console.log('写入成功!');
resolve(true);
}
})
}
}) });
}
//启动方法
async function startFun(){
const ipList = await getProxyList();//获取代理ip
const body = await getPageIndex( ipList, pageUrl );//爬取主页面
if(!body){
startFun();
return;
}
const item = await getChapter(body);//获取爬到的内容
if(!item){
startFun();
return;
}
const info = item.content;
//判断是否有下一页,全部爬完之后写入到本地,生产文件
//如果章节过多,应该加上一点处理方式,这里不赘述,我想应该是这样的
if(item.path){
// if(pageNumber%2===0){
const isWrite = await Write(info);
// info = '';
// }
pageNumber++;
pageUrl = item.path;
startFun();
}
}
//启动方法
startFun();

package.json

{
"name": "reptile",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"start": "babel-node app.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"bluebird": "^3.5.1",
"cheerio": "^1.0.0-rc.2",
"fs": "^0.0.1-security",
"iconv": "^2.3.0",
"request": "^2.87.0"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-2": "^6.24.1"
}
}

es6的支持之前也记录的有,就是代理ip的成功率不怎么高,有点慢,

我这里用的是边爬边写入,爬完一次写入 应该也是可以的,这里就不做验证了

代码地址:https://github.com/bruce-gou/Reptilian

node.js 爬虫案例的更多相关文章

  1. Node.js爬虫-爬取慕课网课程信息

    第一次学习Node.js爬虫,所以这时一个简单的爬虫,Node.js的好处就是可以并发的执行 这个爬虫主要就是获取慕课网的课程信息,并把获得的信息存储到一个文件中,其中要用到cheerio库,它可以让 ...

  2. node.js爬虫

    这是一个简单的node.js爬虫项目,麻雀虽小五脏俱全. 本项目主要包含一下技术: 发送http抓取页面(http).分析页面(cheerio).中文乱码处理(bufferhelper).异步并发流程 ...

  3. Node.js aitaotu图片批量下载Node.js爬虫1.00版

    即使是https网页,解析的方式也不是一致的,需要多试试. 代码: //====================================================== // aitaot ...

  4. Node.js umei图片批量下载Node.js爬虫1.00

    这个爬虫在abaike爬虫的基础上改改图片路径和下一页路径就出来了,代码如下: //====================================================== // ...

  5. Node.js abaike图片批量下载Node.js爬虫1.01版

    //====================================================== // abaike图片批量下载Node.js爬虫1.01 // 1.01 修正了输出目 ...

  6. Node.js abaike图片批量下载Node.js爬虫1.00版

    这个与前作的差别在于地址的不规律性,需要找到下一页的地址再爬过去找. //====================================================== // abaik ...

  7. Node JS爬虫:爬取瀑布流网页高清图

    原文链接:Node JS爬虫:爬取瀑布流网页高清图 静态为主的网页往往用get方法就能获取页面所有内容.动态网页即异步请求数据的网页则需要用浏览器加载完成后再进行抓取.本文介绍了如何连续爬取瀑布流网页 ...

  8. Node.js 爬虫爬取电影信息

    Node.js 爬虫爬取电影信息 我的CSDN地址:https://blog.csdn.net/weixin_45580251/article/details/107669713 爬取的是1905电影 ...

  9. Node.js 爬虫初探

    前言 在学习慕课网视频和Cnode新手入门接触到爬虫,说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherrio. 使用http直接获取url路 ...

随机推荐

  1. git撤销本地所有未提交的更改

    1. git clean -df2. git reset --hard第一个命令只删除所有untracked的文件,如果文件已经被tracked, 修改过的文件不会被回退.而第二个命令把tracked ...

  2. quartz中设置Job不并发执行

    使用quartz框架可以完成定时任务处理即Job,比如有时候我们设置1个Job每隔5分钟执行1次,后来会发现当前Job启动的时候上一个Job还没有运行结束,这显然不是我们期望的,此时可以设置quart ...

  3. PyQt—QTableWidget实现翻页功能

    主要使用QTableWidget中的三个函数实现: verticalScrollBar().setSliderPosition() 设置当前滑动条的位置 verticalScrollBar().max ...

  4. 2.Linux技能要求

    Linux嵌入式工程师技能要求: 1.C语言                    具备C语言基础.理解C语言基础编程及高级编程,包括:数据类型.数组.指针.结构体.链表.文件操作.队列.栈.     ...

  5. 使用Mesos和Marathon管理Docker集群

    分布式系统是难于理解.设计.构建 和管理的,他们将比单个机器成倍还要多的变量引入到设计中,使应用程序的根源问题更难发现.SLA(服务水平协议)是衡量停机和/或性能下降的标准,大多数现代应用程序有一个期 ...

  6. 服务网关zuul之一:入门介绍

    通过之前几篇Spring Cloud中几个核心组件的介绍,我们已经可以构建一个简略的(不够完善)微服务架构了.比如下图所示: 我们使用Spring Cloud Netflix中的Eureka实现了服务 ...

  7. 服务容错保护断路器Hystrix之二:Hystrix工作流程解析

    一.总运行流程 当你发出请求后,hystrix是这么运行的 红圈 :Hystrix 命令执行失败,执行回退逻辑.也就是大家经常在文章中看到的“服务降级”. 绿圈 :四种情况会触发失败回退逻辑( fal ...

  8. sys.modules[__name__]的一个实例

    关于sys.modules[__name__]的用法,百度上阅读量比较多得一个帖子是:https://www.cnblogs.com/robinunix/p/8523601.html 对于里面提到的基 ...

  9. Find the peace with yourself

    The purpose of being mature is to find the real calm and peace with yourself. Or you can say the tur ...

  10. OpenGL模版小案例分析

    下面的案例通过模版实现三角形截取的功能,代码如下: void draw(){ GLuint programObject; GLfloat vVerticessmall[] = { 0.0f, 0.25 ...