Nest是用于构建高效,可扩展的Node.js
服务器端应用程序的框架。它使用渐进式JavaScript,内置并完全支持TypeScript
,并结合了OOP(面向对象编程),FP(函数式编程)和FRP(函数响应式编程)。
在底层,Nest默认使用Express
这类的健壮的服务端框架,并且你可以选择自己配置使用Fastify
。
Nest在这些常见的Node.js框架(Express / Fastify)之上提供了一个抽象级别,但也直接向开发者公开了它们的API。 这使开发人员可以自由使用底层平台的第三方库。
这里就不对Nest进行更多介绍了,你可以在Nest官方文档了解更多信息。毕竟本文的重点是展示如何去爬虫。
开始你的Nest应用
npm i -g @nestjs/cli
nest new project-name
连接MYSQL数据库
为了与SQL和NoSQL数据库集成,Nest提供了@nestjs/typeorm
包。 TypeORM
其官方文档称之为是可用于TypeScript的最成熟的对象关系映射器(ORM)。由于它是用TypeScript编写的,因此可以与Nest框架很好地集成。
使用TypeORM和在.Net或者JAVA里使用任何对象关系映射器并没有什么区别,它只是给我们方便的提供诸如find
,save
,create
,remove
,update
,delete
等API,如果你不喜欢,你当然也可以直接使用query
传入SQL语句来操作数据库。
npm install --save @nestjs/typeorm typeorm mysql
然后在app.module.ts里:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';
import { CollegeModule } from './college/college.module';
import { BachelorPointsModule } from './bachelor-points/backelor.module';
import { MasterPointsModule } from './master-points/master.module';
import { ProvinceModule } from './province-data/province-data.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type:'mysql',
host:'localhost',
port:3306,
username:'root',
password:'root123456',
database:'test',
autoLoadEntities: true,
synchronize:true
}),
UsersModule,CollegeModule,BachelorPointsModule,MasterPointsModule,ProvinceModule
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
关于这个如果你遇到什么问题,可以参考我的文章Nest.js初探索之实体映射数据库的三种方式。
爬取所有省份
为了获取中国所有大学信息,我们希望可以根据省份查询。所以,我们先爬取所有省份。
先创建province实体类
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
@Entity()
export class Province {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column(
{
nullable: true
}
)
abbr: string;
@CreateDateColumn({ comment: '创建时间',type:'datetime' }) // 自动生成列
created_ts: string
@UpdateDateColumn({ comment: '更新时间',type:'datetime'}) // 自动生成并自动更新列
updated_ts: string
}
然后在controller里面设置Rest API相关信息
import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common';
import { Province } from './province.entity';
import { ProvinceService } from './province.service';
@Controller('province')
export class ProvinceController {
constructor(private readonly provinceService: ProvinceService) {
}
@Post('crawler')
crawlerProvince() {
return this.provinceService.crawlerProvince();
}
@Get()
list(): Promise<Province[]> {
return this.provinceService.listProvince();
}
}
在Service里进行爬虫和数据库操作
主要的爬虫逻辑在service里,我们重点看下crawlerProvince方法,使用了Crawler库来解析和爬虫。
import { Injectable, HttpException, HttpStatus } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as Crawler from 'crawler';
import { Province } from './province.entity';
@Injectable()
export class ProvinceService {
constructor(
@InjectRepository(Province)
private provinceRepository: Repository<Province>,
) {
}
listProvince(): Promise<Province[]> {
return this.provinceRepository.find();
}
async crawlerProvince() {
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 = [];
$('.row').find('.col-md-12').find('.row').find('.col-md-12').find('a').each(async (idx, element) => {
if (idx >= 34) { return; }
var $element = $(element);
let province = {
name: $element.text(),
}
await this.provinceRepository.save(province);
items.push(province);
});
reslove(items);
}
done();
}
});
c.queue(`http://www.52maps.com/china_city.php`);
})
};
return await a();
}
}
这里我们使用Crawler
爬取页面信息之后,将每个省份使用this.provinceRepository.save(province)
都存储进数据库。
module引入
最后我们需要ProvinceModule
将controller和service以及实体Province都在module文件里声明,最后在app.module不要忘记导入ProvinceModule。
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Province } from './province.entity';
import { ProvinceController } from './province-data.controller';
import { ProvinceService } from './province.service';
@Module({
imports: [
TypeOrmModule.forFeature([Province]),
],
controllers:[ProvinceController],
providers:[ProvinceService]
})
export class ProvinceModule {}
这时数据库里就有省份信息了:
使用get请求查看我们爬取的结果:
结果
在前端框架我们就可以让用户选择不同的省份来获取相应的大学信息。
下一篇文章,我将介绍如何根据不同的省份爬取大学列表,并爬取百度百科的大学详细信息。