๐ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ ์ ์น์ฌ์ดํธ๋ฅผ 4๋ถ๋ง์ ํฌ๋กค๋งํ๋ ๋ฐฉ๋ฒ ๐ฅ
17223 ๋จ์ด npmjavascriptopensourcenode
์ค๋์ ์ฃผ์ ๋ ์ ์ ์น ์ฌ์ดํธ์์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ ๋ค์ ์ด ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ปดํจํฐ์ ํ์ผ ๋๋ ์์ ํ ๋ค๋ฅธ ๊ฒ์ผ๋ก ๊ตฌ์กฐํํ๋ ๊ฒ์ ๋๋ค.
Fetch ํฌ๋กค๋ฌ(Node JS) ์๊ฐ
Fetch Crawler๋ ์น ์ฌ์ดํธ ํฌ๋กค๋ง์ ์ํ ๊ธฐ๋ณธ์ ์ด๊ณ ์ ์ฐํ๋ฉฐ ๊ฐ๋ ฅํ API๋ฅผ ์ ๊ณตํ๋๋ก ์ค๊ณ๋์์ต๋๋ค.
ํฌ๋กค๋ฌ๋ ๋ค์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ์ ์ ์น ์ฌ์ดํธ๋ฅผ ํฌ๋กค๋งํ๋ ๊ฐ๋จํ API๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ ์ฒด ๋ฌธ์๋ Github์์ ์ฌ์ฉํ ์ ์์ต๋๋ค. https://github.com/viclafouch/Fetch-Crawler
Fetch-crawler์ ํน์ง์ ์๋นํ ์๊ฐ ์ ์ฝ์ ํ์ฉํ๋ ์์ฒญ์ ๋ณ๋ ฌ๋ก ๊ด๋ฆฌํ๋ค๋ ๊ฒ์ ๋๋ค(์: ๋์์ 10๊ฐ์ ์์ฒญ, ํ๋์ฉ์ด ์๋).
์ฆ, ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ชจ๋ ์์ ์ ์ํํ๋ฏ๋ก ๋ค์ํ ์ต์ ์ ๊ตฌ์ฑํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
๋จ๊ณ๋ณ:
๋จผ์ ํ์ํ ์ข
์์ฑ์ ์ค์นํฉ๋๋ค.
# npm i @viclafouch/fetch-crawler
๊ทธ๋ฐ ๋ค์ js ํ์ผ์์ ๋ชจ๋์ ๊ฐ์ ธ์ค๊ณ launch
์ FetchCrawler
๋ฐฉ๋ฒ์ ์ฌ์ฉํ์ญ์์ค. ํ์ํ ์ ์ผํ ๋งค๊ฐ๋ณ์๋ ๊ทํ์ ์น์ฌ์ดํธ(๋๋ ํ์ด์ง) ๋งํฌ(์ฌ๊ธฐhttps://github.com
)์
๋๋ค.
const FetchCrawler = require('@viclafouch/fetch-crawler')
FetchCrawler.launch({
url: 'https://github.com'
})
๊ทธ๋ฐ ๋ค์ ๋ค์์ ์คํํฉ๋๋ค.
# node example-crawl.js
Node JS๋ก ์ด ํ์ผ์ ์คํํ๋ฉด ์๋ํ์ง๋ง ํฌ๋กค๋ฌ๊ฐ ์๋ฃ๋ ๋๊น์ง ์๋ฌด ์ผ๋ ์ผ์ด๋์ง ์์ต๋๋ค.
์ด์ ์น ์ฌ์ดํธ( documentation )์์ ๋ฐ์ดํฐ๋ฅผ ์ถ์ถํ๋ ๋ฐ ์ฌ์ฉํ ๊ธฐ๋ณธ ์ต์
๋ฐ ๋ฐฉ๋ฒ์ผ๋ก ์ด๋ํ๊ฒ ์ต๋๋ค.
const FetchCrawler = require('@viclafouch/fetch-crawler')
// `$ = Cheerio to get the content of the page
// See https://cheerio.js.org
const collectContent = $ =>
$('body')
.find('h1')
.text()
.trim()
// After getting content of the page, do what you want :)
// Accept async function
const doSomethingWith = (content, url) => console.log(`Here the title '${content}' from ${url}`)
// Here I start my crawler
// You can await for it if you want
FetchCrawler.launch({
url: 'https://github.com',
evaluatePage: $ => collectContent($),
onSuccess: ({ result, url }) => doSomethingWith(result, url),
onError: ({ error, url }) => console.log('Whouaa something wrong happened :('),
maxRequest: 20
})
์, ์์ ํฌํจ๋ ์๋ก์ด ๋ฐฉ๋ฒ๊ณผ ์ต์
์ ๊ฒํ ํด ๋ด
์๋ค.
evaluatePage
: ํ์ด์ง์ ์ฝํ
์ธ ๋ฅผ ํ์/์กฐ์ํ๋ ๊ธฐ๋ฅ์
๋๋ค. Cheerio๋ ๋งํฌ์
์ ๊ตฌ๋ฌธ ๋ถ์ํ๊ธฐ ์ํด ์ ๊ณต๋๋ฉฐ ์ด๋ฅผ ์ํ ๊ฐ๋ ฅํ API๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด ์น ํ์ด์ง์์ ์ํ๋ ์ ํํ ๋ฐ์ดํฐ ์กฐ๊ฐ์ ์ถ์ถํ๋ ํน์ ๊ธฐ๋ฅ์ ๊ตฌ์ถํ ์ ์์ต๋๋ค.
onSuccess
: evaluatePage
๊ฐ ์ฑ๊ณตํ๋ฉด ์ด๋ป๊ฒ ํ์๊ฒ ์ต๋๊น? ์ํ๋ ๋๋ก ํ์ญ์์ค(๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ถ๊ฐ? ํ์ผ์ ๋ฐ์ดํฐ ํฌํจ? ๋ฑ..).
onError
: if evaluatePage
๋ผ๋ ์ฝ๋ฐฑ์ด ์คํจํฉ๋๋ค.
maxRequest
: ํฌ๋กค๋ฌ๊ฐ ์คํํ ์ ์๋ ์ต๋ ์์ฒญ ์๋ฅผ ๋ํ๋
๋๋ค. ์ ํ์ ๋นํ์ฑํํ๋ ค๋ฉด ์ ๋ฌ-1
ํฉ๋๋ค. ๊ทธ๋ฌ๋ ์์ ์์์๋ 20๋ฒ์ ์์ฒญ ํ์ ํฌ๋กค๋ฌ๋ฅผ ์ค์งํ๋ ค๊ณ ํฉ๋๋ค(์คํจํ๋๋ผ๋).
๋๋จธ์ง ๊ตฌ์ฑ์ ๊ฒฝ์ฐ ์ฌ๊ธฐ์์ documentation์ ์ฐพ์ ์ ์์ต๋๋ค.
์ค์ต ์:
๋น๋์ค ๊ฒ์ ์น์ฌ์ดํธ์ ์๋ฅผ ๋ค์ด ๋ณด๊ฒ ์ต๋๋ค. Instant Gaming
์ฐ๋ฆฌ์ ๋ชฉํ: ์น ์ฌ์ดํธ์์ ํ๋งค ์ค์ธ ๋น๋์ค ๊ฒ์(Xbox)์์ ๋ฐ์ดํฐ๋ฅผ ๋ณต๊ตฌํ๊ณ JSON ํ์ผ๋ก ์ปดํ์ผํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ํ๋ก์ ํธ์์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค(์: ์ด ๋ชฉ๋ก์ ์ค์๊ฐ์ผ๋ก ํ์ํ ์ ์๋ Chrome ํ์ฅ ํ๋ก๊ทธ๋จ).
์ด๊ฒ์ด ์ฐ๋ฆฌ ํ์ผexample-crawl.js
์ ํฌํจ๋ ๊ฒ์
๋๋ค.
const fs = require('fs')
const FetchCrawler = require('@viclafouch/fetch-crawler')
// Get all games on xbox platform
const urlToCrawl = 'https://www.instant-gaming.com/en/search/?type%5B0%5D=xbox'
let games = []
// I'm getting an array of each game on the page (name, price, cover, discount)
const collectContent = $ => {
const content = []
$('.item.mainshadow').each(function(i, elem) {
content.push({
name: $(this)
.find($('.name'))
.text()
.trim(),
price: $(this)
.find($('.price'))
.text()
.trim(),
discount: $(this)
.find($('.discount'))
.text()
.trim(),
cover: $(this)
.find($('.picture'))
.attr('src')
})
})
return content
}
// Only url including an exact string
const checkUrl = url => {
try {
const link = new URL(url)
if (link.searchParams.get('type[0]') === 'xbox' && link.searchParams.get('page')) {
return url
}
return false
} catch (error) {
return false
}
}
// Concat my new games to my array
const doSomethingWith = content => (games = games.concat(content))
// Await for the crawler, and then save result in a JSON file
;(async () => {
try {
await FetchCrawler.launch({
url: urlToCrawl,
evaluatePage: $ => collectContent($),
onSuccess: ({ result, url }) => doSomethingWith(result, url),
preRequest: url => checkUrl(url),
maxDepth: 4,
parallel: 6
})
const jsonResult = JSON.stringify({ ...games }, null, 2)
await fs.promises.writeFile('examples/example_4.json', jsonResult)
} catch (error) {
console.error(error)
}
})()
์ด์ ํฌ๋กค๋ฌ๋ฅผ ์์ํ๊ณ ๋ช ์ด๋ง ๊ธฐ๋ค๋ฆฌ๋ฉด ๋ฉ๋๋ค.
# node example-crawl.js
๋ค์์ JSON ํ์ผ์
๋๋ค. https://github.com/viclafouch/Fetch-Crawler/blob/master/examples/example_4.json
๋ณด์๋ค์ํผ json ํ์ผ์์ ๋งค์ฐ ๊นจ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ป์ต๋๋ค. ๋ถ๋ช
ํ ์น ์ฌ์ดํธ์ ๋ฐ์ดํฐ๋ ๊ณง ๋ณ๊ฒฝ๋ ๊ฒ์ด๋ฏ๋ก 24์๊ฐ๋ง๋ค ํฌ๋กค๋ฌ๋ฅผ ๋ฐ๋ณตํ ์ ์์ต๋๋ค.
Fetch Crawler ํจํค์ง์ ๋ํด ์์ธํ ์์๋ณด๋ ค๋ฉด documentation ์ ํ์ธํ์ญ์์ค.
...
์ฝ์ด ์ฃผ์
์ ๊ฐ์ฌํฉ๋๋ค.
์ด ํจํค์ง์ ์ ์ ํจ๊ป ๊ธฐ์ฌํด ์ฃผ์ธ์ :)
Google ํ๋ก์ ํธ์ ํ์ํ๊ณ ๋ฐ์ดํฐ ์ถ์ถ์ด ๊ฝค ์ด๋ ค์ ๊ธฐ ๋๋ฌธ์ ์ด ํจํค์ง๋ฅผ ๋ง๋ค์์ต๋๋ค.
Reference
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(๐ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ ์ ์น์ฌ์ดํธ๋ฅผ 4๋ถ๋ง์ ํฌ๋กค๋งํ๋ ๋ฐฉ๋ฒ ๐ฅ), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค
https://dev.to/viclafouch/how-to-crawl-a-static-website-in-javascript-in-4min-36g3
ํ
์คํธ๋ฅผ ์์ ๋กญ๊ฒ ๊ณต์ ํ๊ฑฐ๋ ๋ณต์ฌํ ์ ์์ต๋๋ค.ํ์ง๋ง ์ด ๋ฌธ์์ URL์ ์ฐธ์กฐ URL๋ก ๋จ๊ฒจ ๋์ญ์์ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ
์ธ ๋ฐ๊ฒฌ์ ์ ๋
(Collection and Share based on the CC Protocol.)
# npm i @viclafouch/fetch-crawler
const FetchCrawler = require('@viclafouch/fetch-crawler')
FetchCrawler.launch({
url: 'https://github.com'
})
# node example-crawl.js
const FetchCrawler = require('@viclafouch/fetch-crawler')
// `$ = Cheerio to get the content of the page
// See https://cheerio.js.org
const collectContent = $ =>
$('body')
.find('h1')
.text()
.trim()
// After getting content of the page, do what you want :)
// Accept async function
const doSomethingWith = (content, url) => console.log(`Here the title '${content}' from ${url}`)
// Here I start my crawler
// You can await for it if you want
FetchCrawler.launch({
url: 'https://github.com',
evaluatePage: $ => collectContent($),
onSuccess: ({ result, url }) => doSomethingWith(result, url),
onError: ({ error, url }) => console.log('Whouaa something wrong happened :('),
maxRequest: 20
})
๋น๋์ค ๊ฒ์ ์น์ฌ์ดํธ์ ์๋ฅผ ๋ค์ด ๋ณด๊ฒ ์ต๋๋ค. Instant Gaming
์ฐ๋ฆฌ์ ๋ชฉํ: ์น ์ฌ์ดํธ์์ ํ๋งค ์ค์ธ ๋น๋์ค ๊ฒ์(Xbox)์์ ๋ฐ์ดํฐ๋ฅผ ๋ณต๊ตฌํ๊ณ JSON ํ์ผ๋ก ์ปดํ์ผํฉ๋๋ค. ๊ทธ๋ฐ ๋ค์ ํ๋ก์ ํธ์์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค(์: ์ด ๋ชฉ๋ก์ ์ค์๊ฐ์ผ๋ก ํ์ํ ์ ์๋ Chrome ํ์ฅ ํ๋ก๊ทธ๋จ).
์ด๊ฒ์ด ์ฐ๋ฆฌ ํ์ผ
example-crawl.js
์ ํฌํจ๋ ๊ฒ์
๋๋ค.const fs = require('fs')
const FetchCrawler = require('@viclafouch/fetch-crawler')
// Get all games on xbox platform
const urlToCrawl = 'https://www.instant-gaming.com/en/search/?type%5B0%5D=xbox'
let games = []
// I'm getting an array of each game on the page (name, price, cover, discount)
const collectContent = $ => {
const content = []
$('.item.mainshadow').each(function(i, elem) {
content.push({
name: $(this)
.find($('.name'))
.text()
.trim(),
price: $(this)
.find($('.price'))
.text()
.trim(),
discount: $(this)
.find($('.discount'))
.text()
.trim(),
cover: $(this)
.find($('.picture'))
.attr('src')
})
})
return content
}
// Only url including an exact string
const checkUrl = url => {
try {
const link = new URL(url)
if (link.searchParams.get('type[0]') === 'xbox' && link.searchParams.get('page')) {
return url
}
return false
} catch (error) {
return false
}
}
// Concat my new games to my array
const doSomethingWith = content => (games = games.concat(content))
// Await for the crawler, and then save result in a JSON file
;(async () => {
try {
await FetchCrawler.launch({
url: urlToCrawl,
evaluatePage: $ => collectContent($),
onSuccess: ({ result, url }) => doSomethingWith(result, url),
preRequest: url => checkUrl(url),
maxDepth: 4,
parallel: 6
})
const jsonResult = JSON.stringify({ ...games }, null, 2)
await fs.promises.writeFile('examples/example_4.json', jsonResult)
} catch (error) {
console.error(error)
}
})()
์ด์ ํฌ๋กค๋ฌ๋ฅผ ์์ํ๊ณ ๋ช ์ด๋ง ๊ธฐ๋ค๋ฆฌ๋ฉด ๋ฉ๋๋ค.
# node example-crawl.js
๋ค์์ JSON ํ์ผ์ ๋๋ค. https://github.com/viclafouch/Fetch-Crawler/blob/master/examples/example_4.json
๋ณด์๋ค์ํผ json ํ์ผ์์ ๋งค์ฐ ๊นจ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ป์ต๋๋ค. ๋ถ๋ช ํ ์น ์ฌ์ดํธ์ ๋ฐ์ดํฐ๋ ๊ณง ๋ณ๊ฒฝ๋ ๊ฒ์ด๋ฏ๋ก 24์๊ฐ๋ง๋ค ํฌ๋กค๋ฌ๋ฅผ ๋ฐ๋ณตํ ์ ์์ต๋๋ค.
Fetch Crawler ํจํค์ง์ ๋ํด ์์ธํ ์์๋ณด๋ ค๋ฉด documentation ์ ํ์ธํ์ญ์์ค.
...
์ฝ์ด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค.
์ด ํจํค์ง์ ์ ์ ํจ๊ป ๊ธฐ์ฌํด ์ฃผ์ธ์ :)
Google ํ๋ก์ ํธ์ ํ์ํ๊ณ ๋ฐ์ดํฐ ์ถ์ถ์ด ๊ฝค ์ด๋ ค์ ๊ธฐ ๋๋ฌธ์ ์ด ํจํค์ง๋ฅผ ๋ง๋ค์์ต๋๋ค.
Reference
์ด ๋ฌธ์ ์ ๊ดํ์ฌ(๐ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ ์ ์น์ฌ์ดํธ๋ฅผ 4๋ถ๋ง์ ํฌ๋กค๋งํ๋ ๋ฐฉ๋ฒ ๐ฅ), ์ฐ๋ฆฌ๋ ์ด๊ณณ์์ ๋ ๋ง์ ์๋ฃ๋ฅผ ๋ฐ๊ฒฌํ๊ณ ๋งํฌ๋ฅผ ํด๋ฆญํ์ฌ ๋ณด์๋ค https://dev.to/viclafouch/how-to-crawl-a-static-website-in-javascript-in-4min-36g3ํ ์คํธ๋ฅผ ์์ ๋กญ๊ฒ ๊ณต์ ํ๊ฑฐ๋ ๋ณต์ฌํ ์ ์์ต๋๋ค.ํ์ง๋ง ์ด ๋ฌธ์์ URL์ ์ฐธ์กฐ URL๋ก ๋จ๊ฒจ ๋์ญ์์ค.
์ฐ์ํ ๊ฐ๋ฐ์ ์ฝํ ์ธ ๋ฐ๊ฒฌ์ ์ ๋ (Collection and Share based on the CC Protocol.)
์ข์ ์นํ์ด์ง ์ฆ๊ฒจ์ฐพ๊ธฐ
๊ฐ๋ฐ์ ์ฐ์ ์ฌ์ดํธ ์์ง
๊ฐ๋ฐ์๊ฐ ์์์ผ ํ ํ์ ์ฌ์ดํธ 100์ ์ถ์ฒ ์ฐ๋ฆฌ๋ ๋น์ ์ ์ํด 100๊ฐ์ ์์ฃผ ์ฌ์ฉํ๋ ๊ฐ๋ฐ์ ํ์ต ์ฌ์ดํธ๋ฅผ ์ ๋ฆฌํ์ต๋๋ค