node.js 爬虫案例
本案例是爬的一部小说,爬取并存在本地
使用了动态浏览器头部信息,和 动态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 爬虫案例的更多相关文章
- Node.js爬虫-爬取慕课网课程信息
第一次学习Node.js爬虫,所以这时一个简单的爬虫,Node.js的好处就是可以并发的执行 这个爬虫主要就是获取慕课网的课程信息,并把获得的信息存储到一个文件中,其中要用到cheerio库,它可以让 ...
- node.js爬虫
这是一个简单的node.js爬虫项目,麻雀虽小五脏俱全. 本项目主要包含一下技术: 发送http抓取页面(http).分析页面(cheerio).中文乱码处理(bufferhelper).异步并发流程 ...
- Node.js aitaotu图片批量下载Node.js爬虫1.00版
即使是https网页,解析的方式也不是一致的,需要多试试. 代码: //====================================================== // aitaot ...
- Node.js umei图片批量下载Node.js爬虫1.00
这个爬虫在abaike爬虫的基础上改改图片路径和下一页路径就出来了,代码如下: //====================================================== // ...
- Node.js abaike图片批量下载Node.js爬虫1.01版
//====================================================== // abaike图片批量下载Node.js爬虫1.01 // 1.01 修正了输出目 ...
- Node.js abaike图片批量下载Node.js爬虫1.00版
这个与前作的差别在于地址的不规律性,需要找到下一页的地址再爬过去找. //====================================================== // abaik ...
- Node JS爬虫:爬取瀑布流网页高清图
原文链接:Node JS爬虫:爬取瀑布流网页高清图 静态为主的网页往往用get方法就能获取页面所有内容.动态网页即异步请求数据的网页则需要用浏览器加载完成后再进行抓取.本文介绍了如何连续爬取瀑布流网页 ...
- Node.js 爬虫爬取电影信息
Node.js 爬虫爬取电影信息 我的CSDN地址:https://blog.csdn.net/weixin_45580251/article/details/107669713 爬取的是1905电影 ...
- Node.js 爬虫初探
前言 在学习慕课网视频和Cnode新手入门接触到爬虫,说是爬虫初探,其实并没有用到爬虫相关第三方类库,主要用了node.js基础模块http.网页分析工具cherrio. 使用http直接获取url路 ...
随机推荐
- live555峰哥的私房菜(二)-----计划任务(TaskScheduler)探讨
计划任务(TaskScheduler)探讨 上一篇谈到SingleStep()函数会找到三种任务类型并执行之. 这三种任务是: socket handler, event handler, delay ...
- uoj#119. 【UR #8】决战圆锥曲线
http://uoj.ac/problem/119 可以认为数据基本随机,于是可以直接用线段树维护,对每个询问在线段树上进行剪枝搜索. #include<bits/stdc++.h> ty ...
- Rabbitmq 安装&启动
安装socat [root@Aliyun software]# yum -y install epel-release [root@Aliyun software]# yum -y install s ...
- Hive之一:hive2.1.1安装部署
一.Hive 运行模式 与 Hadoop 类似,Hive 也有 3 种运行模式: 1. 内嵌模式 将元数据保存在本地内嵌的 Derby 数据库中,这是使用 Hive 最简单的方式.但是这种方式缺点也比 ...
- WordPress更换主题空白问题
刚才尝试着更换了一个主题,后来发现预览主页的时候是一片空白.查了很多资料,有说是index.php的权限问题,有说是插件问题,有说是UTL-8编码的问题,我都试过了,发现都不行,后来仔细研究了一下,发 ...
- [UE4]位与字节
位 1.bit,比特 2.一个位可以表示两个值,0或者1(一个位只能表示0或者1,并不是能同时表示0和1). 3.一个位为什么只能是2个值,而不能是3个值呢?这是由于技术因素造成的,在硬件中,如果用一 ...
- Mybatis 系列7-结合源码解析核心CRUD 配置及用法
[Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...
- RedHat使用Centos的yum仓库
RedHat使用Centos的yum仓库 卸载红帽yum源 [root@zhouwanchun ~]# rpm -qa | grep yum [root@zhouwanchun ~]# rpm -e ...
- (转)Linux中设置服务自启动的三种方式
有时候我们需要Linux系统在开机的时候自动加载某些脚本或系统服务 主要用三种方式进行这一操作: ln -s 在/etc/rc.d/rc*.d目录中建立/e ...
- sas spawner
注册spawner 服务"c:\Program Files\SASHome\SASFoundation\9.4\cntspawn.exe" -install -service 51 ...