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