//cnpm install superagent cheerio eventproxy fs path
var superagent = require('superagent');
var cheerio = require('cheerio');
var eventproxy = require('eventproxy');
var fs = require("fs");
var path = require("path");
var ep = new eventproxy(); //全局变量
var g = {
//抓取时间间隔
list_fetch_sec : 500,
//抓取页码数
list_fetch_num : 50,
//抓取失败待重试的数组
list_fail_url : [],
//最终获取的图片数组
list_href_arr : [],
//抓取的版块
pid:'douban_explore_ent',
//每个文件下的数据条数
file_data_num:30
}; get_article_list_url(); function get_list_url(){
var url_arr = [];
//控制抓取页码数
for(var i=0;i<g.list_fetch_num;i++){
var strat = i*30;
url_arr.push('https://www.douban.com/group/explore/ent?start='+strat);
}
return url_arr;
} //解析列表页dom
function parseList(all_arr){
//遍历
all_arr.forEach(function (item) {
var itemUrl = item[0];
var itemHtml = item[1]; console.log('列表页抓取完成'); var $ = cheerio.load(itemHtml);
var a_dom = $('#content .channel-item .bd h3 a'); a_dom.each(function(){
var href = $(this).attr('href');
console.log(href);
g.list_href_arr.push(href);
});
});
} //解析详情页dom
function praseDetail(all_arr){
var text_arr = [];
all_arr.forEach(function (item) {
var itemUrl = item[0];
var itemHtml = item[1];
var group_no = item[2];
if(itemHtml){
//decodeEntities 是否解码实体
var $ = cheerio.load(itemHtml,{decodeEntities: false});
var content_jq = $('#content .topic-doc .topic-content');
var title_jq = $('#content h1');
try{
var first_floor = content_jq.html();
var title = title_jq.text();
var data = {
content:first_floor,
title:title,
url:itemUrl
};
text_arr.push(data);
}catch(msg){
console.log('error');
}
}
});
return text_arr;
} //获取帖子列表
//feeling
function get_article_list_url(){
var url_arr = get_list_url(); url_arr.forEach(function (url,index) {
var _index = index+1;
fetch_op(url,_index,g.list_fetch_sec,'list_parse','');
}); ep.after('list_parse', url_arr.length, function (all_arr) {
parseList(all_arr);
console.log('开始抓取详情页');
get_article_detail();
});
} //获取帖子详情
function get_article_detail(){
//分割数组
var obj = {};
g.list_href_arr.forEach(function (url,index) {
var _group = parseInt(index/g.file_data_num)+1;
//没有则新建数组
if(!obj[_group]){
obj[_group] = [];
}
obj[_group].push(url);
}); console.log(obj); var group_no;
for(group_no in obj){
var group_data = obj[group_no];
var len = group_data.length;
//设置计数器
ep_after(group_no,len); //每组再遍历
var j;
var count = 0;
for(j in group_data){
count++;
var url = group_data[j];
var _index = count;
fetch_op(url,_index,g.list_fetch_sec,'detail_parse_'+group_no,group_no);
}
}
} function ep_after(_group,len){
//计数器作用 当emit的detail_parse达到指定的数量时出发回调
ep.after('detail_parse_'+_group, len, function (all_arr) {
console.log('详情页第['+_group+']组抓取完成'); var text_arr = praseDetail(all_arr); if(text_arr.length){
//如果目录不存在 同步创建目录
var dir_path_name = get_dir_path_name(g.pid);
if (!fs.existsSync(dir_path_name)) {
console.log('新建目录: '+dir_path_name);
fs.mkdirSync(dir_path_name);
} console.log('saveing '+'详情页第['+_group+']组');
var save_data = {data:text_arr};
var path_name = get_file_path_name(g.pid,_group);
fs.writeFile(path_name, JSON.stringify(save_data), function (err) {
if (err) throw err;
console.log('save done!');
});
}
});
} function get_file_path_name(dirname,no){
var filename = dirname+'_'+no+'.js';
return path.join(__dirname,'data',dirname,filename);
} function get_dir_path_name(dirname){
return path.join(__dirname,'data',dirname);
} function fetch_op(url,i,sec,emit_name,group_no){
setTimeout(function(){
superagent.get(url)
.end(function (err, res) {
if(res){
console.log('抓取 第['+group_no+']组 ' + url + ' 成功');
ep.emit(emit_name, [url,res.text,group_no]);
}else{
ep.emit(emit_name, [url,'',group_no]);
console.log('抓取 第['+group_no+']组 ' + url + ' 失败');
}
});
},i*sec);
}

注意:以上代码请仅用于学习用途,切勿用于生产环境或者其他非法用途,否则后果请自行承担

superagent 是一个轻量的,渐进式的ajax api,可读性好,学习曲线低,内部依赖nodejs原生的请求api

cheerio 用于解析dom,用法与jquery类似

eventproxy 并发控制(计数器功能)

功能:爬取豆瓣的某版块列表页中的详情的内容,自动创建文件夹并写入文件中存储,可供接口调用。

代码解读:

执行get_article_list_url方法获取列表的url存进g.list_href_arr中,在执行完成的计数器回调中调用get_article_detail方法,该方法首先根据g.file_data_num对g.list_href_arr的url进行分组,
分完组后根据组数控制请求间隔,在执行完成的计数器回调中新建目录,将抓取回来的数据写入文件。
可直接供前端做接口使用。

nodejs 使用superagent+cheerio+eventproxy爬取豆瓣帖子的更多相关文章

  1. 第一个nodejs爬虫:爬取豆瓣电影图片

    第一个nodejs爬虫:爬取豆瓣电影图片存入本地: 首先在命令行下 npm install request cheerio express -save; 代码: var http = require( ...

  2. Node.js爬取豆瓣数据

    一直自以为自己vue还可以,一直自以为webpack还可以,今天在慕课逛node的时候,才发现,自己还差的很远.众所周知,vue-cli基于webpack,而webpack基于node,对node不了 ...

  3. urllib+BeautifulSoup无登录模式爬取豆瓣电影Top250

    对于简单的爬虫任务,尤其对于初学者,urllib+BeautifulSoup足以满足大部分的任务. 1.urllib是Python3自带的库,不需要安装,但是BeautifulSoup却是需要安装的. ...

  4. python2.7爬取豆瓣电影top250并写入到TXT,Excel,MySQL数据库

    python2.7爬取豆瓣电影top250并分别写入到TXT,Excel,MySQL数据库 1.任务 爬取豆瓣电影top250 以txt文件保存 以Excel文档保存 将数据录入数据库 2.分析 电影 ...

  5. python定时器爬取豆瓣音乐Top榜歌名

    python定时器爬取豆瓣音乐Top榜歌名 作者:vpoet mail:vpoet_sir@163.com 注:这些小demo都是前段时间为了学python写的,现在贴出来纯粹是为了和大家分享一下 # ...

  6. Scrapy 通过登录的方式爬取豆瓣影评数据

    Scrapy 通过登录的方式爬取豆瓣影评数据 爬虫 Scrapy 豆瓣 Fly 由于需要爬取影评数据在来做分析,就选择了豆瓣影评来抓取数据,工具使用的是Scrapy工具来实现.scrapy工具使用起来 ...

  7. Python爬取豆瓣音乐存储MongoDB数据库(Python爬虫实战1)

    1.  爬虫设计的技术 1)数据获取,通过http获取网站的数据,如urllib,urllib2,requests等模块: 2)数据提取,将web站点所获取的数据进行处理,获取所需要的数据,常使用的技 ...

  8. scrapy爬虫框架教程(二)-- 爬取豆瓣电影TOP250

    scrapy爬虫框架教程(二)-- 爬取豆瓣电影TOP250 前言 经过上一篇教程我们已经大致了解了Scrapy的基本情况,并写了一个简单的小demo.这次我会以爬取豆瓣电影TOP250为例进一步为大 ...

  9. scrapy爬取豆瓣电影top250

    # -*- coding: utf-8 -*- # scrapy爬取豆瓣电影top250 import scrapy from douban.items import DoubanItem class ...

随机推荐

  1. 一文掌握 Linux 性能分析之 CPU 篇

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. 平常工作会涉及 ...

  2. 墨水屏 E-Paper module【转】

    转自:https://blog.csdn.net/smallmount123/article/details/77489196 https://www.digikey.com/product-deta ...

  3. 【easy】263. Ugly Number 判断丑数

    class Solution { public: bool isUgly(int num) { ) return false; ) return true; && num % == ) ...

  4. vue ajax返回html代码不渲染解决

    <span v-html='lists.html'></span>

  5. 根据SQL_ID查询并杀会话

    Oracle 根据SQL_ID查询并杀会话,清空执行计划缓冲池2018年09月06日 10:31:40 小学生汤米 阅读数:4731. 查询最近五分钟内最高频次SQL,查看event select t ...

  6. Java学习之Java接口回调理解

    Java接口回调 在Java学习中有个比较重要的知识点,就是今天我们要讲的接口回调.接口回调的理解如果解释起来会比较抽象,我一般喜欢用一个或几个经典的例子来帮助加深理解. 举例:老板分派给员工做事,员 ...

  7. 使用Java API操作HDFS文件系统

    使用Junit封装HFDS import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org ...

  8. django admin 修改批量操作内容

    @admin.register(Template) class TemplateAdmin(admin.ModelAdmin): list_display = ( 'cn_title', 'start ...

  9. 微信支付的安全漏洞之XXE

    1.场景:国外安全社区公布微信支付官方SDK存在严重漏洞,可导致商家服务器被入侵(绕过支付的效果).目前,漏洞详细信息以及攻击方式已被公开,影响范围巨大(已确认陌陌.vivo因使用该SDK而存在该漏洞 ...

  10. [方案]基于Zynq WiFi方案构建

    基于Zynq系列,搭建无线传输平台 1) 2.4G 2) 5G AC