nodejs爬虫——汽车之家所有车型数据
应用介绍
项目Github地址:https://github.com/iNuanfeng/node-spider/
nodejs爬虫,爬取汽车之家所有车型数据 http://www.autohome.com.cn/car/
包括品牌,车系,年份,车型四个层级。
使用的node模块:
superagent, request, iconv; (网络请求模块,iconv用于gbk转码)
cheerio; (和jQuery一样的API,处理请求来的html,省去正则匹配)
eventproxy, async; (控制并发请求,async控制得更细)
async控制并发请求数量为10个(避免封IP与网络错误)
模拟sleep使间隔100ms(不设间隔偶尔会出现dns错误)
去除express模块,该为控制台直接开启爬虫(数据量大,打开网页来开启爬虫可能会由于超时而重新发起访问)
最终使用的模块为: request
, iconv
, cheerio
, async
最后写入到数据库mysql或mongoDB
写入data.json:
项目说明
app.js是爬虫主程序,分步骤抓取数据。
- 抓取品牌和车系
- 抓取年份
- 抓取车型
- 存入本地json文件
- 按需写入数据库(暂时没写)
细节控制
http://www.autohome.com.cn/3128 在售款有2016,2017同时存在
有的车系在售有2016,停售也有2016
抓取失败时重新抓取该页面
项目代码
Github地址:https://github.com/iNuanfeng/node-spider/
app.js:
var express = require('express'),
app = express(),
request = require('request'),
iconv = require('iconv-lite'),
cheerio = require('cheerio'),
async = require("async"), // 控制并发数,防止被封IP
fs = require('fs');
var fetchData = []; // 存放爬取数据
/**
* 睡眠模拟函数
* @param {Number} numberMillis 毫秒
*/
function sleep(numberMillis) {
var now = new Date();
var exitTime = now.getTime() + numberMillis;
while (true) {
now = new Date();
if (now.getTime() > exitTime)
return;
}
}
/**
* 爬取品牌 & 车系
*/
function fetchBrand(req, res) {
var pageUrls = []; // 存放爬取网址
var count = 0; // 总数
var countSuccess = 0; // 成功数
var chars = ['A', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'W', 'X', 'Y', 'Z'];
for (var char of chars) {
count++;
pageUrls.push('http://www.autohome.com.cn/grade/carhtml/' + char + '.html');
}
var curCount = 0;
var reptileMove = function(url, callback) {
var startTime = Date.now(); // 记录该次爬取的开始时间
request({
url: url,
encoding: null // 关键代码
}, function(err, res, body) {
if (err || res.statusCode != 200) {
console.error(err);
console.log('抓取该页面失败,重新抓取该页面..')
reptileMove(series, callback);
return false;
}
var html = iconv.decode(body, 'gb2312')
var $ = cheerio.load(html);
var curBrands = $('dl');
for (var i = 0; i < curBrands.length; i++) {
var obj = {
name: curBrands.eq(i).find('dt div a').text(),
sub: []
}
fetchData.push(obj);
var curSeries = curBrands.eq(i).find('h4 a');
for (var j = 0; j < curSeries.length; j++) {
var obj = {
name: curSeries.eq(j).text(),
sub: [],
url: curSeries.eq(j).attr('href')
}
fetchData[fetchData.length - 1].sub.push(obj);
}
}
countSuccess++;
var time = Date.now() - startTime;
console.log(countSuccess + ', ' + url + ', 耗时 ' + time + 'ms');
callback(null, url + 'Call back content');
});
};
// 使用async控制异步抓取
// mapLimit(arr, limit, iterator, [callback])
// 异步回调
async.mapLimit(pageUrls, 1, function(url, callback) {
reptileMove(url, callback);
}, function(err, result) {
console.log('----------------------------');
console.log('品牌车系抓取完毕!');
console.log('----------------------------');
fetchYear(req, res);
});
}
/**
* 爬取年份
*/
function fetchYear(req, res) {
var count = 0; // 总数
var countSuccess = 0; // 成功数
var seriesArr = [];
// 轮询所有车系
for (var brand of fetchData) {
for (var series of brand.sub) {
count++;
seriesArr.push(series);
}
}
var curCount = 0;
var reptileMove = function(series, callback) {
var startTime = Date.now(); // 记录该次爬取的开始时间
curCount++; // 并发数
request({
url: series.url,
encoding: null // gbk转码关键代码
}, function(err, res, body) {
if (err || res.statusCode != 200) {
console.error(err);
console.log('抓取该页面失败,重新抓取该页面..')
reptileMove(series, callback);
return false;
}
var html = iconv.decode(body, 'gb2312')
var $ = cheerio.load(html);
// 页面默认的数据
var itemList = $('.interval01-list li');
itemList.each(function(){
var year = $(this).find('a').eq(0).text().substr(0, 4);
var name = $(this).find('a').eq(0).text();
var flag = false;
for (item of series.sub) {
if (item.name == year) {
item.sub.push(name);
flag = true;
}
}
if (!flag) {
var obj = {
name: year,
sub: [$(this).find('a').eq(0).text()],
url: ''
};
series.sub.push(obj);
}
});
// 下拉框中的年份抓取
var curYears = $('.cartype-sale-list li');
curYears.each(function(){
var year = $(this).text().substr(0, 4);
var flag = false;
var href = series.url;
var s = href.split('/')[3]; // 从url中截取所需的s参数
var y = ($(this).find('a').attr('data'))
var url = 'http://www.autohome.com.cn/ashx/series_allspec.ashx?s='
+ s + '&y=' + y;
for (item of series.sub) {
if (item.name == year) {
item.url = url;
flag = true;
}
}
if (!flag) {
var obj = {
name: year,
sub: [],
url: url
};
series.sub.push(obj);
}
})
curCount--;
countSuccess++;
var time = Date.now() - startTime;
console.log(countSuccess + ', ' + series.url + ', 耗时 ' + time + 'ms');
sleep(50);
callback(null, series.url + 'Call back content');
});
};
console.log('车系数据总共:' + count + '条,开始抓取...')
// 使用async控制异步抓取
// mapLimit(arr, limit, iterator, [callback])
// 异步回调
async.mapLimit(seriesArr, 10, function(series, callback) {
reptileMove(series, callback);
}, function(err, result) {
// 访问完成的回调函数
console.log('----------------------------');
console.log('车系抓取成功,共有数据:' + countSuccess);
console.log('----------------------------');
fetchName(req, res);
});
}
/**
* 爬取型号
*/
function fetchName(req, res) {
var count = 0; // 总数
var countSuccess = 0; // 成功数
var yearArr = [];
// 轮询所有车系
for (var brand of fetchData) {
for (var series of brand.sub) {
for (var year of series.sub) {
if (year.url) {
count++; // 过滤没有url的年款
yearArr.push(year);
}
}
}
}
var curCount = 0;
var reptileMove = function(year, callback) {
var startTime = Date.now(); // 记录该次爬取的开始时间
curCount++; // 并发数
// console.log(curCount + ': ' + series.url);
request({
url: year.url,
encoding: null // gbk转码关键代码
}, function(err, res, body) {
if (err || res.statusCode != 200) {
console.error(err);
console.log('抓取该页面失败,重新抓取该页面..')
console.log(year)
reptileMove(year, callback);
return false;
}
console.log(countSuccess + ', 抓取: ' + year.url)
var html = iconv.decode(body, 'gb2312')
try {
var data = JSON.parse(html)
} catch(e) {
console.log('error... 忽略该页面');
// reptileMove(series, callback);
curCount--;
callback(null, year.url + 'Call back content');
return false;
}
var specArr = data.Spec;
for (var item of specArr) {
year.sub.push(item.Name);
}
curCount--;
countSuccess++;
var time = Date.now() - startTime;
// sleep(100);
callback(null, year.url + 'Call back content');
});
};
console.log('车型数据总共:' + count + '条,开始抓取...')
// 使用async控制异步抓取
// mapLimit(arr, limit, iterator, [callback])
// 异步回调
async.mapLimit(yearArr, 20, function(year, callback) {
reptileMove(year, callback);
}, function(err, result) {
// 访问完成的回调函数
console.log('----------------------------');
console.log('车型抓取成功,共有数据:' + countSuccess);
console.log('----------------------------');
// res.send(fetchData);
var t = JSON.stringify(fetchData);
fs.writeFileSync('data.json', t);
});
}
/**
* 爬虫入口
*/
fetchBrand();
// 开启express路由,用于浏览器调试
// app.get('/', fetchBrand);
// var server = app.listen(3000, function() {
// console.log('listening at 3000');
// });
nodejs爬虫——汽车之家所有车型数据的更多相关文章
- python3 爬取汽车之家所有车型数据操作步骤(更新版)
题记: 互联网上关于使用python3去爬取汽车之家的汽车数据(主要是汽车基本参数,配置参数,颜色参数,内饰参数)的教程已经非常多了,但大体的方案分两种: 1.解析出汽车之家某个车型的网页,然后正则表 ...
- python爬虫——汽车之家数据
相信很多买车的朋友,首先会在网上查资料,对比车型价格等,首选就是"汽车之家",于是,今天我就给大家扒一扒汽车之家的数据: 一.汽车价格: 首先获取的数据是各款汽车名称.价格范围以及 ...
- python 爬虫 汽车之家车辆参数反爬
水平有限,仅供参考. 如图所示,汽车之家的车辆详情里的数据做了反爬对策,数据被CSS伪类替换. 观察 Sources 发现数据就在当前页面. 发现若干条进行CSS替换的js 继续深入此JS 知道了数据 ...
- PuppeteerSharp+AngleSharp的爬虫实战之汽车之家数据抓取
参考了DotNetSpider示例, 感觉DotNetSpider太重了,它是一个比较完整的爬虫框架. 对比了以下各种无头浏览器,最终采用PuppeteerSharp+AngleSharp写一个爬虫示 ...
- 汽车之家店铺数据抓取 DotnetSpider实战[一]
一.背景 春节也不能闲着,一直想学一下爬虫怎么玩,网上搜了一大堆,大多都是Python的,大家也比较活跃,文章也比较多,找了一圈,发现园子里面有个大神开发了一个DotNetSpider的开源库,很值得 ...
- 汽车之家店铺商品详情数据抓取 DotnetSpider实战[二]
一.迟到的下期预告 自从上一篇文章发布到现在,大约差不多有3个月的样子,其实一直想把这个实战入门系列的教程写完,一个是为了支持DotnetSpider,二个是为了.Net 社区发展献出一份绵薄之力,这 ...
- 汽车之家店铺数据抓取 DotnetSpider实战
一.背景 春节也不能闲着,一直想学一下爬虫怎么玩,网上搜了一大堆,大多都是Python的,大家也比较活跃,文章也比较多,找了一圈,发现园子里面有个大神开发了一个DotNetSpider的开源库,很值得 ...
- 爬虫实战:汽车之家配置页面 破解伪元素和混淆JS
本篇介绍如何破解汽车之家配置页面的伪元素和混淆的JS. ** 温馨提示:如需转载本文,请注明内容出处.** 本文链接:https://www.cnblogs.com/grom/p/9242156.ht ...
- Python爬虫入门教程 64-100 反爬教科书级别的网站-汽车之家,字体反爬之二
说说这个网站 汽车之家,反爬神一般的存在,字体反爬的鼻祖网站,这个网站的开发团队,一定擅长前端吧,2019年4月19日开始写这篇博客,不保证这个代码可以存活到月底,希望后来爬虫coder,继续和汽车之 ...
随机推荐
- [Angular Tutorial] 13 -REST and Custom Services
在这一步中,我们将会改变我们获取数据的方式. ·我们定义一个代表RESTful客户端的自定义服务.使用这个客户端,我们可以用一种更简单的方法向服务端请求数据,而不用处理更底层的$httpAPI,HTT ...
- C# .NET修改注册表
c#修改注册表,需要引用Microsoft.Win32命名空间 using Microsoft.Win32; //声明 ///引用 RegistryKey reg; reg = Registry.Cl ...
- Struts2验证
一.声明式验证 1.字段验证 fielderror的两种显示方式 fielderror的提示信息可以国际化 2.非字段验证:actionErrors / <s:actionerror> 例 ...
- Python3基础 setdefault() 根据键查找值,找不到键会添加
镇场诗: 诚听如来语,顿舍世间名与利.愿做地藏徒,广演是经阎浮提. 愿尽吾所学,成就一良心博客.愿诸后来人,重现智慧清净体.-------------------------------------- ...
- Centos下关于ssh、scp与rsync设置与应用
最近应公司要求,需要对文件数据进行远程传输与备份操作,特此写了一篇文章记录下了关于ssh.scp以及rsync的应用配置全过程,可能过程太过罗嗦,但主要就是想在不遗漏每个过程的情况下对此进行阐述,希望 ...
- CSS中position属性( absolute | relative | static | fixed )详解
我们先来看看CSS3 Api中对position属性的相关定义: static:无特殊定位,对象遵循正常文档流.top,right,bottom,left等属性不会被应用. relative:对象遵循 ...
- Aaron Swartz – 互联网天才开挂的人生历程:每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的?
Aaron说的一句话让我挺有感触的-- 相信你应该真的每时每刻都问自己,现在这世界有什么最重要的事是我能参与去做的? 如果你没在做那最重要的事,那又是为什么? 1986年11月8日,有个叫Aaron ...
- PHP新手之学习类与对象(1)
本文介绍的是PHP程序设计语言中类和对象的基本知识,适合初学者阅读,希望对你有帮助,一起来看. PHP 5 引入了新的对象模型(Object Model).完全重写了 PHP 处理对象的方式,允许更佳 ...
- YII 1.0 验证码
public function actions(){ return array ( ‘captcha’=> array( ‘class’=> ‘CCatpchaAction’, ‘heig ...
- Keepalived+Nginx提供前端负载均衡+主从双机热备+自动切换
原文链接:http://unun.in/linux/156.html 方案: 采用两台Nginx服务器作为前端,提供静态web内容,分发web请求,一主一从,Keepalived实现状态监测,保证 N ...