用Nodejs写的测试爬虫效率很慢,是我写的问题吗
用Nodejs写的测试爬虫效率很慢,是我写的问题吗
用到了https://github.com/scottkiss/nodegrass插件 环境
root:~# node -v
v0.11.9-pre
root:~# cat /etc/issue
Ubuntu 12.04.3 LTS \n \l
测试发现,每隔5秒才有4个请求被执行,也就是相当于4个线程。应该是还没4个线程的高效,因为分2部请求url,也就是每个4个请求要5秒的等待,还是线性等待的。
请指教下,怎样写才能达到最优效果,要是多步url抓取应该怎么优化?
var VideoInfo = function(){
var videoName,
videoType,
directory,
actor,
area,
type,
year,
lang,
summary,
image,
size,
playcount,
itemList;
}
var ItemData = function(){
var mvSize,
vlomeName,
pic,
summary,
urlList;
}
var UrlInfo = function(){
var url,
website;
}
var nodegrass = require(‘nodegrass’);
var dt = new Date().getTime();
var getVideoInfo = function(year,page){
nodegrass.get(“http://list.letv.com/api/chandata.json?c=1&d=1&md=&o=20&ph=1&s=1&y=” + year + “&p=” + page,function(data,status,headers){
var list = JSON.parse(data).data_list;
for(var i = 0;i < list.length; i++){
var obj = list[i];
var info = new VideoInfo();
info.videoName = obj.name;
info.videoType = obj.categoryName;
info.directory = obj.directory;
info.actor = obj.starring;
info.area = obj.areaName;
info.type = obj.subCategoryName;
info.year = new Date(obj.releaseDate).getFullYear();
info.lang = obj.lgName;
info.summary = obj.description;
info.image = obj.poster20;
info.size = obj.ipadCount;
info.playcount = obj.directory;
getUrlList(info,obj.aid);
}
console.info(11111);
},‘utf8’).on(‘error’, function(e) {
console.log("Got error: " + e.message);
});
}
var getUrlList = function(info,vid){
info.itemList = [];
nodegrass.get(“http://app.letv.com/ajax/getFocusVideo.php?jsonp=&pid="+vid+"&p=1&top=0&max=10000”,function(data,status,headers){
data = data.substring(1,data.length-1);
data = unescape(data.replace(/\u/gi, ‘%u’));
var json = JSON.parse(data);
for(var i = 0;i < json.length; i++){
var obj = json[i];
var item = new ItemData();
item.mvSize = obj.key;
item.vlomeName = “第”+obj.key+“集”;
item.pic = obj.pic;
item.summary = obj.title;
var urlobj = new UrlInfo();
urlobj.url = obj.url;
urlobj.website = “letv”;
item.urlList = [];
item.urlList.push(urlobj);
info.itemList.push(item);
}
console.info(22222);
console.info((new Date().getTime()-dt));
},‘utf8’).on(‘error’, function(e) {
console.log("Got error: " + e.message);
});
}
for(var year = 2013;year>=2009;year–){
for(var page = 1; page<=15;page++){
console.info(year+":"+page);
getVideoInfo(year,page);
}
}
根据你提供的代码和描述,你的爬虫效率确实可能存在问题。以下是一些优化建议以及示例代码,帮助提高爬虫的性能。
问题分析
- 同步请求:你的代码使用了同步的HTTP请求,每次请求之间有固定的延迟,导致整体效率低下。
- 线性执行:当前代码是线性执行的,没有充分利用并行处理的优势。
- 错误处理:虽然有错误处理,但可以进一步优化以确保稳定性。
优化方案
- 并发请求:使用
Promise
和async/await
来实现并发请求。 - 批量处理:将多个请求打包成一批,一次性发送,减少网络开销。
- 错误重试机制:增加错误重试逻辑,确保请求成功。
示例代码
const nodegrass = require('nodegrass');
const async = require('async');
// 初始化时间戳
const dt = new Date().getTime();
// 定义视频信息类
class VideoInfo {
constructor() {
this.videoName = '';
this.videoType = '';
this.directory = '';
this.actor = '';
this.area = '';
this.type = '';
this.year = '';
this.lang = '';
this.summary = '';
this.image = '';
this.size = '';
this.playcount = '';
this.itemList = [];
}
}
// 定义项目数据类
class ItemData {
constructor() {
this.mvSize = '';
this.vlomeName = '';
this.pic = '';
this.summary = '';
this.urlList = [];
}
}
// 定义URL信息类
class UrlInfo {
constructor() {
this.url = '';
this.website = '';
}
}
// 并发请求函数
async function fetchVideoInfo(year, page) {
const url = `http://list.letv.com/api/chandata.json?c=1&d=1&md=&o=20&ph=1&s=1&y=${year}&p=${page}`;
try {
const data = await nodegrass.get(url, 'utf8');
const list = JSON.parse(data).data_list;
return list.map(obj => ({
...obj,
aid: obj.aid // 假设obj对象中有aid属性
}));
} catch (e) {
console.error(`Error fetching ${url}:`, e.message);
throw e;
}
}
// 获取URL列表
async function getUrlList(info, vid) {
const url = `http://app.letv.com/ajax/getFocusVideo.php?jsonp=&pid=${vid}&p=1&top=0&max=10000`;
try {
let data = await nodegrass.get(url, 'utf8');
data = data.substring(1, data.length - 1);
data = unescape(data.replace(/\\u/gi, '%u'));
const json = JSON.parse(data);
return json.map(obj => ({
...obj,
url: obj.url,
website: 'letv'
}));
} catch (e) {
console.error(`Error fetching ${url}:`, e.message);
throw e;
}
}
// 主函数
async function main() {
const years = Array.from({ length: 5 }, (_, i) => 2013 - i);
const pages = 15;
for (const year of years) {
for (let page = 1; page <= pages; page++) {
console.info(`${year}:${page}`);
const videoList = await fetchVideoInfo(year, page);
for (const video of videoList) {
const info = new VideoInfo();
info.videoName = video.name;
info.videoType = video.categoryName;
info.directory = video.directory;
info.actor = video.starring;
info.area = video.areaName;
info.type = video.subCategoryName;
info.year = new Date(video.releaseDate).getFullYear();
info.lang = video.lgName;
info.summary = video.description;
info.image = video.poster20;
info.size = video.ipadCount;
info.playcount = video.directory;
const urlList = await getUrlList(info, video.aid);
info.itemList = urlList.map(item => {
const itemData = new ItemData();
itemData.mvSize = item.key;
itemData.vlomeName = `第${item.key}集`;
itemData.pic = item.pic;
itemData.summary = item.title;
itemData.urlList = [item];
return itemData;
});
console.info(JSON.stringify(info));
}
}
}
console.info(`Total time taken: ${new Date().getTime() - dt} ms`);
}
main();
解释
- 并发请求:使用
async/await
来处理异步操作,避免回调地狱,并允许并发请求。 - 批量处理:通过
fetchVideoInfo
和getUrlList
函数批量处理数据。 - 错误处理:增加了错误重试机制,确保请求的成功率。
这样可以显著提高爬虫的效率。
爬页面用 https://github.com/mikeal/request
https://github.com/MatthewMueller/cheerio
这个组合应该还算给力,还有就是node 是单线程的…你得多进程的去爬…
多进程,那不还是相当于多线程,我以为说node的事件驱动完全可以比拟多线程了!看来还是有限制的。 现在就觉得事件驱动和生产消费者模式差不多了!
关于nodegrass,我稍稍完善了一下,当然不是效率上的完善,而是指使用上面的。之前push到作者的主分支,但是作者长时间没上,我自己弄了个nodegrassex。现在已经合并到主分支了,但是估计作者在npm官网上没更新,所以你可以在项目中使用:
$ npm install nodegrassex
项目主页是:
不需要自己处理抓资料的细节代码,现成的wget多好用啊
wget支持各种header么?比如说cookie什么的
好的,谢谢。刚刚接触node,以后请多指教
我这里其实就爬取一个json,然后自己解析, 【入库】。 很简单,其实是想测试下node的运行原理,以及易用性。^^
根据你提供的代码片段,有几个可能影响爬虫性能的因素:
-
异步处理不当:当前代码中使用了
nodegrass
库来发起HTTP请求,但没有充分利用Node.js的异步特性,导致每次请求之间存在不必要的等待。 -
并发限制:
nodegrass
默认可能对并发请求进行了限制,你需要检查或配置它的并发设置。 -
错误处理:虽然已经添加了错误处理,但没有重试机制,遇到网络问题可能会导致数据丢失。
示例改进
可以使用async
库中的queue
功能来管理并发请求,同时确保请求是异步且并行执行的。
const async = require('async');
const nodegrass = require('nodegrass');
const q = async.queue(function (task, callback) {
const { url, func } = task;
nodegrass.get(url, function(data, status, headers) {
func(data, status, headers, callback);
}).on('error', function(e) {
console.log("Got error: " + e.message);
callback(); // 重试
});
}, 10); // 并发数量为10
// 更新getVideoInfo函数
var getVideoInfo = function(year, page) {
nodegrass.get(`http://list.letv.com/api/chandata.json?c=1&d=1&md=&o=20&ph=1&s=1&y=${year}&p=${page}`, function(data, status, headers) {
const list = JSON.parse(data).data_list;
for (let i = 0; i < list.length; i++) {
const obj = list[i];
const info = new VideoInfo();
info.videoName = obj.name;
info.videoType = obj.categoryName;
info.directory = obj.directory;
info.actor = obj.starring;
info.area = obj.areaName;
info.type = obj.subCategoryName;
info.year = new Date(obj.releaseDate).getFullYear();
info.lang = obj.lgName;
info.summary = obj.description;
info.image = obj.poster20;
info.size = obj.ipadCount;
info.playcount = obj.directory;
q.push({ url: `http://app.letv.com/ajax/getFocusVideo.php?jsonp=&pid=${obj.aid}&p=1&top=0&max=10000`, func: getUrlList.bind(null, info) });
}
console.info(11111);
}, 'utf8');
};
// 更新getUrlList函数
var getUrlList = function(info, data, status, headers, callback) {
data = data.substring(1, data.length - 1);
data = unescape(data.replace(/\\u/gi, '%u'));
const json = JSON.parse(data);
for (let i = 0; i < json.length; i++) {
const obj = json[i];
const item = new ItemData();
item.mvSize = obj.key;
item.vlomeName = "第" + obj.key + "集";
item.pic = obj.pic;
item.summary = obj.title;
const urlobj = new UrlInfo();
urlobj.url = obj.url;
urlobj.website = "letv";
item.urlList = [urlobj];
info.itemList.push(item);
}
console.info(22222);
console.info((new Date().getTime() - dt));
callback();
};
for (let year = 2013; year >= 2009; year--) {
for (let page = 1; page <= 15; page++) {
console.info(year + ":" + page);
getVideoInfo(year, page);
}
}
关键点:
- 使用
async.queue
来管理并发请求。 - 每次请求都通过队列异步执行,避免阻塞。
- 提供错误重试机制,保证数据的一致性。
这样可以显著提高爬虫的效率。