node实现爬虫之遇到的坑
1、需要的工具
1)superagent(超级代理):用来发送http请求,需要注意的是它发出的请求是异步的
安装方式:
npm install superagent 或者yarn add superagent
2)cheerio:用来将浏览器返回的结果转换为html文档,方便解析
安装:
npm install cheerio
2、实现过程
1、需求分析
本次爬虫需要爬取链家上的所有省份、城市及每个城市包含的区域信息
链接地址:https://www.lianjia.com/city/
2、导入所需文件
const express = require('express');
const app = express();
const superagent = require('superagent');
const cheerio = require('cheerio');
let url = 'https://www.lianjia.com/city/';//将要爬取的网站
如图:
3、开始发送请求
//superagent可以发送get,post,put,delete,head请求
superagent.get(url,(err,res)=>{
if(err) console.log(err)
//res.text及为浏览器返回的整个页面字符串
getCity(res.text);
})
superagent的使用详情
4、开始解析
1、使用cheerio解析
//将字符串使用cheerio.load解析为html文档,解析完成之后可以使用类似jQuery的语法获取元素内容
let $ = cheerio.load(res);
2、获取到需要取数据的标签
//获取到值以后使用each方法遍历
$('.city_list_ul .city_firstletter').each((item,ele)=>{}
3、取值
//使用$(ele).text()方法取得标签中的内容
/**
*如果需要取某个属性,比如a标签的herf属性值,则使用$(ele).attr('href')
*/
let city = {
letter: $(ele).children('span').text(),
provinces:[],
}
4、完整代码
let getCity =async (res)=>{
//将字符串使用cheerio.load解析为html文档,解析完成之后可以使用类似jQuery的语法获取元素内容
let $ = cheerio.load(res);
//获取所有的省份列表
$('.city_list_ul .city_firstletter').each((item,ele)=>{
let promise;
//定义所有省份信息
let city = {
letter: $(ele).children('span').text(),
provinces:[],
}
//获取省份信息
$(ele).siblings('.city_list').children('.city_province').each((item,ele)=>{
//定义一个省份列表
let province = {
tit:$(ele).children('.city_list_tit').text(),
cities:[]//放每个省份的城市列表
};
//获取城市列表
$(ele).children('ul').children('li').each((_item,_ele)=>{
//定义城市信息列表
let cList = {
cName:$(_ele).text(),
url:$(_ele).children('a').attr('href'),
areas:[]
}
//获取每个城市的区域列表
promise = new Promise(async(resolve,reject)=>{
let res = await superagent.get(`${cList.url}/ershoufang/`);
let reslut = getAreas(res.text);
cList.areas=reslut;
//console.log(cList.cName)
province.cities.push(cList);
resolve('1')
})
})
promise.then((result)=>{
city.provinces.push(province);
})
})
//将信息插入数据库
promise.then((result)=>{
console.log(city)
})
})
}
//获取区域信息的方法
let getAreas = (res)=>{
let $ = cheerio.load(res);
var area ;
var areas = [];
$('.position > dl:nth-child(2) > dd > div:nth-child(1) > div a').each((item,ele)=>{
area = {
name:$(ele).text(),
url:$(ele).attr('href')
}
areas.push(area)
})
return areas;
}
3、踩坑之旅
1、分析
因为当获取到城市信息后,再去获取该城市对应的区域信息,需要发送新的http请求,而该请求又是异步方法,所以会导致先执行了后续的程序(我将数据保存到了数据库,这时数据库中的区域信息为空),最后才拿到返回的区域信息
2、解决
将发送新请求的方法封装为一个promise对象,再与async和await结合
promise = new Promise(async(resolve,reject)=>{
let res = await superagent.get(`${cList.url}/ershoufang/`);
let reslut = getAreas(res.text);
cList.areas=reslut;
//console.log(cList.cName)
province.cities.push(cList);
resolve('1')
})
等到数据返回后再使用promise.then方法操作
promise.then((result)=>{
city.provinces.push(province);
})