1. 생각의 흐름
- 설 대회로는
설
그 자체를 표현하라고 했다. - 사실 원래 계획은 Word2Vec를 통해서
9월 (혹은 10월) : 추석 = 1월 (혹은 2월) : 설
이라던가 송편과 떡국 간의 관계를 통해서 설이라는 단어를 결과값으로 뽑고 싶었다. - 체계단 내에서 누군가가 konlpy로 무언가를 하고 있는 것을 봤다. NLP라는 영역이 겹치는 것 같아서 뭔가 아이디어가 겹친다고 생각되어 처음부터 다시 생각하기로 했다.
과연 설은 무엇일까?
- 구글 번역기에 영어로 돌려봤다. New Year’s Day라고 나온다.
- New Yera’s Day라고 검색하니 별로 안나왔다.
- 중국어로 춘절이라고 해서 검색해보니 중국 코로나 바이러스 사태 기사만 나온다.
- 별 도움이 되지 않았다 ㅠ
깨달음
- 아! 아무도 설에 대해서 모르는구나. 모두 설을 뭐라고 생각할까 알아보기로 했다.
- 그렇다면 설은 어떻게 표현될까?
- 잘 모르겠다. 설 당일 사람들이 검색하는게 곧 설이지 않을까라는 생각에 도달했다.
2. 구체화
- 아이디어는 다음과 같다.
- 설 당일 네이버 실시간 검색을 1분 단위로 저장해논다.
- 이를 가공해서 사람들에게 보여주자! 그게 곧 설아니겠는가?
반전!
- 사실 설이 일요일인줄 알고 토요일날 코딩해도 널널하겠네 라고 생각했는데 설이 알고보니 토요일이였다.
- 이걸 깨달은건 토요일 아침 7시이다.
- 이미 늦었다. 젠장OTL.
- 생각해낸 아이디어는 시간이 제일 중요하다. 실시간 검색어를 바로 모으기로 했다.
- 그래서 아침 8시부터 저녁 10시까지 1분 간격으로 크롤링했다.
2-1. 크롤링 구현
var request = require ("request");
var fs = require("fs");
request.get({url : "https://m.search.naver.com/search.naver?sm=mtp_sug.top&where=m&query=%EC%8B%A4%EC%8B%9C%EA%B0%84%EA%B2%80%EC%83%89%EC%96%B4&acq=%EC%8B%A8&acr=0&qdt=0" }, function (err, response, body){
fs.writeFileSync(""+Date.now(), body);
});
- 사실 별거 없다. 1분만에 짠 코드답다. 뭔가 처리하지도 않고 그냥 데이터를 저장만 한다.
- 이걸 1분마다 실행해야 된다. node 로 해도 되는데, 먼가 에러 처리를 하나도 안해놔서 속된말로 쫄렸다.
- 그래서 뭔가 에러나도 별 상관 없이 프로세스가 안죽었으면 했다.
-
걍 프로세스가 죽어도 냅두기로 했다. 걍 1분마다 재실행 하는게 더 빠르겠더라.
crontab -e
를 열어서 1분마다 위 코드를 실행하기로 했다.
그렇게 서버에 14시간 정도 방치해놨다.
2-2. 전처리
- 역시 전체를 걍 저장하니 용량이 어마어마했다. 400MB나 됬다.
- 이제 전체 검색 결과가 아니라, 실시간 검색어만 빼보자(사실 이렇게 하면 5MB 이내일듯)
- 지금 시간 새벽 1시 5분 이제 전처리 시작한다 ㅠ
- 1시간 정도 짯다 ㅠ 오프라인 극혐… node_modules를 까서 사용법을 읽어본 다음 짜려고 하니 너무 오래걸린다.
const fs = require('fs');
const cheerio = require('cheerio');
fs.readdir('./raw_data', (err, files) => {
if (err)
throw err;
const data = files.map((filename) => {
const body = fs.readFileSync(`./raw_data/${filename}`);
const $ = cheerio.load(body);
const keywords = $('._keyword');
let retval = '';
for (let i = 0, length = keywords.length;
i < length;
i += 1) {
retval += keywords[i].children[0].data + ',';
}
return retval;
}).reduce((retval, now) => retval+='\n'+now);
fs.writeFileSync('data.csv', data);
});
데이터를 읽어서 오름차순의 형태로 1줄이 1분을 나타내도록 저장한다.
- 결과적으론 1MB 짜리 파일이 하나 나왔다.
2-3. 시각화
- 실제로 수치를 분석해서 우리가 상품을 제작할것도 아니고 대충 어떤 키워드들이 있는지 알아보기 위한거니 WordCloud를 시각화 도구로 사용하기로 했다.
- WordCloud는 D3를 이용해서 만들껀데 지금 오프라인이라 파일을 다운 받을 방법이 없으니 일단은 여기까지- 현재시각 2시 15분 - (어휴 이정도면 많이 했다.)
- 현재 시각 13시 5분, 방금 일어났다. 이제 시각화를 해보자
나중에야 정리 (2020-04-07)
- http-server module을 설치해서 간단한 web-server를 연다.
- 안그러면 csv파일 형식을 못연다.
- output.html ```html <!DOCTYPE html>
* wordcloud.js
```js
const width = 2080, height = 1920;
const svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.csv("worddata.csv").
then(function (data) {
showCloud(data)
setInterval(function() {
showCloud(data);
}, 10000);
});
wordScale = d3.scaleLinear().domain([0, 100]).range([0, 150]).clamp(true);
svg.append("g").attr("transform", "translate(" + width * 2 + "," + height +")");
function showCloud(data) {
d3.layout.cloud().size([width, height])
.words(data)
.rotate(function(d) {
return d.text.length > 3 ? 0 : 90;
})
.font("Impact")
.fontSize(function (d) {
return wordScale(d.frequency);
})
.on("end", draw)
.start();
function draw(words) {
var cloud = svg.selectAll("text").data(words)
cloud.enter()
.append("text")
.style("fill", "#4052")
.attr("text-anchor", "middle")
.style("font-size", function (d) {
return d.size + "px";
})
//.attr("font-size", 1)
.text(function (d) {
return d.text;
});
cloud
.transition()
.duration(600)
.style("font-size", function (d) {
return d.size + "px";
}).attr("transform", function (d) {
return `translate(${d.x+width/2}, ${d.y+height/2})rotate(${d.rotate})`
}).style("fill-opacity", 1);
}
}
결과 이미지
- 생각만큼 잘 나오지 않았다.
- 그리고 급하게 하느라고 그때 데이터가 아니라 다른 데이터로 그림을 만들었다.