网站首页 > 技术文章 正文
上文我们说到用 node 爬了博客园 20*150 条数据,但是这 150 次请求完全是并发的,如果某些网站有 "反爬" 机制,就很有可能封锁你的 IP。这样的情况下我们就可以使用async模块。
还是以具体例子来说明,一直爬博客园也不好,我们换个爬爬。在 Nodejs - 如何用 eventproxy 模块控制并发一文中介绍了如何用 eventproxy 模块并发完成 10 条请求,文中爬的是zoj,后来发现 zoj 有时网络太差,便改爬hdoj,完整代码可以猛戳这里,具体实现可以参考上文。今天我们要完成的事情还是类似,爬取 hdoj 1000-1099 100道题目的最优解提交时间,并发量控制为 5(本来想爬取最优解的用户 id,发现 hdoj 用的是 gbk,中文会乱码,这个问题暂时放一放)。
主要用到的是 async 的 mapLimit(arr, limit, iterator, callback)
接口。用法很简单,直接看代码:
第一个参数 urls 为数组,保存了需要爬取页面的 100 个 url,第二个参数 5 表示并发爬取数量为 5,第三个参数是迭代函数(每个 url 需要执行这个函数),其第一个参数 url,是 urls 数组的每个 item,第二个参数 callback 与 mapLimit 方法第四个参数有关,callback 会往 result 参数里存放数据。如何理解?callback 是第三个参数 iterator 的回调,以爬虫为例,爬完页面肯定会分析一些数据,然后保存,执行 callback 函数就能把结果保存在 result(第四个参数函数中的参数) 中。
说完 mapLimit,我们从头开始这次爬虫。
首先,我们采集 100 个需要爬取页面的 url,保存在 urls 数组中:
// 需要爬的网址
function getUrls {
var urls =
, baseUrl = 'http://acm.hdu.edu.cn/statistic.php?pid=';
for (var i = 1000; i < 1010; i++) {
var tmp = baseUrl + i;
urls.push(tmp);
}
return urls;
}
接着我们用 async 进行异步爬取:
app.get('/', function (req, res, next) {
var urls = getUrls;
// 并发量控制为 5
async.mapLimit(urls, 5, function (url, callback) {
fetchUrl(url, callback);
}, function (err, result) {
res.send(result);
});
});
我们再来看 fetchUrl 函数:
// 抓取网页内容
function fetchUrl(url, callback) {
superagent.get(url)
.end(function (err, res) {
var page = res.text;
// 页面分析,返回需要的数据
var postTime = analyze(page);
callback(null, postTime);
});
}
这里要注意的是 callback 中的第二个参数,其实 postTime 已经储存在了 mapLimit 方法第四个参数的 result 参数中。当然,你完全可以自定义变量来保存数据。
代码比较简单,完整代码可以猛戳 这里。
还有个问题是,什么时候用 eventproxy,什么时候使用 async 呢?它们不都是用来做异步流程控制的吗?
alsotang 的答案是:
当你需要去多个源(一般是小于 10 个)汇总数据的时候,用 eventproxy 方便;当你需要用到队列,需要控制并发数,或者你喜欢函数式编程思维时,使用 async。大部分场景是前者,所以我个人大部分时间是用 eventproxy 的。
经过测试,并发量确实为 5。具体测试方法,定义一个全局(global)变量 num,初始化为 0。当其进入 fetchUrl 函数时,num 自加,同时打印此时并发的数量,在 superagent 的 end 回调中,num 自减,因为此时该并发已经结束。
猜你喜欢
- 2025-07-14 CompletableFuture.failedFuture 在 java 8中的替代方法
- 2025-07-14 harmony-utils之PickerUtil,拍照、文件选择和保存,工具类
- 2025-07-14 Java异步编程7大夺命坑!阿里P8血泪逃生指南(附性能核弹包)
- 2025-07-14 Nuxt错误处理完整指南:从基础到高级实践
- 2025-07-14 webpack的几个常见loader源码浅析,动手实现一个md2html-loader
- 2025-07-14 async/await 在 C# 语言中是如何工作的?(中)
- 2025-07-14 Vue3 远程加载组件(vue3远程加载组件)
- 2025-07-14 webpack 常见loader原理剖析,动手实现一个md2html的loader
- 2025-07-14 Vue 3最佳实践:10万QPS性能调优手册
- 2025-07-14 Teleport+Suspense:动态组件加载新方案
- 1512℃桌面软件开发新体验!用 Blazor Hybrid 打造简洁高效的视频处理工具
- 556℃Dify工具使用全场景:dify-sandbox沙盒的原理(源码篇·第2期)
- 504℃MySQL service启动脚本浅析(r12笔记第59天)
- 482℃服务器异常重启,导致mysql启动失败,问题解决过程记录
- 480℃启用MySQL查询缓存(mysql8.0查询缓存)
- 460℃「赵强老师」MySQL的闪回(赵强iso是哪个大学毕业的)
- 441℃mysql服务怎么启动和关闭?(mysql服务怎么启动和关闭)
- 438℃MySQL server PID file could not be found!失败
- 最近发表
- 标签列表
-
- c++中::是什么意思 (83)
- 标签用于 (65)
- 主键只能有一个吗 (66)
- c#console.writeline不显示 (75)
- pythoncase语句 (81)
- es6includes (73)
- windowsscripthost (67)
- apt-getinstall-y (86)
- node_modules怎么生成 (76)
- c++int转char (75)
- static函数和普通函数 (76)
- el-date-picker开始日期早于结束日期 (70)
- js判断是否是json字符串 (67)
- checkout-b (67)
- c语言min函数头文件 (68)
- asynccallback (71)
- localstorage.removeitem (74)
- vector线程安全吗 (70)
- & (66)
- java (73)
- js数组插入 (83)
- mac安装java (72)
- eacces (67)
- 查看mysql是否启动 (70)
- 无效的列索引 (74)