一次node.js爬虫实践

node.js爬虫实践

小猫的毕设系统需要一些疾病数据信息,于是决定从某医药网站上爬取相关数据。

小猫以前学爬虫的时候,用的是python,这次决定用node.js试试。

首先说说用node做一个简单的爬虫需要的库

  • request:用于http请求
  • iconv-lite:用于转换字符编码
  • cheerio:用于解析html,
  • node-xlsx:将数据生成excel文件

一个简单的爬虫其实就是爬取网页,解析数据,存储数据。

小猫使用request获取网页,当然也可以选择其他方式,比如node自带的http模块。因为小猫爬取的网页字符编码为‘gb2312’,因此使用iconv-lite转换为utf8。并且需要注意的是使用request不传encoding字段时有一个默认编码,因此需要传入{encoding: null}的参数去除默认格式iconv-lite才会生效。

const request = require('request');
const iconv = require('iconv-lite');

// 获得html
const requestPromise = (url)=>{
    
    
    return new Promise((resolve,reject)=>{
    
    
        request(url,{
    
    encoding: null}, (error, response, body)=>{
    
    
            if(response.statusCode === 200) {
    
    
                 //  编码格式转换 ,{encoding: null}解除默认格式
                const bufs = iconv.decode(body, 'gb2312')
                const html = bufs.toString('utf8')
                resolve(html)
            }else {
    
    
                reject()
            }
        })
    })
    
}

获取到html后使用cheerio.load解析html。cheerio.load可以让使用者通过jquery的方式获取想要的元素,比如$('.class').text()获取文字。

将获取到的数据整理好后,使用node-xlsx的xlsx.build创建一个buffer,最后用node的fs文件系统写入文件。

const cheerio = require('cheerio');
var fs = require('fs');
var xlsx = require('node-xlsx');
// 解析数据
const getData = async(url) =>{
    
    
    const html = await requestPromise(url)
    const $ = cheerio.load(html) // 解析html
    // 疾病名称
    var disease = $('body > div.wrap.mt10.clearfix.graydeep > div.main-sub.fl > div.jib-janj.bor.clearfix > div.jib-articl.fr.f14 > div.jib-articl-con.jib-lh-articl > strong').text()
    
    // 存进excel
    var buffer = xlsx.build([
        {
    
    
            name:'sheet1',
            data:[[disease]]
        }        
    ]);
    fs.writeFileSync('diseaseData.xlsx',buffer);
}

下面是完整的demo代码,其中省去了部分数据处理,仅保留一个disease字段,并且隐去了具体网址。

const request = require('request');
const iconv = require('iconv-lite');
const cheerio = require('cheerio');
var fs = require('fs');
var xlsx = require('node-xlsx');
// 获得html
const requestPromise = (url)=>{
    
    
    return new Promise((resolve,reject)=>{
    
    
        request(url,{
    
    encoding: null}, (error, response, body)=>{
    
    
            if(response.statusCode === 200) {
    
    
                 //  编码格式转换 ,{encoding: null}解除默认格式
                const bufs = iconv.decode(body, 'gb2312')
                const html = bufs.toString('utf8')
                resolve(html)
            }else {
    
    
                reject()
            }
        })
    })
    
}
// 解析数据
const getData = async(url) =>{
    
    
    const html = await requestPromise(url)
    const $ = cheerio.load(html) // 解析html
    // 疾病名称
    var disease = $('body > div.wrap.mt10.clearfix.graydeep > div.main-sub.fl > div.jib-janj.bor.clearfix > div.jib-articl.fr.f14 > div.jib-articl-con.jib-lh-articl > strong').text()
    
    // 存进excel
    var buffer = xlsx.build([
        {
    
    
            name:'sheet1',
            data:[[disease]]
        }        
    ]);
    // fs.appendFileSync('diseaseData.xlsx',buffer);
    fs.writeFileSync('diseaseData.xlsx',buffer);
}


// 循环存入url,并进行数据请求
var urls = []
for (let i = 50; i<53;i++) {
    
    
    const url = `http://xxx/${
      
      i}.htm`
    urls.push(url)
}
// 等待一个请求处理结束后再进行第二次请求
urls.reduce((rs,url)=>{
    
    
    return rs.then(res => {
    
    
        return new Promise(async (resolve)=>{
    
    
            await getData(url)
            resolve()
        })
    })
},Promise.resolve())

两个note:

  • 为什么使用reduce和Promise,而不直接在for循环中getData(url)

    reduce方法作为累加器可以将数组的上一次计算结果传给下一项,第二个参数接受Promise.resolve()的初始值,在回调函数中只有当一次数据请求和处理完成并且resolve()后才会进行下一项的处理,这样就可以逐次请求数组中的url,避免给服务器造成较大压力。

  • 使用fs.writeFileSync会覆盖已有文件,追加文件本来应该使用appendFileSync,但在本例中遇到问题

    一开始是想使用appendFileSync向excel中追加数据的,但是生成的文件一直显示损坏,似乎是写入错误。一直没有找到问题的原因。因为这不是小猫此次毕设的主要任务,决定先留一个坑。后来迫于时间压力,选择了先将适量数据存入一个数组,然后使用writeFileSync生成多个excel,最后在excel软件中一次性合并多张表。

    本次共收集10000余条数据,最后晒张图。

猜你喜欢

转载自blog.csdn.net/qq_44041514/article/details/114386300