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 ...
随机推荐
- LeetCode第十四题-字符串数组中最长的共同前缀
Longest Common Prefix 问题简介: 编写一个函数来查找字符串数组中最长的公共前缀字符串,如果没有公共前缀,则返回空字符串"" 举例: 1: 输入: [“xwq” ...
- Basic Calculator I && II && III
Basic Calculator I Implement a basic calculator to evaluate a simple expression string. The expressi ...
- ubuntu 1604安装docker-ce 记录
以前在linux 内核为2.x的低版本中安装过docker,但是很多功能特性都无法使用,本次是在ubuntu 16.04上安装,记录安装过程,方便后续查阅. 一.安装前准备: 1. 安装包,允许 ap ...
- theos安装详解
1.安装 Homebrew 安装命令官方网站 https://brew.sh 2.利用
- 移动端遮罩及阻止页面滑动,实用!!! 我们经常做一个fixed定位的遮罩和一个提示弹框,这时就要用到。记录--
document.body.style.height = '100%'; document.body.style.overflow = 'hidden'; document.getElementByI ...
- Python学习笔记九
Python学习笔记之九 为什么要有操作系统 管理硬件,提供接口. 管理调度进程,并且将多个进程对硬件的竞争变得有序. 操作系统发展史 第一代计算机:真空管和穿孔卡片 没有操作系统,所有的程序设计直接 ...
- 解决 for xml path encode 的问题
select stuff( (select ', <' + name + '>' from sys.databases where database_id > 4 order by ...
- 如何手写Ajax实现异步刷新
所谓的异步刷新,就是不刷新整个网页进行更新数据. 只有通过js才能实现Ajax,进而实行异步刷新 表单提交数据和Ajax提交数据的区别:表单提交是提交的整个页面中的数据,提交数据之后会抛弃之前的页面( ...
- Python学习(三十三)—— Django之ORM
Object Relational Mapping(ORM) 一.ORM介绍 ORM概念 对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系 ...
- Django搭建博客文章---模型层
页面展示所需字段 1.文章标题---文本类型 2.文章摘要---文本类型 3.文章内容--文本类型 4.唯一的ID标记---int数字类型(自增.主键) 5.发布日期--日期类型 模型层定义字段 1. ...