nodejs 使用superagent+cheerio+eventproxy爬取豆瓣帖子
//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爬取豆瓣帖子的更多相关文章
- 第一个nodejs爬虫:爬取豆瓣电影图片
第一个nodejs爬虫:爬取豆瓣电影图片存入本地: 首先在命令行下 npm install request cheerio express -save; 代码: var http = require( ...
- Node.js爬取豆瓣数据
一直自以为自己vue还可以,一直自以为webpack还可以,今天在慕课逛node的时候,才发现,自己还差的很远.众所周知,vue-cli基于webpack,而webpack基于node,对node不了 ...
- urllib+BeautifulSoup无登录模式爬取豆瓣电影Top250
对于简单的爬虫任务,尤其对于初学者,urllib+BeautifulSoup足以满足大部分的任务. 1.urllib是Python3自带的库,不需要安装,但是BeautifulSoup却是需要安装的. ...
- python2.7爬取豆瓣电影top250并写入到TXT,Excel,MySQL数据库
python2.7爬取豆瓣电影top250并分别写入到TXT,Excel,MySQL数据库 1.任务 爬取豆瓣电影top250 以txt文件保存 以Excel文档保存 将数据录入数据库 2.分析 电影 ...
- python定时器爬取豆瓣音乐Top榜歌名
python定时器爬取豆瓣音乐Top榜歌名 作者:vpoet mail:vpoet_sir@163.com 注:这些小demo都是前段时间为了学python写的,现在贴出来纯粹是为了和大家分享一下 # ...
- Scrapy 通过登录的方式爬取豆瓣影评数据
Scrapy 通过登录的方式爬取豆瓣影评数据 爬虫 Scrapy 豆瓣 Fly 由于需要爬取影评数据在来做分析,就选择了豆瓣影评来抓取数据,工具使用的是Scrapy工具来实现.scrapy工具使用起来 ...
- Python爬取豆瓣音乐存储MongoDB数据库(Python爬虫实战1)
1. 爬虫设计的技术 1)数据获取,通过http获取网站的数据,如urllib,urllib2,requests等模块: 2)数据提取,将web站点所获取的数据进行处理,获取所需要的数据,常使用的技 ...
- scrapy爬虫框架教程(二)-- 爬取豆瓣电影TOP250
scrapy爬虫框架教程(二)-- 爬取豆瓣电影TOP250 前言 经过上一篇教程我们已经大致了解了Scrapy的基本情况,并写了一个简单的小demo.这次我会以爬取豆瓣电影TOP250为例进一步为大 ...
- scrapy爬取豆瓣电影top250
# -*- coding: utf-8 -*- # scrapy爬取豆瓣电影top250 import scrapy from douban.items import DoubanItem class ...
随机推荐
- mysl 常用函数 union all if ifnull exists case when
1.union all UNION 操作符用于合并两个或多个 SELECT 语句的结果集.请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每条 S ...
- PHP 【五】
函数 如要在页面加载时执行脚本,可以把它放到函数里 函数是通过调用函数来执行的 可在页面的任何位置调用函数 <?phpfunction functionName(){ // 要执行的代码} ...
- Python Libhunt
有一个网站,Python Libhunt: https://python.libhunt.com 这个网站类似于Github的Awesome xxx系列,不过它是随时更新的,排序方式,分类也更加友好. ...
- mycat 测试主从读写分离
下载解压及创建用户组和目录属性 下载地址:1.https://github.com/MyCATApache/Mycat-download.2.http://dl.mycat.io/ wget http ...
- 【原创】运维基础之Docker(7)关于docker latest tag
Docker images have a tag named latest which doesn’t work as you expect.Latest is just a tag with a s ...
- 浮点数运算的精度问题:以js语言为例
在 JavaScript 中整数和浮点数都属于 Number 数据类型,所有数字都是以 64 位浮点数形式储存,即便整数也是如此. 所以我们在打印 1.00 这样的浮点数的结果是 1 而非 1.00 ...
- 在python中使用print()时,raw write()返回无效的长度:OSError: raw write() returned invalid length 254 (should have been between 0 and 127)
写出一个不是code的bug,很烦恼,解决了挺长时间,都翻到外文来看,不过还是解决了,只尝试了一种简单可观的方法,希望对大家有用 我正在使用Django与Keras(tensorflow)来训练一个模 ...
- 虚拟机Ubuntu16.04无法进入图形界面 The system is running in low-graphics mode
安装的虚拟机Ubuntu16.04 64位本可以正常使用,在安装了许多软件包(caffe)后不知哪里配置出现问题,出现The system is running in low-graphics mod ...
- k个一组翻转链表
给出一个链表,每 k 个节点一组进行翻转,并返回翻转后的链表. k 是一个正整数,它的值小于或等于链表的长度.如果节点总数不是 k 的整数倍,那么将最后剩余节点保持原有顺序. 示例 : 给定这个链表: ...
- Selenium的webdriver的常用方法,鼠标事件
就来认识 WebDriver 中最常用的几个方法: get():跳转到的地址clear(): 清除文本. send_keys (value): 模拟按键输入. click(): 单击元素. 示例: f ...