使用Node爬虫中国所有大学数据(二)

本文介绍根据省份爬取大学列表和每个大学的百度百科详情。

创建大学实体类

大学的信息比较多,因为我们想展示比较详细的大学数据。

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class College {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column({
    nullable: true,
  })
  createdYear: string;

  @Column({
    nullable: true
  })
  type: string;


  @Column({
    nullable: true,
    type: 'simple-array'
  })
  tags: string[];

  @Column({
    nullable: true
  })
  category: string;


  @Column({
    nullable: true,
    type: 'simple-array'
  })
  schoolFellow: string;

  @Column({
    nullable: true
  })
  department: string;

  @Column({
    nullable: true
  })
  website: string;

  @Column({
    nullable: true,
  })
  code: string;

  @Column({
    nullable: true
  })
  motto: string;

  @Column({
    nullable: true
  })
  location: string;

  @Column({
    nullable: true
  })
  provinceAbbr: string;

  @Column({
    nullable: true
  })
  create_ts: string;

  @Column({
    nullable: true
  })
  update_ts: string;


  @Column({
    nullable: true,
    type: 'text'
  })
  des: string;

}

爬取大学列表

我们先从https://daxue.eol.cn/爬取每个省份的大学列表,然后获取到name,location,provinceAbbr,create_ts这几个简单字段,然后使用this.collegeRepository.save(collegeListInfo)将大学列表存入数据库。

注意解析DOM的each方法里使用了async类型匿名回调,因为回调函数里面我们保存到数据库的操作是await方法。

 async crawlerCollegeaNameByProvince(province) {
    const a = () => {
      return new Promise((reslove, reject) => {
        var c = new Crawler({
          maxConnections: 10,
          callback: (error, res, done) => {
            if (error) {
              console.log(error);
              reject(error)
            } else {
              var $ = res.$;
              let items = [];
               $('.table-x').find('tbody').find('tr').each(async (idx, element) => {
                if (idx === 0 || idx === 1) { return; }
                var $element = $(element);
                let collegeListInfo = {
                  name: $element.find('td').eq(1).text(),
                  location: $element.find('td').eq(4).text(),
                  provinceAbbr: province,
                  create_ts: moment().format('YYYY-MM-DD HH:mm:ss')
                }
                await this.collegeRepository.save(collegeListInfo);
                items.push(collegeListInfo);
              });
              reslove(items);
            }
            done();
          }
        });
        c.queue(`https://daxue.eol.cn/${province}.shtml`);
      })
    };
    return await a();
  }

我们可以在Controller里传参数:

 @Post('crawlerCollegeList')
  reptile(@Body('province') province: string) {
    return this.collegeService.crawlerCollegeaNameByProvince(province);
  }
爬取百度百科大学详情

一些网站为了防爬虫会设置一些检查机制,这时我们就需要添加请求头,伪装成浏览器正常访问。
header的内容在浏览器的开发者工具中便可看到,将这些信息添加到我们的爬虫代码中即可。
‘Accept-Encoding’:是浏览器发给服务器,声明浏览器支持的编码类型。一般有gzip,deflate,br 等等。

而百度百科的爬虫,我们需要设置accept-encodingbr,否则会一直报错解析不了。


  async crawlerCollegeInfo() {
    const crawlerFun = (name,id) => {
      return new Promise((reslove, reject) => {
        var c = new Crawler({
          rateLimit: 4000,
          maxConnections: 1,
          callback: (error, res, done) => {
            if (error) {
              reject(error)
            } else {
              const $ = res.$;
              let collegeBasicInfo: College = new College();
              $('.lemma-summary').find('.para').each((index, element) => {
                const $element = $(element);
                if(collegeBasicInfo.des){
                  collegeBasicInfo.des = $element.text() + '\n';
                }else{
                  collegeBasicInfo.des = collegeBasicInfo.des + $element.text() + '\n';
                }
              });
              collegeBasicInfo.website = $('.baseBox').find('.dl-baseinfo').last().find('dl').last().find('dd').find('a').text();
              let allParamsElement = $('.basic-info').find('dt');
              allParamsElement.each((index, element) => {
                const $element = $(element);
                const labelTitle = $element.text().replace(/\s+/g, "");
                for (const key in ParamCrawler) {
                  if (labelTitle === ParamCrawler[key]) {
                    if (key === 'createdYear') {
                      collegeBasicInfo.createdYear = $element.next().text().substr(0,5);
                    } 
                    else {
                      collegeBasicInfo[key] = $element.next().text().replace(/[\r\n]/g, "");
                    }
                  }
                }
              })
              reslove(collegeBasicInfo);
            }
            done();
          }
        });
        c.queue(
          {
            uri: `https://baike.baidu.com/item/${name}`,
            headers: {
              'accept-encoding': 'br'
            }
          }
        );
      })
    };

    let response=await this.collegeRepository.find();
    response.forEach(async (item)=>{
      let basicInfo= await crawlerFun(encodeURIComponent(item.name),item.id);
      await this.collegeRepository.update(item.id, basicInfo);
    })
  }

可以看到,我们先获取到大学列表,然后爬虫每个大学的百度百科全信息,这样的做法其实比较粗糙,但是这里因为我们只是学习爬虫的用法,所以先简单这样做。最后会返回一个处理完的数组。

我们加一个get列表的接口:

 controller:
 @Post('list')
  findAll(@Body() condition: Object): Promise<College[]> {
    return this.collegeService.findAll(condition);
  }
  
  service:
  findAll(condition: Object): Promise<College[]> {
    return this.collegeRepository.find({
      where: condition
    });
  }

这里我们只要在/college/list的post接口传入过滤对象就可以获得对应的返回结果,比如传入省份代码。

更新/删除/获取具体大学信息

有时候因为数据错误或不准确,我们需要手动更新数据。那么Nest如何进行Rest API的增删改查呢?

  controller:
  @Get('detail/:id')
  findOne(@Param('id') id: string): Promise<College> {
    return this.collegeService.findOne(id);
  }

  @Put(':id')
  update(
    @Param('id') id: string,
    @Body() updateCollege: College,
  ): Promise<void> {
    return this.collegeService.update(id, updateCollege);
  }

  @Delete(':id')
  remove(@Param('id') id: string): Promise<void> {
    return this.collegeService.remove(id);
  }
  
  service:
  async update(id, updateCollege: College): Promise<void> {
    delete updateCollege.id;
    updateCollege.update_ts = moment().format('YYYY-MM-DD HH:mm:ss');
    await this.collegeRepository.update(id, updateCollege);
  }

  async findOne(id: string): Promise<College> {
    await this.checkCollegeExist(id);
    return this.collegeRepository.findOne(id);
  }

  async remove(id: string): Promise<void> {
    await this.checkCollegeExist(id);
    await this.collegeRepository.delete(id);
  }

最后,我们看一下我们爬到的大学信息:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值