search.js
6.72 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
const rp = require("request-promise");
const cheerio = require("cheerio");
const Entities = require('html-entities').XmlEntities;
const machineRead = require('./machineRead');
const entities = new Entities();
const searchURL = {
"naver" : "https://search.naver.com/search.naver?",
"google" : "https://www.google.com/search?"
}
/**
* @param {string} keywordText 검색할 키워드
* @param {cheerio} $ cheerio임.
* @param {string} elem 내용을 찾을 html의 selector인거같음
* @return {boolean} 찾았다면 true
* @description 주어진 html의 selector에서 keyword의 내용을 찾아 여부를 반환
*/
const keywordChecking = ( keywordText, $, elem ) => {
let tempCheck = false;
keywordText.split( ' ' ).forEach( ( Word ) => {
if( $( elem ).text().indexOf( Word ) !== -1 ) {
tempCheck = true;
}
});
if( tempCheck ) {
return true;
}
return false;
}
/**
* @param {Object} searchResult 검색된 내용이 담긴 빈 오브젝트
* @param {cheerio} $ cheerio임.
* @param {string} elem 내용을 찾을 html의 selector인거같음
* @param {string} defaultURL url이 없을 경우 달아주는 비상용 원래 링크
* @description 구글용 title passage 찾기함수
*/
const google = ( searchResult, $, elem , defaultURL ) => {
searchResult.passage = entities.decode( $( elem ).parent().parent().parent().text()).trim(),
searchResult.url = decodeURIComponent( $( elem ).attr( "href" ) );
searchResult.title = entities.decode( $( elem ).children("div").text() ); // title 캐오기 수정 가능
if( searchResult.url.indexOf( "/url?q=" ) === 0 ) {
searchResult.url = searchResult.url.replace( "/url?q=", "" );
} else if( searchResult.url.indexOf( "/search?" ) === 0 ) {
searchResult.url = "https://google.com" + searchResult.url;
} else {
searchResult.url = defaultURL;
}
}
/**
* @param {object} searchResult 검색된 내용이 담긴 빈 오브젝트
* @param {cheerio} $ cheerio임.
* @param {string} elem 내용을 찾을 html의 selector인거같음
* @param {string} defaultURL url이 없을 경우 달아주는 비상용 원래 링크
* @description 네이버용 title passage 찾기함수
*/
const naver = ( searchResult, $, elem , defaultURL ) => {
searchResult.title = $( elem ).parent().attr( "title" );
searchResult.passage = entities.decode( $( elem ).parent().parent().parent().text()).trim(),
searchResult.url = $( elem ).parent().attr( "href" );
if( searchResult.url === undefined ) {
searchResult.url = defaultURL;
}
}
/**
* @param {{title:string,passage:string,ulr:string}} searchResult 검색 결과가 담긴 object
* @param {[]} result 최종 결과가 담기는 어레이
* @param {boolean} keywordCheck 키워드가 확인됐는지 여부
* @description 타이틀이 없을 경우 달아주거나 중복된 것들 제거하는 등의 역활을 해 최종적으로 결과에 담아주는 함수
*/
const searchToResult = (searchResult, result, keywordCheck) => {
searchResult.passage = searchResult.passage.replace( /(http(s)?:\/\/)([a-z0-9\w]+\.*)+[a-z0-9]{2,4}/gi, ' ' ).replace( /\s{1,}|\s{1,}|\r\n|\r|\n/g, ' ' ).trim();
if( searchResult.title === undefined || !searchResult.title.length ) {
searchResult.title = searchResult.passage.split(' ').slice( 0, 3 ).toString().replace(/,/g,' ') + "..";
} else {
searchResult.title = searchResult.title.replace( /(http(s)?:\/\/)([a-z0-9\w]+\.*)+[a-z0-9]{2,4}/gi, ' ' ).replace( /\s{1,}|\s{1,}|\r\n|\r|\n/g, ' ' ).trim();
searchResult.passage = searchResult.passage.replace( searchResult.title, '' );
}
if( !result.length ) {
if( keywordCheck ) {
result.push( searchResult );
}
} else if( keywordCheck ) {
// 공백 제거하고 비교
if( result[ result.length - 1 ].passage.replace( /\s/g, '' ) !== searchResult.passage.replace( /\s/g, '' ) ) {
result.push( searchResult );
}
}
}
/**
* @param {string} main 검색할 사이트의 메인 내용이 들어있는 셀렉터를 줘야합니다.
* @param {string} keywordText 분석할 키워드의 내용
* @param {string} html html 파싱한 내용
* @param {string} defaultURL url이 없을 경우 달아주는 비상용 원래 링크
* @param {()=>{}} findSearchResult search result를 찾아주는 함수
* @returns {{url:string,title:string,passage:string}[]} object 여러 개를 가진 list를 준다. 각 json의 url, title, passage로 접근가능
* @description html을 크롤링한 데이터에서 url title passage를 캐오는 함수이다.
*/
const getHtmlMain = ( main, keywordText, html, defaultURL, findSearchResult ) => {
const $ = cheerio.load( html );
let result = [];
$( main ).each( (i, elem ) => {
let keywordCheck = keywordChecking( keywordText, $, elem );
if( keywordCheck ) {
let searchResult = {};
findSearchResult( searchResult, $, elem , defaultURL );
searchToResult( searchResult, result, keywordCheck );
}
});
return result;
}
const search = {};
/**
* @param {string} keywordText 검색할 내용
* @returns {{url:string,title:string,passage:string}[]} object 여러 개를 가진 list를 준다. 각 json의 url, title, passage로 접근가능
* @description 네이버에서 키워드의 내용을 크롤링해온다.
*/
search.naver = ( keywordText ) => {
return new Promise( async ( resolve, reject ) => {
let naverMain = "#main_pack strong",
result = [],
naverURL = searchURL.naver + "query=" + encodeURI( keywordText );
rp( {
"uri" : naverURL,
} )
.then( ( html ) => {
result = getHtmlMain( naverMain, keywordText, html, naverURL, naver );
resolve( result );
})
.catch( ( err ) => {
throw new Error( err );
});
})
}
/**
* @param {string} keywordText 검색할 내용
* @returns {{url:string,title:string,passage:string}[]} object 여러 개를 가진 list를 준다. 각 json의 url, title, passage로 접근가능
* @description 구글에서 키워드의 내용을 크롤링해온다.
*/
search.google = ( keywordText ) => {
return new Promise( ( resolve, reject ) => {
let googleMain = "#main a",
result = [],
googleURL = searchURL.google + "q=" + encodeURI( keywordText )
rp( {
"uri" : googleURL,
})
.then( ( html ) => {
result = getHtmlMain( googleMain, keywordText, html, googleURL, google );
resolve( result );
})
.catch( ( err ) => {
throw new Error( err );
});
})
}
module.exports = search;