1、主进程

const http = require('http');
const fs = require('fs');
const cheerio = require('cheerio');
const request = require('request');
const makePool = require('./pooler')
const runJob = makePool('./worker')
var i = 0;
var url = "http://xxx.com/articles/";
//初始url
let g = '';
function fetchPage(x) { //封装了一层函数
console.log(x)
if(!x || x==''){
g.next()
return
}
startRequest(x);
} function startRequest(x) {
//采用http模块向服务器发起一次get请求
return http.get(x, function (res) {
var html = ''; //用来存储请求网页的整个html内容
var titles = [];
res.setEncoding('utf-8'); //防止中文乱码
//监听data事件,每次取一块数据
res.on('data', function (chunk) {
html += chunk;
});
//监听end事件,如果整个网页内容的html都获取完毕,就执行回调函数
res.on('end', function () {
var $ = cheerio.load(html); //采用cheerio模块解析html var time = new Date();
var p = $('.content p')
p.each((index,item)=>{
if($(item).find('strong').length) {
var fex_item = {
//获取文章的标题
title: $(item).find('strong').text().trim(),
//获取文章发布的时间
time: time,
//获取当前文章的url
link: $($(item).children('a').get(0)).attr('href'),
des:$(item).children().remove()&&$(item).text(),
//i是用来判断获取了多少篇文章
i: index+1 };
runJob(fex_item,(err,data)=>{
if(err) console.error('get link error')
console.log('get link ok')
})
} })
g.next()
}) }).on('error', function (err) {
console.log(err);
g.next()
}); }
function* gen(urls){
let len = urls.length;
for(var i=0;i<len;i++){
yield fetchPage(urls[i])
}
} function getUrl(x){
//采用http模块向服务器发起一次get请求
http.get(x, function (res) {
var html = ''; //用来存储请求网页的整个html内容
var titles = [];
res.setEncoding('utf-8'); //防止中文乱码
//监听data事件,每次取一块数据
res.on('data', function (chunk) {
html += chunk;
});
//监听end事件,如果整个网页内容的html都获取完毕,就执行回调函数
res.on('end', function () {
var $ = cheerio.load(html); //采用cheerio模块解析html var time = new Date();
var lists = $('.articles .post-list li')
var urls = [];
lists.each(function(index,item){
if($(item).find('a').length) {
var url = 'http://xxxx.com'+$($(item).children('a').get(0)).attr('href');
if(url)
urls.push(url); //主程序开始运行
}
})
g = gen(urls)
g.next()
}) }).on('error', function (err) {
console.log(err);
});
} getUrl(url)

2、创建进程池

const cp = require('child_process')
const cpus = require('os').cpus().length; module.exports = function pooler(workModule){
let awaiting = [],readyPool = [],poolSize = 0;
return function doWork(job,cb){
if(!readyPool.length&&poolSize>cpus)
return awaiting.push([doWork,job,cb]) let child = readyPool.length ? readyPool.shift():(poolSize++,cp.fork(workModule))
let cbTriggered = false;
child.removeAllListeners()
.once('error',function(err){
if(!cbTriggered){
cb(err)
cbTriggered = true
}
child.kill()
})
.once('eixt',function(){
if(!cbTriggered)
cb(new Error('childe exited with code:'+code))
poolSize--;
let childIdx = readyPool.indexOf(child)
if(childIdx > -1)readyPool.splice(childIdx,1)
})
.once('message',function(msg){
cb(null,msg)
cbTriggered = true
readyPool.push(child)
if(awaiting.length)setImmediate.apply(null,awaiting.shift())
})
.send(job)
}
}

3、工作进程接受消息并处理内容

const fs = require('fs')
process.on('message',function(job){
let _job = job
let x = 'TITLE:'+_job.title+'\n' + 'LINK:'+_job.link + '\n DES:'+_job.des+'\n SAVE-TIME:'+_job.time fs.writeFile('../xx/data/' + _job.title + '.txt', x, 'utf-8', function (err) {
if (err) {
console.log(err);
}
});
process.send('finish')
})

从零系列--node爬虫利用进程池写数据的更多相关文章

  1. python系列之 - 并发编程(进程池,线程池,协程)

    需要注意一下不能无限的开进程,不能无限的开线程最常用的就是开进程池,开线程池.其中回调函数非常重要回调函数其实可以作为一种编程思想,谁好了谁就去掉 只要你用并发,就会有锁的问题,但是你不能一直去自己加 ...

  2. Python之进程 3 - 进程池和multiprocess.Poll

    一.为什么要有进程池? 在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星任务.那么在成千上万个任务需要被执行的时候,我们就需要去创建成千上万个进程么?首先,创建进程需要消耗 ...

  3. (7)Pool进程池

    (1)# 开启过多的进程并不一定提高你的效率 因为进程池可以实现并行的概念,比Process单核并发的速度要快 # 如果cpu负载任务过多,平均单个任务执行的效率就会低,反而降低执行速度. 1个人做4 ...

  4. python基础-UDP、进程、进程池、paramike模块

    1 基于UDP套接字1.1 介绍 udp是无连接的,是数据报协议,先启动哪端都不会报错 udp服务端 import socket sk = socket() #创建一个服务器的套接字 sk.bind( ...

  5. 运用pool进程池启动大量子进程

    # Pool进程池类 from multiprocessing import Pool import os import time import random def run(index): prin ...

  6. 进程池(Pool)

    进程池用于进程维护, 当使用时,将会去进程池取数据 from multiprocessing import Pool, Processimport os, time def f(i): time.sl ...

  7. python爬虫之线程池和进程池

    一.需求 最近准备爬取某电商网站的数据,先不考虑代理.分布式,先说效率问题(当然你要是请求的太快就会被封掉,亲测,400个请求过去,服务器直接拒绝连接,心碎),步入正题.一般情况下小白的我们第一个想到 ...

  8. python编程系列---进程池的优越性体验

    1.通过multiprocessing.Process()类创建子进程 import multiprocessing, time, os, random def work(index): " ...

  9. 进程池与回调函数与正则表达式和re爬虫例子

    # 使用进程池的进程爬取网页内容,使用回调函数处理数据,用到了正则表达式和re模块 import re from urllib.request import urlopen from multipro ...

随机推荐

  1. iOS 开源库系列 Aspects核心源码分析---面向切面编程之疯狂的 Aspects

    Aspects的源码学习,我学到的有几下几点 Objective-C Runtime 理解OC的消息分发机制 KVO中的指针交换技术 Block 在内存中的数据结构 const 的修饰区别 block ...

  2. Spring-IOC BeanFactory运行时动态注册bean

    在spring运行时,动态的添加bean,dapeng框架在解析xml的字段时,使用到了动态注册,注册了一个实现了FactoryBean类! 定义一个没有被Spring管理的Controller pu ...

  3. 【转】Android中获取应用程序(包)的信息-----PackageManager的使用(一)

    转载请注明出处:http://blog.csdn.net/qinjuning       本节内容是如何获取Android系统中应用程序的信息,主要包括packagename.label.icon.占 ...

  4. 利用Zookeeper实现分布式锁及服务注册中心

    对于Zookeeper的定义以及原理,网上已经有很多的优秀文章对其进行了详细的介绍,所以本文不再进行这方面的阐述. 本文主要介绍一些基本的准备工作以及zookeeper.net的使用. 本文源代码gi ...

  5. Week4:Neural Networks难点记录

    为什么θ的维度是Sj+1*(Sj+1)? 课堂PPT没有两层单元个数不同的状态,故举一个例子就知道了 and sj+1=4, so sj+1×(sj+1)=4×3. 编程作业: Multi-class ...

  6. Kafka设计解析(十七)Kafka 0.11客户端集群管理工具AdminClient

    转载自 huxihx,原文链接 Kafka 0.11客户端集群管理工具AdminClient 很多用户都有直接使用程序API操作Kafka集群的需求.在0.11版本之前,kafka的服务器端代码(即添 ...

  7. Spring源码分析(十二)FactoryBean的使用

    摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 一般情况下,Spring通过反射机制利用bean的class属性指定实现 ...

  8. html5复习--canvas

    一.简介 <canvas>是html5新增的标签,可以使用脚本(通常为JavaScript)在其中绘制图像的 HTML 元素.它可以用来制作照片集或者制作简单(也不是那么简单)的动画,甚至 ...

  9. 使用JS与jQuery实现文字逐渐出现特效

    该需求出现原因:想要实现一个在一开始加载页面时就出现一行文字逐渐出现的效果,且需要实现的是一种逐渐的过渡出现效果为不是一种生硬的突然间歇性出现.于是便开始尝试利用最近正在学习的jQuery技术和JS实 ...

  10. git merge的使用

    在实际开发中经常会用到git merge操作.但很多情况下我们并不想合并后直接提交,这里介绍git merge的两个常用参数: --no-commit --no-commit 参数使得合并后,为了防止 ...