Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/#101: 웨일비 데이터 크롤링 추가 #102

Merged
merged 7 commits into from
Sep 18, 2023
11 changes: 10 additions & 1 deletion src/apis/notice/controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getNotices, getSchoolNotices } from '@apis/notice/service';
import { getNotices, getSchoolNotices, getWhalebe } from '@apis/notice/service';
import express, { Request, Response } from 'express';

const router = express.Router();
Expand All @@ -17,4 +17,13 @@ router.get('/', async (req: Request, res: Response) => {
}
});

router.get('/whalebe', async (req: Request, res: Response) => {
try {
const whalebeData = await getWhalebe();
res.json(whalebeData);
} catch (err) {
res.json();
}
});

export default router;
12 changes: 12 additions & 0 deletions src/apis/notice/service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { WhalebeData } from '@crawling/whalebeCrawling';
import db from '@db/index';
import { Notice } from 'src/@types/college';
import notificationToSlack from 'src/hooks/notificateToSlack';

interface SeparateNoti {
고정: Notice[];
Expand Down Expand Up @@ -42,3 +44,13 @@ export const getSchoolNotices = async (): Promise<SeparateNoti> => {
};
return notices;
};

export const getWhalebe = async (): Promise<WhalebeData[]> => {
const query = 'SELECT * FROM 웨일비;';
return new Promise<WhalebeData[]>((resolve) => {
db.query(query, (err, res) => {
if (err) notificationToSlack('웨일비 조회 실패');
resolve(res as WhalebeData[]);
});
});
};
39 changes: 39 additions & 0 deletions src/crawling/whalebeCrawling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import axios from 'axios';
import * as cheerio from 'cheerio';

export interface WhalebeData {
title: string;
date: string;
imgUrl: string;
}

export const whalebeCrawling = async (): Promise<WhalebeData[]> => {
const hostname = 'https://whalebe.pknu.ac.kr';
const whalebeLink = hostname + '/main';
const whalebeData: WhalebeData[] = [];

const response = await axios.get(whalebeLink);
const $ = cheerio.load(response.data);

const programs = $('ul.px-0').find('li');
if (programs.length < 1) return;

programs.each((_, element) => {
const imgUrl = hostname + $(element).find('img').attr('src');
const title = $(element).find('.card-title').text();
const date = $(element)
.find('.app_date')
.find('.col-12')
.first()
.text()
.split('~')[1]
.trim();
const tmpData = {
title,
date,
imgUrl,
};
whalebeData.push(tmpData);
});
return whalebeData;
};
18 changes: 18 additions & 0 deletions src/db/data/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
noticeCrawling,
noticeListCrawling,
} from '@crawling/noticeCrawling';
import { whalebeCrawling } from '@crawling/whalebeCrawling';
import { RowDataPacket } from 'mysql2';
import { College, Notice } from 'src/@types/college';
import db from 'src/db';
Expand Down Expand Up @@ -205,3 +206,20 @@ export const saveSchoolNoticeToDB = async (): Promise<void> => {

await Promise.all(savePromises);
};

export const saveWhalebeToDB = async (): Promise<void> => {
const query = 'INSERT INTO 웨일비 (title, date, imgUrl) VALUES (?, ?, ?)';
const whalebeDatas = await whalebeCrawling();

const promises = whalebeDatas.map((data) => {
const values = [data.title, data.date, data.imgUrl];

return new Promise<void>((resolve) => {
db.query(query, values, () => {
resolve();
});
});
});

Promise.all(promises);
};
18 changes: 18 additions & 0 deletions src/db/table/createTables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,26 @@ const createSchoolNoticeTable = () => {
}
};

const createWhalebeDataTable = () => {
const createTableQuery = `CREATE TABLE 웨일비 (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL UNIQUE,
date VARCHAR(255) NOT NULL,
imgUrl VARCHAR(255) NOT NULL
);`;

db.query(createTableQuery, (error) => {
if (error) {
console.log('웨일비 DB 생성 실패', error);
return;
}
console.log('웨일비 테이블 생성 성공!');
});
};

const createAllTables = (college: College[]) => {
createDepartmentTable();
createWhalebeDataTable();
createGraduationTable();
createSchoolNoticeTable();
createNoticeTable(college);
Expand Down
7 changes: 6 additions & 1 deletion src/hooks/cronNoticeCrawling.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { pushNotification } from '@apis/subscribe/service';
import { saveNoticeToDB, saveSchoolNoticeToDB } from '@db/data/handler';
import {
saveNoticeToDB,
saveSchoolNoticeToDB,
saveWhalebeToDB,
} from '@db/data/handler';
import cron from 'node-cron';
import notificationToSlack from 'src/hooks/notificateToSlack';

Expand All @@ -16,6 +20,7 @@ cron.schedule('0 3 * * *', async () => {
const majors = await saveNoticeToDB();
await saveNoticeToDB();
await saveSchoolNoticeToDB();
await saveWhalebeToDB();
const today = new Date();
const year = today.getFullYear();
const month = today.getMonth() + 1; // 월은 0부터 시작하므로 1을 더해줍니다.
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/startCrawlingData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
saveDepartmentToDB,
saveNoticeToDB,
saveSchoolNoticeToDB,
saveWhalebeToDB,
} from '@db/data/handler';
import db from '@db/index';
import createNoticeTable from '@db/table/createTables';
Expand All @@ -22,6 +23,7 @@ export const initialCrawling = () => {
await saveDepartmentToDB(collegeList);
await saveGraduationRequirementToDB();
await saveSchoolNoticeToDB();
await saveWhalebeToDB();
await saveNoticeToDB();
}
});
Expand Down
26 changes: 26 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import noticeRouter from '@apis/notice/controller';
import subscriptionRouter from '@apis/subscribe/controller';
import suggestionRouter from '@apis/suggestion/controller';
import env from '@config';
import { saveWhalebeToDB } from '@db/data/handler';
import db from '@db/index';
import { corsOptions } from '@middlewares/cors';
import errorHandler from '@middlewares/error-handler';
import cors from 'cors';
Expand Down Expand Up @@ -39,3 +41,27 @@ app.listen(env.SERVER_PORT, () => {
});

webpush();

const forDeployedServer = () => {
// 이 함수는 현재 배포되어있는 서버를 위해 사용되는 로직이며 최초 서버에 배포되는 1회만 실행되도록 하기위한 함수에요
// 그렇기에 아래에 작성된 코드들은 배포서버에 배포되면 다음 배포전 수정해주세요!!

// 웨일비 관련 테이블 생성 후 데이터 삽입
const createTableQuery = `CREATE TABLE 웨일비 (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL UNIQUE,
date VARCHAR(255) NOT NULL,
imgUrl VARCHAR(255) NOT NULL
);`;

db.query(createTableQuery, (error) => {
if (error) {
console.log('웨일비 DB 생성 실패', error);
return;
}
console.log('웨일비 테이블 생성 성공!');
saveWhalebeToDB();
});
};

forDeployedServer();