์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿ•ธ๏ธ Go์–ธ์–ด ํฌ๋กค๋ง๊ณผ Colly๋ฅผ ํ™œ์šฉํ•œ ์›น ์Šคํฌ๋ž˜ํ•‘ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

๐Ÿ•ธ๏ธ Go์–ธ์–ด ํฌ๋กค๋ง๊ณผ Colly๋ฅผ ํ™œ์šฉํ•œ ์›น ์Šคํฌ๋ž˜ํ•‘ ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์™„๋ฒฝ ๊ฐ€์ด๋“œ

์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๋Š” Go ์›น ํฌ๋กค๋ง์˜ ๋ชจ๋“  ๊ฒƒ

์•ˆ๋…•! ๐Ÿ‘‹ ์˜ค๋Š˜์€ Go์–ธ์–ด๋กœ ์›น ํฌ๋กค๋ง์„ ์–ด๋–ป๊ฒŒ ํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  Colly๋ผ๋Š” ์—„์ฒญ ๊ฐ•๋ ฅํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ํ•จ๊ป˜ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ. ํ˜น์‹œ "์›น์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ž๋™์œผ๋กœ ์ˆ˜์ง‘ํ•˜๊ณ  ์‹ถ์€๋ฐ ์–ด๋–ป๊ฒŒ ์‹œ์ž‘ํ•ด์•ผ ํ• ๊นŒ?" ๊ณ ๋ฏผํ•œ ์  ์žˆ์–ด? ๊ทธ๋ ‡๋‹ค๋ฉด ์ œ๋Œ€๋กœ ์ฐพ์•„์™”์–ด! ๐Ÿ˜Š

์š”์ฆ˜ ๋ฐ์ดํ„ฐ๋Š” ์ •๋ง ์ค‘์š”ํ•˜์ž–์•„? ๊ฐ€๊ฒฉ ๋น„๊ต, ๋‰ด์Šค ๋ชจ๋‹ˆํ„ฐ๋ง, ์‹œ์žฅ ์กฐ์‚ฌ ๋“ฑ ๋‹ค์–‘ํ•œ ๋ถ„์•ผ์—์„œ ์›น ์Šคํฌ๋ž˜ํ•‘์ด ํ™œ์šฉ๋˜๊ณ  ์žˆ์–ด. ๊ทธ๋ฆฌ๊ณ  Go์–ธ์–ด๋Š” ์ด๋Ÿฐ ์ž‘์—…์— ์ •๋ง ์ตœ์ ํ™”๋œ ์–ธ์–ด์•ผ. ๋น ๋ฅด๊ณ , ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ๊ฐ€ ๋›ฐ์–ด๋‚˜๊ณ , ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ๋„ ์ข‹๊ฑฐ๋“ ! ๐Ÿš€

์ด ๊ธ€์—์„œ๋Š” ๊ธฐ์ดˆ๋ถ€ํ„ฐ ์‹ค์ „ ํ™œ์šฉ๊นŒ์ง€, ๋งˆ์น˜ ์˜†์—์„œ ์นœ๊ตฌ๊ฐ€ ์„ค๋ช…ํ•ด์ฃผ๋“ฏ์ด ์‰ฝ๊ณ  ์žฌ๋ฏธ์žˆ๊ฒŒ ์•Œ๋ ค์ค„๊ฒŒ. ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋ฐฐ์šฐ๊ณ  ์‹ถ๊ฑฐ๋‚˜ ์ƒˆ๋กœ์šด ๊ธฐ์ˆ ์„ ์ตํžˆ๊ณ  ์‹ถ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ๋„์›€์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ!

Go Colly Framework Web Scraping Data Collection HTML Parsing Concurrency Data Storage Error Handling

๐ŸŽฏ ์›น ํฌ๋กค๋ง์ด๋ž€ ๋ฌด์—‡์ผ๊นŒ?

๋จผ์ € ๊ธฐ๋ณธ๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž! ์›น ํฌ๋กค๋ง(Web Crawling)์€ ์ธํ„ฐ๋„ท์— ์žˆ๋Š” ์›น ํŽ˜์ด์ง€๋“ค์„ ์ž๋™์œผ๋กœ ํƒ์ƒ‰ํ•˜๊ณ  ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ๊ธฐ์ˆ ์ด์•ผ. ๋งˆ์น˜ ๊ฑฐ๋ฏธ(Spider)๊ฐ€ ๊ฑฐ๋ฏธ์ค„์„ ํƒ€๊ณ  ๋‹ค๋‹ˆ๋“ฏ์ด ์›น ํŽ˜์ด์ง€๋“ค์„ ๋Œ์•„๋‹ค๋‹Œ๋‹ค๊ณ  ํ•ด์„œ 'ํฌ๋กค๋ง'์ด๋ผ๋Š” ์ด๋ฆ„์ด ๋ถ™์—ˆ์–ด. ๐Ÿ•ท๏ธ

๐Ÿ“Œ ์›น ํฌ๋กค๋ง vs ์›น ์Šคํฌ๋ž˜ํ•‘

๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ด ๋‘ ์šฉ์–ด๋ฅผ ํ˜ผ์šฉํ•˜๋Š”๋ฐ, ์‚ฌ์‹ค ์•ฝ๊ฐ„์˜ ์ฐจ์ด๊ฐ€ ์žˆ์–ด:

โ€ข ์›น ํฌ๋กค๋ง: ์›น ํŽ˜์ด์ง€๋ฅผ ์ž๋™์œผ๋กœ ํƒ์ƒ‰ํ•˜๊ณ  ์ธ๋ฑ์‹ฑํ•˜๋Š” ๊ณผ์ • (๊ฒ€์ƒ‰์—”์ง„์ด ํ•˜๋Š” ์ผ)

โ€ข ์›น ์Šคํฌ๋ž˜ํ•‘: ํŠน์ • ์›น ํŽ˜์ด์ง€์—์„œ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•˜๋Š” ๊ณผ์ •

ํ•˜์ง€๋งŒ ์‹ค๋ฌด์—์„œ๋Š” ๋‘ ์šฉ์–ด๋ฅผ ๊ฑฐ์˜ ๊ฐ™์€ ์˜๋ฏธ๋กœ ์‚ฌ์šฉํ•˜๋‹ˆ๊นŒ ๋„ˆ๋ฌด ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋ผ! ๐Ÿ˜‰

๐Ÿค” ์™œ Go์–ธ์–ด๋กœ ํฌ๋กค๋ง์„ ํ• ๊นŒ?

Python์ด ์›น ํฌ๋กค๋ง์—์„œ ๊ฐ€์žฅ ์œ ๋ช…ํ•˜๊ธด ํ•˜์ง€๋งŒ, Go์–ธ์–ด๋„ ์—„์ฒญ๋‚œ ์žฅ์ ๋“ค์ด ์žˆ์–ด:

1. ๋†€๋ผ์šด ์†๋„ โšก Go๋Š” ์ปดํŒŒ์ผ ์–ธ์–ด๋ผ์„œ Python๋ณด๋‹ค ํ›จ์”ฌ ๋น ๋ฅด๊ฒŒ ์‹คํ–‰๋ผ. ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•  ๋•Œ ์ด ์ฐจ์ด๊ฐ€ ์ •๋ง ํฌ๊ฒŒ ๋А๊ปด์ ธ!

2. ๊ณ ๋ฃจํ‹ด(Goroutine)์„ ํ†ตํ•œ ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ ๐Ÿ”„
์ˆ˜์ฒœ ๊ฐœ์˜ ์›น ํŽ˜์ด์ง€๋ฅผ ๋™์‹œ์— ํฌ๋กค๋งํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฒŒ Go์˜ ๊ฐ€์žฅ ํฐ ๊ฐ•์ ์ด์•ผ. ๋‹ค๋ฅธ ์–ธ์–ด์—์„œ๋Š” ๋ณต์žกํ•œ ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜์ง€๋งŒ, Go์—์„œ๋Š” `go` ํ‚ค์›Œ๋“œ ํ•˜๋‚˜๋ฉด ๋!

3. ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ ๐Ÿ’พ
๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์ด ํšจ์œจ์ ์œผ๋กœ ์ž‘๋™ํ•ด์„œ ์žฅ์‹œ๊ฐ„ ์‹คํ–‰๋˜๋Š” ํฌ๋กค๋Ÿฌ์—๋„ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฑฑ์ •์ด ์ ์–ด.

4. ๋‹จ์ผ ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐฐํฌ ๐Ÿ“ฆ
์ปดํŒŒ์ผํ•˜๋ฉด ํ•˜๋‚˜์˜ ์‹คํ–‰ ํŒŒ์ผ๋กœ ๋งŒ๋“ค์–ด์ ธ์„œ ๋ฐฐํฌ๊ฐ€ ์ •๋ง ๊ฐ„๋‹จํ•ด. ์˜์กด์„ฑ ์ง€์˜ฅ์—์„œ ํ•ด๋ฐฉ!

๐Ÿ› ๏ธ Colly ํ”„๋ ˆ์ž„์›Œํฌ ์†Œ๊ฐœ

Colly๋Š” Go์–ธ์–ด๋กœ ์ž‘์„ฑ๋œ ๊ฐ€์žฅ ์ธ๊ธฐ ์žˆ๋Š” ์›น ์Šคํฌ๋ž˜ํ•‘ ํ”„๋ ˆ์ž„์›Œํฌ์•ผ. GitHub์—์„œ 2๋งŒ ๊ฐœ ์ด์ƒ์˜ ์Šคํƒ€๋ฅผ ๋ฐ›์€ ๊ฒ€์ฆ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ง€! โญ

๐ŸŽจ Colly์˜ ์ฃผ์š” ํŠน์ง•

โ€ข ๊น”๋”ํ•œ API: ์ง๊ด€์ ์ด๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‰ฌ์šด ์ธํ„ฐํŽ˜์ด์Šค

โ€ข ๋น ๋ฅธ ์†๋„: ์ดˆ๋‹น ์ˆ˜์ฒœ ๊ฐœ์˜ ์š”์ฒญ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

โ€ข ์ž๋™ ์ฟ ํ‚ค ๋ฐ ์„ธ์…˜ ๊ด€๋ฆฌ: ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ ์‚ฌ์ดํŠธ๋„ ์‰ฝ๊ฒŒ ํฌ๋กค๋ง

โ€ข ๋กœ๋ด‡ ๋ฐฐ์ œ ํ‘œ์ค€ ์ง€์›: robots.txt๋ฅผ ์ž๋™์œผ๋กœ ํ™•์ธ

โ€ข ๋ถ„์‚ฐ ํฌ๋กค๋ง: ์—ฌ๋Ÿฌ ์„œ๋ฒ„์—์„œ ๋™์‹œ์— ํฌ๋กค๋ง ๊ฐ€๋Šฅ

โ€ข ์บ์‹ฑ ์ง€์›: ์ค‘๋ณต ์š”์ฒญ ๋ฐฉ์ง€

๐Ÿ“ฅ Colly ์„ค์น˜ํ•˜๊ธฐ

์„ค์น˜๋Š” ์ •๋ง ๊ฐ„๋‹จํ•ด! Go ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด ๋‹ค์Œ ๋ช…๋ น์–ด ํ•˜๋‚˜๋ฉด ๋์ด์•ผ:

go get -u github.com/gocolly/colly/v2

๐Ÿ’ก Tip: Go 1.16 ์ด์ƒ ๋ฒ„์ „์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด `go mod init` ๋ช…๋ น์–ด๋กœ ๋จผ์ € ๋ชจ๋“ˆ์„ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•ด. ํ”„๋กœ์ ํŠธ ํด๋”์—์„œ `go mod init myproject` ์ด๋Ÿฐ ์‹์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด ๋ผ!

๐Ÿš€ ์ฒซ ๋ฒˆ์งธ ํฌ๋กค๋Ÿฌ ๋งŒ๋“ค๊ธฐ

์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด๋ณผ๊นŒ? ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ํฌ๋กค๋Ÿฌ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด๋ณด์ž! ๐Ÿ˜Š

๐Ÿ“ ๊ธฐ๋ณธ ์˜ˆ์ œ: ์›น ํŽ˜์ด์ง€ ์ œ๋ชฉ ๊ฐ€์ ธ์˜ค๊ธฐ

package main

import (
    "fmt"
    "github.com/gocolly/colly/v2"
)

func main() {
    // Collector ์ƒ์„ฑ
    c := colly.NewCollector()

    // HTML ์š”์†Œ๋ฅผ ์ฐพ์•˜์„ ๋•Œ ์‹คํ–‰๋  ์ฝœ๋ฐฑ ํ•จ์ˆ˜
    c.OnHTML("h1", func(e *colly.HTMLElement) {
        fmt.Println("์ œ๋ชฉ:", e.Text)
    })

    // ์š”์ฒญ ์ „์— ์‹คํ–‰๋  ์ฝœ๋ฐฑ
    c.OnRequest(func(r *colly.Request) {
        fmt.Println("๋ฐฉ๋ฌธ ์ค‘:", r.URL)
    })

    // ํฌ๋กค๋ง ์‹œ์ž‘
    c.Visit("https://example.com")
}

์–ด๋•Œ? ์ƒ๊ฐ๋ณด๋‹ค ๊ฐ„๋‹จํ•˜์ง€? ์ด ์ฝ”๋“œ๊ฐ€ ํ•˜๋Š” ์ผ์„ ๋‹จ๊ณ„๋ณ„๋กœ ์„ค๋ช…ํ•ด์ค„๊ฒŒ:

Step 1: `colly.NewCollector()`๋กœ ์ƒˆ๋กœ์šด ์ปฌ๋ ‰ํ„ฐ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“ค์–ด. ์ด๊ฒŒ ํฌ๋กค๋ง์˜ ํ•ต์‹ฌ์ด์•ผ!

Step 2: `OnHTML()` ๋ฉ”์„œ๋“œ๋กœ ํŠน์ • HTML ์š”์†Œ๋ฅผ ์ฐพ์•˜์„ ๋•Œ ์‹คํ–‰ํ•  ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•ด. ์—ฌ๊ธฐ์„œ๋Š” `h1` ํƒœ๊ทธ๋ฅผ ์ฐพ๊ณ  ์žˆ์–ด.

Step 3: `OnRequest()`๋กœ ์š”์ฒญ์„ ๋ณด๋‚ด๊ธฐ ์ „์— ์‹คํ–‰ํ•  ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•ด. ๋กœ๊น…์ด๋‚˜ ๋””๋ฒ„๊น…์— ์œ ์šฉํ•ด!

Step 4: `Visit()` ๋ฉ”์„œ๋“œ๋กœ ์‹ค์ œ ํฌ๋กค๋ง์„ ์‹œ์ž‘ํ•ด. URL์„ ๋„˜๊ฒจ์ฃผ๋ฉด ์•Œ์•„์„œ ํŽ˜์ด์ง€๋ฅผ ๊ฐ€์ ธ์™€!

๐ŸŽฏ CSS ์„ ํƒ์ž ํ™œ์šฉํ•˜๊ธฐ

Colly๋Š” CSS ์„ ํƒ์ž๋ฅผ ์‚ฌ์šฉํ•ด์„œ HTML ์š”์†Œ๋ฅผ ์ฐพ์•„. ์›น ๊ฐœ๋ฐœ ๊ฒฝํ—˜์ด ์žˆ๋‹ค๋ฉด ์ต์ˆ™ํ•  ๊ฑฐ์•ผ! ๋ช‡ ๊ฐ€์ง€ ์˜ˆ์ œ๋ฅผ ๋ณผ๊นŒ?

// ํด๋ž˜์Šค๋กœ ์ฐพ๊ธฐ
c.OnHTML(".article-title", func(e *colly.HTMLElement) {
    fmt.Println(e.Text)
})

// ID๋กœ ์ฐพ๊ธฐ
c.OnHTML("#main-content", func(e *colly.HTMLElement) {
    fmt.Println(e.Text)
})

// ์†์„ฑ์œผ๋กœ ์ฐพ๊ธฐ
c.OnHTML("a[href]", func(e *colly.HTMLElement) {
    link := e.Attr("href")
    fmt.Println("๋งํฌ:", link)
})

// ๋ณต์žกํ•œ ์„ ํƒ์ž
c.OnHTML("div.container > article.post h2.title", func(e *colly.HTMLElement) {
    fmt.Println(e.Text)
})

๐Ÿ’ก Pro Tip: ๋ธŒ๋ผ์šฐ์ €์˜ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ(F12)๋ฅผ ์—ด๊ณ  ์›ํ•˜๋Š” ์š”์†Œ์—์„œ ์šฐํด๋ฆญ โ†’ "Copy selector"๋ฅผ ์„ ํƒํ•˜๋ฉด CSS ์„ ํƒ์ž๋ฅผ ์ž๋™์œผ๋กœ ๋ณต์‚ฌํ•  ์ˆ˜ ์žˆ์–ด!

๐Ÿ”ฅ ์‹ค์ „ ์˜ˆ์ œ: ๋‰ด์Šค ์‚ฌ์ดํŠธ ํฌ๋กค๋ง

์ด์ œ ์ข€ ๋” ์‹ค์šฉ์ ์ธ ์˜ˆ์ œ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! ๋‰ด์Šค ์‚ฌ์ดํŠธ์—์„œ ๊ธฐ์‚ฌ ์ œ๋ชฉ๊ณผ ๋งํฌ๋ฅผ ์ˆ˜์ง‘ํ•˜๋Š” ํฌ๋กค๋Ÿฌ์•ผ. ๐Ÿ“ฐ

package main

import (
    "fmt"
    "github.com/gocolly/colly/v2"
)

type Article struct {
    Title string
    Link  string
    Date  string
}

func main() {
    var articles []Article

    c := colly.NewCollector(
        // ํ—ˆ์šฉํ•  ๋„๋ฉ”์ธ ์ง€์ •
        colly.AllowedDomains("example-news.com"),
        // ๋™์‹œ ์š”์ฒญ ์ˆ˜ ์ œํ•œ
        colly.Async(true),
    )

    // ์š”์ฒญ ์†๋„ ์ œํ•œ (์ดˆ๋‹น 2๊ฐœ)
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 2,
        RandomDelay: 1 * time.Second,
    })

    // ๊ธฐ์‚ฌ ์ •๋ณด ์ˆ˜์ง‘
    c.OnHTML("article.news-item", func(e *colly.HTMLElement) {
        article := Article{
            Title: e.ChildText("h2.title"),
            Link:  e.ChildAttr("a", "href"),
            Date:  e.ChildText("time.published"),
        }
        articles = append(articles, article)
    })

    // ๋‹ค์Œ ํŽ˜์ด์ง€ ๋งํฌ ์ฐพ๊ธฐ
    c.OnHTML("a.next-page", func(e *colly.HTMLElement) {
        nextPage := e.Attr("href")
        c.Visit(e.Request.AbsoluteURL(nextPage))
    })

    // ์—๋Ÿฌ ์ฒ˜๋ฆฌ
    c.OnError(func(r *colly.Response, err error) {
        fmt.Println("์—๋Ÿฌ ๋ฐœ์ƒ:", r.StatusCode, err)
    })

    // ํฌ๋กค๋ง ์™„๋ฃŒ ํ›„
    c.OnScraped(func(r *colly.Response) {
        fmt.Printf("์ด %d๊ฐœ์˜ ๊ธฐ์‚ฌ๋ฅผ ์ˆ˜์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค!\n", len(articles))
    })

    // ์‹œ์ž‘!
    c.Visit("https://example-news.com/latest")
    c.Wait()

    // ๊ฒฐ๊ณผ ์ถœ๋ ฅ
    for _, article := range articles {
        fmt.Printf("์ œ๋ชฉ: %s\n๋งํฌ: %s\n๋‚ ์งœ: %s\n\n", 
            article.Title, article.Link, article.Date)
    }
}

์™€! ์ฝ”๋“œ๊ฐ€ ์ข€ ๊ธธ์–ด์กŒ์ง€? ํ•˜์ง€๋งŒ ๊ฑฑ์ • ๋งˆ. ํ•˜๋‚˜์”ฉ ๋œฏ์–ด๋ณด๋ฉด ์–ด๋ ต์ง€ ์•Š์•„! ๐Ÿค“

๐Ÿ” ์ฝ”๋“œ ๋ถ„์„

1. ๊ตฌ์กฐ์ฒด ์ •์˜
`Article` ๊ตฌ์กฐ์ฒด๋กœ ์ˆ˜์ง‘ํ•  ๋ฐ์ดํ„ฐ์˜ ํ˜•ํƒœ๋ฅผ ์ •์˜ํ–ˆ์–ด. ์ œ๋ชฉ, ๋งํฌ, ๋‚ ์งœ๋ฅผ ์ €์žฅํ•  ๊ฑฐ์•ผ.

2. Collector ์˜ต์…˜
`AllowedDomains`๋กœ ํŠน์ • ๋„๋ฉ”์ธ๋งŒ ํฌ๋กค๋งํ•˜๋„๋ก ์ œํ•œํ–ˆ์–ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์™ธ๋ถ€ ๋งํฌ๋กœ ๋น ์ ธ๋‚˜๊ฐ€๋Š” ๊ฑธ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์ง€!

3. ์†๋„ ์ œํ•œ
`Limit()` ๋ฉ”์„œ๋“œ๋กœ ์š”์ฒญ ์†๋„๋ฅผ ์ œํ•œํ–ˆ์–ด. ์„œ๋ฒ„์— ๋ถ€๋‹ด์„ ์ฃผ์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๊ผญ ํ•„์š”ํ•œ ๋ถ€๋ถ„์ด์•ผ! ์ดˆ๋‹น 2๊ฐœ์˜ ์š”์ฒญ๋งŒ ๋ณด๋‚ด๊ณ , ๋žœ๋ค ๋”œ๋ ˆ์ด๋„ ์ถ”๊ฐ€ํ–ˆ์–ด.

4. ํŽ˜์ด์ง€๋„ค์ด์…˜
"๋‹ค์Œ ํŽ˜์ด์ง€" ๋งํฌ๋ฅผ ์ฐพ์•„์„œ ์ž๋™์œผ๋กœ ๊ณ„์† ํฌ๋กค๋งํ•˜๋„๋ก ํ–ˆ์–ด. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€๋ฅผ ์ž๋™์œผ๋กœ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ์ง€!

5. ์—๋Ÿฌ ์ฒ˜๋ฆฌ
`OnError`๋กœ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ• ์ง€ ์ •์˜ํ–ˆ์–ด. ์‹ค์ „์—์„œ๋Š” ๋กœ๊น…์ด๋‚˜ ์žฌ์‹œ๋„ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์ข‹์•„.

โš™๏ธ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ ํ™œ์šฉํ•˜๊ธฐ

๊ธฐ๋ณธ์€ ๋งˆ์Šคํ„ฐํ–ˆ์œผ๋‹ˆ ์ด์ œ ์ข€ ๋” ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ๋“ค์„ ์•Œ์•„๋ณด์ž! ๐Ÿ’ช

๐Ÿ” ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•œ ์‚ฌ์ดํŠธ ํฌ๋กค๋ง

๋งŽ์€ ์‚ฌ์ดํŠธ๋“ค์ด ๋กœ๊ทธ์ธ์„ ์š”๊ตฌํ•˜์ง€? Colly๋กœ๋Š” ์ด๊ฒƒ๋„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด!

package main

import (
    "github.com/gocolly/colly/v2"
    "net/http"
    "net/url"
)

func main() {
    c := colly.NewCollector()

    // ์ฟ ํ‚ค ์ €์žฅ์†Œ ์„ค์ •
    c.OnRequest(func(r *colly.Request) {
        r.Headers.Set("User-Agent", "Mozilla/5.0...")
    })

    // ๋กœ๊ทธ์ธ ํผ ๋ฐ์ดํ„ฐ ์ค€๋น„
    loginData := url.Values{
        "username": {"your_username"},
        "password": {"your_password"},
    }

    // ๋กœ๊ทธ์ธ ์š”์ฒญ
    err := c.Post("https://example.com/login", loginData)
    if err != nil {
        panic(err)
    }

    // ๋กœ๊ทธ์ธ ํ›„ ๋ณดํ˜ธ๋œ ํŽ˜์ด์ง€ ์ ‘๊ทผ
    c.OnHTML("div.protected-content", func(e *colly.HTMLElement) {
        fmt.Println(e.Text)
    })

    c.Visit("https://example.com/protected-page")
}

๐Ÿ–ผ๏ธ ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œํ•˜๊ธฐ

์ด๋ฏธ์ง€๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์ €์žฅํ•˜๋Š” ๊ฒƒ๋„ ๊ฐ€๋Šฅํ•ด!

package main

import (
    "github.com/gocolly/colly/v2"
    "io"
    "net/http"
    "os"
    "path"
)

func main() {
    c := colly.NewCollector()

    c.OnHTML("img[src]", func(e *colly.HTMLElement) {
        imgURL := e.Request.AbsoluteURL(e.Attr("src"))
        
        // ์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ
        resp, err := http.Get(imgURL)
        if err != nil {
            return
        }
        defer resp.Body.Close()

        // ํŒŒ์ผ๋ช… ์ถ”์ถœ
        fileName := path.Base(imgURL)
        
        // ํŒŒ์ผ ์ƒ์„ฑ
        file, err := os.Create("images/" + fileName)
        if err != nil {
            return
        }
        defer file.Close()

        // ์ด๋ฏธ์ง€ ์ €์žฅ
        io.Copy(file, resp.Body)
        fmt.Println("๋‹ค์šด๋กœ๋“œ ์™„๋ฃŒ:", fileName)
    })

    c.Visit("https://example.com/gallery")
}

โš ๏ธ ์ฃผ์˜: ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ธฐ ์ „์— ํ•ด๋‹น ์‚ฌ์ดํŠธ์˜ ์ด์šฉ์•ฝ๊ด€๊ณผ ์ €์ž‘๊ถŒ์„ ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•ด์•ผ ํ•ด! ๋ฌด๋‹จ์œผ๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ˆ˜์ง‘ํ•˜๋ฉด ๋ฒ•์  ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธธ ์ˆ˜ ์žˆ์–ด.

๐ŸŒ ํ”„๋ก์‹œ ์‚ฌ์šฉํ•˜๊ธฐ

IP ์ฐจ๋‹จ์„ ํ”ผํ•˜๊ฑฐ๋‚˜ ์ง€์—ญ ์ œํ•œ์„ ์šฐํšŒํ•˜๊ธฐ ์œ„ํ•ด ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด:

package main

import (
    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/proxy"
)

func main() {
    c := colly.NewCollector()

    // ํ”„๋ก์‹œ ์„ค์ •
    rp, err := proxy.RoundRobinProxySwitcher(
        "socks5://127.0.0.1:1080",
        "http://proxy1.com:8080",
        "http://proxy2.com:8080",
    )
    if err != nil {
        panic(err)
    }
    
    c.SetProxyFunc(rp)

    // ํฌ๋กค๋ง ์‹œ์ž‘
    c.Visit("https://example.com")
}

๐Ÿ“Š ๋ฐ์ดํ„ฐ๋ฅผ JSON์œผ๋กœ ์ €์žฅํ•˜๊ธฐ

์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ JSON ํŒŒ์ผ๋กœ ์ €์žฅํ•˜๋ฉด ๋‚˜์ค‘์— ๋ถ„์„ํ•˜๊ธฐ ํŽธํ•ด!

package main

import (
    "encoding/json"
    "github.com/gocolly/colly/v2"
    "os"
)

type Product struct {
    Name  string  `json:"name"`
    Price float64 `json:"price"`
    URL   string  `json:"url"`
}

func main() {
    var products []Product

    c := colly.NewCollector()

    c.OnHTML("div.product", func(e *colly.HTMLElement) {
        product := Product{
            Name:  e.ChildText("h3.product-name"),
            Price: parsePrice(e.ChildText("span.price")),
            URL:   e.ChildAttr("a", "href"),
        }
        products = append(products, product)
    })

    c.Visit("https://example-shop.com/products")

    // JSON ํŒŒ์ผ๋กœ ์ €์žฅ
    file, err := os.Create("products.json")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    encoder := json.NewEncoder(file)
    encoder.SetIndent("", "  ")
    encoder.Encode(products)

    fmt.Println("๋ฐ์ดํ„ฐ๊ฐ€ products.json์— ์ €์žฅ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
}

func parsePrice(priceStr string) float64 {
    // ๊ฐ€๊ฒฉ ๋ฌธ์ž์—ด์„ float64๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๋กœ์ง
    // ์‹ค์ œ๋กœ๋Š” ๋” ๋ณต์žกํ•œ ํŒŒ์‹ฑ์ด ํ•„์š”ํ•  ์ˆ˜ ์žˆ์–ด
    return 0.0
}

๐ŸŽญ User-Agent์™€ ํ—ค๋” ์„ค์ •

๋งŽ์€ ์›น์‚ฌ์ดํŠธ๋“ค์ด ๋ด‡์„ ์ฐจ๋‹จํ•˜๊ธฐ ์œ„ํ•ด User-Agent๋ฅผ ์ฒดํฌํ•ด. ๊ทธ๋ž˜์„œ ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ €์ฒ˜๋Ÿผ ๋ณด์ด๋„๋ก ์„ค์ •ํ•˜๋Š” ๊ฒŒ ์ค‘์š”ํ•ด! ๐ŸŽญ

package main

import (
    "github.com/gocolly/colly/v2"
    "math/rand"
    "time"
)

func main() {
    c := colly.NewCollector()

    // User-Agent ๋ชฉ๋ก
    userAgents := []string{
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36",
    }

    // ๋žœ๋ค User-Agent ์„ค์ •
    rand.Seed(time.Now().UnixNano())
    
    c.OnRequest(func(r *colly.Request) {
        r.Headers.Set("User-Agent", userAgents[rand.Intn(len(userAgents))])
        r.Headers.Set("Accept", "text/html,application/xhtml+xml")
        r.Headers.Set("Accept-Language", "ko-KR,ko;q=0.9,en-US;q=0.8")
        r.Headers.Set("Referer", "https://www.google.com")
    })

    c.Visit("https://example.com")
}

๐Ÿ’ก Tip: ์‹ค์ œ ๋ธŒ๋ผ์šฐ์ €์˜ User-Agent๋ฅผ ๋ณต์‚ฌํ•ด์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋” ์ž์—ฐ์Šค๋Ÿฌ์›Œ! ๋ธŒ๋ผ์šฐ์ € ๊ฐœ๋ฐœ์ž ๋„๊ตฌ์˜ Network ํƒญ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์–ด.

๐Ÿšฆ ์†๋„ ์ œํ•œ๊ณผ ์˜ˆ์˜ ๋ฐ”๋ฅธ ํฌ๋กค๋ง

ํฌ๋กค๋ง์„ ํ•  ๋•Œ๋Š” ๋Œ€์ƒ ์„œ๋ฒ„์— ๋ถ€๋‹ด์„ ์ฃผ์ง€ ์•Š๋„๋ก ์กฐ์‹ฌํ•ด์•ผ ํ•ด. ์ด๊ฑด ๊ธฐ์ˆ ์ ์ธ ๋ฌธ์ œ์ด๊ธฐ๋„ ํ•˜์ง€๋งŒ ์œค๋ฆฌ์ ์ธ ๋ฌธ์ œ์ด๊ธฐ๋„ ํ•ด! ๐Ÿค

๐Ÿ“œ ํฌ๋กค๋ง ์—ํ‹ฐ์ผ“

1. robots.txt ํ™•์ธํ•˜๊ธฐ
๋ชจ๋“  ์›น์‚ฌ์ดํŠธ๋Š” `robots.txt` ํŒŒ์ผ๋กœ ํฌ๋กค๋ง ๊ทœ์น™์„ ๋ช…์‹œํ•ด. ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•˜๊ณ  ๋”ฐ๋ผ์•ผ ํ•ด!

2. ์š”์ฒญ ์†๋„ ์ œํ•œ
์ดˆ๋‹น ์ˆ˜๋ฐฑ ๊ฐœ์˜ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด ์„œ๋ฒ„๊ฐ€ ๋‹ค์šด๋  ์ˆ˜ ์žˆ์–ด. ์ ์ ˆํ•œ ๋”œ๋ ˆ์ด๋ฅผ ๋‘์ž!

3. ์บ์‹ฑ ํ™œ์šฉ
๊ฐ™์€ ํŽ˜์ด์ง€๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ์š”์ฒญํ•˜์ง€ ๋ง๊ณ  ์บ์‹ฑ์„ ์‚ฌ์šฉํ•ด.

4. ์‹๋ณ„ ๊ฐ€๋Šฅํ•œ User-Agent
์ž์‹ ์˜ ํฌ๋กค๋Ÿฌ๋ฅผ ์‹๋ณ„ํ•  ์ˆ˜ ์žˆ๋Š” User-Agent๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ์ข‹์•„. ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์—ฐ๋ฝ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก!

package main

import (
    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/extensions"
    "time"
)

func main() {
    c := colly.NewCollector()

    // robots.txt ์ž๋™ ํ™•์ธ
    c.IgnoreRobotsTxt = false

    // ์š”์ฒญ ์†๋„ ์ œํ•œ
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 2,
        Delay:       2 * time.Second,
        RandomDelay: 1 * time.Second,
    })

    // ๋žœ๋ค User-Agent ์‚ฌ์šฉ (ํ™•์žฅ ๊ธฐ๋Šฅ)
    extensions.RandomUserAgent(c)
    extensions.Referer(c)

    // ์บ์‹ฑ ์„ค์ •
    c.CacheDir = "./cache"

    c.OnRequest(func(r *colly.Request) {
        fmt.Println("๋ฐฉ๋ฌธ:", r.URL)
    })

    c.Visit("https://example.com")
}

๐Ÿ› ๋””๋ฒ„๊น…๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ

ํฌ๋กค๋งํ•˜๋‹ค ๋ณด๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์—๋Ÿฌ๋“ค์ด ๋งŽ์ด ๋ฐœ์ƒํ•ด. ์ œ๋Œ€๋กœ ๋œ ์—๋Ÿฌ ์ฒ˜๋ฆฌ์™€ ๋””๋ฒ„๊น… ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋‘๋ฉด ์ •๋ง ์œ ์šฉํ•ด! ๐Ÿ”ง

๐Ÿ“ ๋กœ๊น… ์„ค์ •ํ•˜๊ธฐ

package main

import (
    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/debug"
    "log"
    "os"
)

func main() {
    // ๋กœ๊ทธ ํŒŒ์ผ ์ƒ์„ฑ
    logFile, err := os.Create("crawler.log")
    if err != nil {
        panic(err)
    }
    defer logFile.Close()

    // ๋กœ๊ฑฐ ์„ค์ •
    logger := log.New(logFile, "", log.LstdFlags)

    c := colly.NewCollector(
        colly.Debugger(&debug.LogDebugger{}),
    )

    // ์š”์ฒญ ๋กœ๊น…
    c.OnRequest(func(r *colly.Request) {
        logger.Printf("๋ฐฉ๋ฌธ: %s\n", r.URL)
    })

    // ์‘๋‹ต ๋กœ๊น…
    c.OnResponse(func(r *colly.Response) {
        logger.Printf("์‘๋‹ต ๋ฐ›์Œ: %s (์ƒํƒœ: %d)\n", 
            r.Request.URL, r.StatusCode)
    })

    // ์—๋Ÿฌ ๋กœ๊น…
    c.OnError(func(r *colly.Response, err error) {
        logger.Printf("์—๋Ÿฌ ๋ฐœ์ƒ: %s - %v\n", r.Request.URL, err)
    })

    c.Visit("https://example.com")
}

๐Ÿ”„ ์žฌ์‹œ๋„ ๋กœ์ง ๊ตฌํ˜„ํ•˜๊ธฐ

๋„คํŠธ์›Œํฌ ๋ฌธ์ œ๋กœ ์š”์ฒญ์ด ์‹คํŒจํ•  ์ˆ˜ ์žˆ์–ด. ์žฌ์‹œ๋„ ๋กœ์ง์„ ์ถ”๊ฐ€ํ•˜๋ฉด ์•ˆ์ •์„ฑ์ด ๋†’์•„์ ธ!

package main

import (
    "github.com/gocolly/colly/v2"
    "time"
)

func main() {
    c := colly.NewCollector()

    // ์žฌ์‹œ๋„ ์„ค์ •
    maxRetries := 3
    retryCount := make(map[string]int)

    c.OnError(func(r *colly.Response, err error) {
        url := r.Request.URL.String()
        
        if retryCount[url] < maxRetries {
            retryCount[url]++
            fmt.Printf("์žฌ์‹œ๋„ %d/%d: %s\n", 
                retryCount[url], maxRetries, url)
            
            // ์ž ์‹œ ๋Œ€๊ธฐ ํ›„ ์žฌ์‹œ๋„
            time.Sleep(2 * time.Second)
            r.Request.Retry()
        } else {
            fmt.Printf("์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜ ์ดˆ๊ณผ: %s\n", url)
        }
    })

    c.Visit("https://example.com")
}

๐Ÿ’พ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๊ธฐ

๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•œ๋‹ค๋ฉด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•˜๋Š” ๊ฒŒ ํšจ์œจ์ ์ด์•ผ. MongoDB ์˜ˆ์ œ๋ฅผ ๋ณผ๊นŒ? ๐Ÿ—„๏ธ

package main

import (
    "context"
    "github.com/gocolly/colly/v2"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
    "time"
)

type Article struct {
    Title     string    `bson:"title"`
    Content   string    `bson:"content"`
    URL       string    `bson:"url"`
    CreatedAt time.Time `bson:"created_at"`
}

func main() {
    // MongoDB ์—ฐ๊ฒฐ
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    client, err := mongo.Connect(ctx, 
        options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        panic(err)
    }
    defer client.Disconnect(ctx)

    collection := client.Database("crawler").Collection("articles")

    c := colly.NewCollector()

    c.OnHTML("article", func(e *colly.HTMLElement) {
        article := Article{
            Title:     e.ChildText("h2"),
            Content:   e.ChildText("p"),
            URL:       e.Request.URL.String(),
            CreatedAt: time.Now(),
        }

        // MongoDB์— ์ €์žฅ
        _, err := collection.InsertOne(ctx, article)
        if err != nil {
            fmt.Println("์ €์žฅ ์‹คํŒจ:", err)
        } else {
            fmt.Println("์ €์žฅ ์™„๋ฃŒ:", article.Title)
        }
    })

    c.Visit("https://example.com/articles")
}

๐Ÿ’ก Tip: PostgreSQL์ด๋‚˜ MySQL์„ ์„ ํ˜ธํ•œ๋‹ค๋ฉด `database/sql` ํŒจํ‚ค์ง€๋‚˜ GORM ๊ฐ™์€ ORM์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด. ๊ฐ์ž์˜ ํ”„๋กœ์ ํŠธ ์š”๊ตฌ์‚ฌํ•ญ์— ๋งž๊ฒŒ ์„ ํƒํ•˜๋ฉด ๋ผ!

๐Ÿ” JavaScript๋กœ ๋ Œ๋”๋ง๋˜๋Š” ํŽ˜์ด์ง€ ํฌ๋กค๋ง

์š”์ฆ˜ ๋งŽ์€ ์›น์‚ฌ์ดํŠธ๋“ค์ด React, Vue ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ JavaScript๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ๋ Œ๋”๋งํ•ด. ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” Colly๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•˜๊ณ  ํ—ค๋“œ๋ฆฌ์Šค ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•„์š”ํ•ด! ๐ŸŒ

๐Ÿค– Chromedp ์‚ฌ์šฉํ•˜๊ธฐ

Chromedp๋Š” Go์—์„œ Chrome์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ. Colly์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ•๋ ฅํ•ด!

package main

import (
    "context"
    "github.com/chromedp/chromedp"
    "github.com/gocolly/colly/v2"
    "time"
)

func main() {
    // Chrome ์ปจํ…์ŠคํŠธ ์ƒ์„ฑ
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()

    // ํƒ€์ž„์•„์›ƒ ์„ค์ •
    ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
    defer cancel()

    var htmlContent string

    // JavaScript ์‹คํ–‰ ํ›„ HTML ๊ฐ€์ ธ์˜ค๊ธฐ
    err := chromedp.Run(ctx,
        chromedp.Navigate("https://example-spa.com"),
        chromedp.WaitVisible("div.content", chromedp.ByQuery),
        chromedp.Sleep(2*time.Second),
        chromedp.OuterHTML("html", &htmlContent),
    )
    if err != nil {
        panic(err)
    }

    // Colly๋กœ ํŒŒ์‹ฑ
    c := colly.NewCollector()
    
    c.OnHTML("div.dynamic-content", func(e *colly.HTMLElement) {
        fmt.Println("์ œ๋ชฉ:", e.ChildText("h2"))
    })

    c.OnHTML(htmlContent, func(e *colly.HTMLElement) {
        // HTML ํŒŒ์‹ฑ
    })
}

โš ๏ธ ์ฃผ์˜: ํ—ค๋“œ๋ฆฌ์Šค ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ฆฌ์†Œ์Šค๋ฅผ ๋งŽ์ด ์‚ฌ์šฉํ•ด. ๋Œ€๋Ÿ‰ ํฌ๋กค๋ง์—๋Š” ์ ํ•ฉํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋‹ˆ ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—๋งŒ ์‚ฌ์šฉํ•˜์ž!

๐Ÿ“Š ์‹ค์ „ ํ”„๋กœ์ ํŠธ: ๊ฐ€๊ฒฉ ๋น„๊ต ํฌ๋กค๋Ÿฌ

์ด์ œ ๋ฐฐ์šด ๋‚ด์šฉ์„ ์ข…ํ•ฉํ•ด์„œ ์‹ค์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž! ์—ฌ๋Ÿฌ ์‡ผํ•‘๋ชฐ์˜ ๊ฐ€๊ฒฉ์„ ๋น„๊ตํ•˜๋Š” ํฌ๋กค๋Ÿฌ์•ผ. ๐Ÿ›๏ธ

package main

import (
    "encoding/csv"
    "fmt"
    "github.com/gocolly/colly/v2"
    "os"
    "strconv"
    "strings"
    "time"
)

type Product struct {
    Name     string
    Price    float64
    Shop     string
    URL      string
    ImageURL string
}

func main() {
    products := []Product{}

    // ์‡ผํ•‘๋ชฐ๋ณ„ ํฌ๋กค๋Ÿฌ ์„ค์ •
    shops := map[string]string{
        "ShopA": "https://shop-a.com/search?q=",
        "ShopB": "https://shop-b.com/products?keyword=",
    }

    keyword := "๋ฌด์„ ์ด์–ดํฐ"

    for shopName, baseURL := range shops {
        c := colly.NewCollector(
            colly.AllowedDomains(getDomain(baseURL)),
        )

        c.Limit(&colly.LimitRule{
            DomainGlob:  "*",
            Parallelism: 1,
            Delay:       2 * time.Second,
        })

        // ShopA ํฌ๋กค๋ง
        if shopName == "ShopA" {
            c.OnHTML("div.product-item", func(e *colly.HTMLElement) {
                product := Product{
                    Name:     e.ChildText("h3.product-name"),
                    Price:    parsePrice(e.ChildText("span.price")),
                    Shop:     shopName,
                    URL:      e.ChildAttr("a", "href"),
                    ImageURL: e.ChildAttr("img", "src"),
                }
                products = append(products, product)
            })
        }

        // ShopB ํฌ๋กค๋ง
        if shopName == "ShopB" {
            c.OnHTML("article.item", func(e *colly.HTMLElement) {
                product := Product{
                    Name:     e.ChildText("div.title"),
                    Price:    parsePrice(e.ChildText("div.price")),
                    Shop:     shopName,
                    URL:      e.ChildAttr("a.link", "href"),
                    ImageURL: e.ChildAttr("img.thumb", "src"),
                }
                products = append(products, product)
            })
        }

        c.Visit(baseURL + keyword)
    }

    // ๊ฐ€๊ฒฉ์ˆœ ์ •๋ ฌ
    sortByPrice(products)

    // CSV๋กœ ์ €์žฅ
    saveToCSV(products, "price_comparison.csv")

    // ๊ฒฐ๊ณผ ์ถœ๋ ฅ
    fmt.Printf("\n์ด %d๊ฐœ์˜ ์ƒํ’ˆ์„ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค!\n\n", len(products))
    for i, p := range products {
        if i >= 10 {
            break
        }
        fmt.Printf("%d. %s - โ‚ฉ%.0f (%s)\n", 
            i+1, p.Name, p.Price, p.Shop)
    }
}

func parsePrice(priceStr string) float64 {
    // ๊ฐ€๊ฒฉ ๋ฌธ์ž์—ด์—์„œ ์ˆซ์ž๋งŒ ์ถ”์ถœ
    priceStr = strings.ReplaceAll(priceStr, ",", "")
    priceStr = strings.ReplaceAll(priceStr, "์›", "")
    priceStr = strings.TrimSpace(priceStr)
    
    price, err := strconv.ParseFloat(priceStr, 64)
    if err != nil {
        return 0
    }
    return price
}

func getDomain(url string) string {
    // URL์—์„œ ๋„๋ฉ”์ธ ์ถ”์ถœ
    parts := strings.Split(url, "/")
    if len(parts) >= 3 {
        return parts[2]
    }
    return ""
}

func sortByPrice(products []Product) {
    // ๊ฐ„๋‹จํ•œ ๋ฒ„๋ธ” ์ •๋ ฌ
    for i := 0; i < len(products); i++ {
        for j := i + 1; j < len(products); j++ {
            if products[i].Price > products[j].Price {
                products[i], products[j] = products[j], products[i]
            }
        }
    }
}

func saveToCSV(products []Product, filename string) {
    file, err := os.Create(filename)
    if err != nil {
        fmt.Println("CSV ํŒŒ์ผ ์ƒ์„ฑ ์‹คํŒจ:", err)
        return
    }
    defer file.Close()

    writer := csv.NewWriter(file)
    defer writer.Flush()

    // ํ—ค๋” ์ž‘์„ฑ
    writer.Write([]string{"์ƒํ’ˆ๋ช…", "๊ฐ€๊ฒฉ", "์‡ผํ•‘๋ชฐ", "URL", "์ด๋ฏธ์ง€URL"})

    // ๋ฐ์ดํ„ฐ ์ž‘์„ฑ
    for _, p := range products {
        writer.Write([]string{
            p.Name,
            fmt.Sprintf("%.0f", p.Price),
            p.Shop,
            p.URL,
            p.ImageURL,
        })
    }

    fmt.Println("CSV ํŒŒ์ผ ์ €์žฅ ์™„๋ฃŒ:", filename)
}

์ด ํ”„๋กœ์ ํŠธ๋Š” ์—ฌ๋Ÿฌ ์‡ผํ•‘๋ชฐ์„ ๋™์‹œ์— ํฌ๋กค๋งํ•ด์„œ ๊ฐ€๊ฒฉ์„ ๋น„๊ตํ•˜๊ณ  CSV ํŒŒ์ผ๋กœ ์ €์žฅํ•ด. ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ๊ฐ ์‡ผํ•‘๋ชฐ์˜ HTML ๊ตฌ์กฐ์— ๋งž๊ฒŒ ์„ ํƒ์ž๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•ด! ๐Ÿ˜Š

๐ŸŽ“ ์„ฑ๋Šฅ ์ตœ์ ํ™” ํŒ

ํฌ๋กค๋Ÿฌ์˜ ์„ฑ๋Šฅ์„ ์ตœ๋Œ€๋กœ ๋Œ์–ด์˜ฌ๋ฆฌ๋Š” ๋ฐฉ๋ฒ•๋“ค์„ ์•Œ์•„๋ณด์ž! ๐Ÿš€

โšก ์ตœ์ ํ™” ์ „๋žต

1. ๊ณ ๋ฃจํ‹ด ํ™œ์šฉ
Go์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์ธ ๊ณ ๋ฃจํ‹ด์„ ์ ๊ทน ํ™œ์šฉํ•˜์ž. Colly๋Š” `Async(true)` ์˜ต์…˜์œผ๋กœ ์‰ฝ๊ฒŒ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ๋ฅผ ํ•  ์ˆ˜ ์žˆ์–ด.

2. ์บ์‹ฑ ์ „๋žต
๊ฐ™์€ ํŽ˜์ด์ง€๋ฅผ ๋ฐ˜๋ณตํ•ด์„œ ์š”์ฒญํ•˜์ง€ ์•Š๋„๋ก ์บ์‹ฑ์„ ์‚ฌ์šฉํ•ด. ๋””์Šคํฌ ์บ์‹œ๋‚˜ Redis๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด.

3. ์„ ํƒ์  ๋‹ค์šด๋กœ๋“œ
ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๋‹ค์šด๋กœ๋“œํ•˜์ž. ์ด๋ฏธ์ง€๋‚˜ CSS ํŒŒ์ผ์€ ํ•„์š” ์—†๋‹ค๋ฉด ์ฐจ๋‹จํ•ด.

4. ์—ฐ๊ฒฐ ํ’€๋ง
HTTP ์—ฐ๊ฒฐ์„ ์žฌ์‚ฌ์šฉํ•˜๋ฉด ์„ฑ๋Šฅ์ด ํฌ๊ฒŒ ํ–ฅ์ƒ๋ผ. Colly๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์—ฐ๊ฒฐ ํ’€๋ง์„ ์ง€์›ํ•ด.

5. ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ
๋Œ€๋Ÿ‰ ํฌ๋กค๋ง ์‹œ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ์กฐ์‹ฌํ•ด์•ผ ํ•ด. ์ฃผ๊ธฐ์ ์œผ๋กœ ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜์„ ์‹คํ–‰ํ•˜๊ฑฐ๋‚˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ์ €์žฅํ•˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์ œ๊ฑฐํ•˜์ž.

package main

import (
    "github.com/gocolly/colly/v2"
    "runtime"
    "time"
)

func main() {
    c := colly.NewCollector(
        colly.Async(true),
        colly.CacheDir("./cache"),
    )

    // ๋™์‹œ ์š”์ฒญ ์ˆ˜ ์ตœ์ ํ™”
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 10, // CPU ์ฝ”์–ด ์ˆ˜์— ๋งž๊ฒŒ ์กฐ์ •
        Delay:       100 * time.Millisecond,
    })

    // ๋ถˆํ•„์š”ํ•œ ๋ฆฌ์†Œ์Šค ์ฐจ๋‹จ
    c.OnRequest(func(r *colly.Request) {
        // ์ด๋ฏธ์ง€, CSS, JS ํŒŒ์ผ ์ฐจ๋‹จ
        if strings.HasSuffix(r.URL.Path, ".jpg") ||
           strings.HasSuffix(r.URL.Path, ".css") ||
           strings.HasSuffix(r.URL.Path, ".js") {
            r.Abort()
        }
    })

    // ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ
    processedCount := 0
    c.OnScraped(func(r *colly.Response) {
        processedCount++
        
        // 1000๊ฐœ๋งˆ๋‹ค ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰์…˜
        if processedCount%1000 == 0 {
            runtime.GC()
            fmt.Printf("์ฒ˜๋ฆฌ ์™„๋ฃŒ: %d๊ฐœ\n", processedCount)
        }
    })

    // ํฌ๋กค๋ง ์‹œ์ž‘
    for i := 1; i <= 100; i++ {
        c.Visit(fmt.Sprintf("https://example.com/page/%d", i))
    }

    c.Wait()
}

๐Ÿ›ก๏ธ ๋ฒ•์  ๊ณ ๋ ค์‚ฌํ•ญ๊ณผ ์œค๋ฆฌ

๊ธฐ์ˆ ์ ์œผ๋กœ ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ํ•ด์„œ ๋ชจ๋“  ๊ฑธ ํฌ๋กค๋งํ•ด๋„ ๋˜๋Š” ๊ฑด ์•„๋‹ˆ์•ผ. ๋ฒ•์ , ์œค๋ฆฌ์  ์ธก๋ฉด์„ ๊ผญ ๊ณ ๋ คํ•ด์•ผ ํ•ด! โš–๏ธ

โš ๏ธ ์ฃผ์˜ํ•ด์•ผ ํ•  ์‚ฌํ•ญ๋“ค

1. ์ด์šฉ์•ฝ๊ด€ ํ™•์ธ
๋Œ€๋ถ€๋ถ„์˜ ์›น์‚ฌ์ดํŠธ๋Š” ์ด์šฉ์•ฝ๊ด€์—์„œ ์ž๋™ํ™”๋œ ์ ‘๊ทผ์„ ์ œํ•œํ•˜๊ณ  ์žˆ์–ด. ๋ฐ˜๋“œ์‹œ ํ™•์ธํ•˜๊ณ  ํ•„์š”ํ•˜๋ฉด ํ—ˆ๊ฐ€๋ฅผ ๋ฐ›์ž.

2. ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ
๊ฐœ์ธ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๋ฉด ๊ฐœ์ธ์ •๋ณด๋ณดํ˜ธ๋ฒ• ์œ„๋ฐ˜์ด ๋  ์ˆ˜ ์žˆ์–ด. ํŠนํžˆ ์ด๋ฉ”์ผ, ์ „ํ™”๋ฒˆํ˜ธ ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ์กฐ์‹ฌํ•ด์•ผ ํ•ด.

3. ์ €์ž‘๊ถŒ
์›น์‚ฌ์ดํŠธ์˜ ์ฝ˜ํ…์ธ ๋Š” ์ €์ž‘๊ถŒ์œผ๋กœ ๋ณดํ˜ธ๋ฐ›์•„. ์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ์—…์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ €์ž‘๊ถŒ์ž์˜ ํ—ˆ๊ฐ€๊ฐ€ ํ•„์š”ํ•ด.

4. ์„œ๋ฒ„ ๋ถ€ํ•˜
๊ณผ๋„ํ•œ ์š”์ฒญ์œผ๋กœ ์„œ๋ฒ„์— ๋ถ€๋‹ด์„ ์ฃผ๋ฉด ์—…๋ฌด๋ฐฉํ•ด์ฃ„๊ฐ€ ์„ฑ๋ฆฝ๋  ์ˆ˜ ์žˆ์–ด. ์ ์ ˆํ•œ ์†๋„ ์ œํ•œ์„ ๊ผญ ์„ค์ •ํ•˜์ž.

5. API ์šฐ์„  ์‚ฌ์šฉ
๋งŽ์€ ์„œ๋น„์Šค๋“ค์ด ๊ณต์‹ API๋ฅผ ์ œ๊ณตํ•ด. ํฌ๋กค๋ง๋ณด๋‹ค๋Š” API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒŒ ๋” ์•ˆ์ „ํ•˜๊ณ  ํ•ฉ๋ฒ•์ ์ด์•ผ.

๐Ÿ’ก Pro Tip: ํฌ๋กค๋ง ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๋ฒ•๋ฅ  ์ „๋ฌธ๊ฐ€์™€ ์ƒ๋‹ดํ•˜๋Š” ๊ฑธ ์ถ”์ฒœํ•ด. ํŠนํžˆ ์ƒ์—…์  ๋ชฉ์ ์ด๋ผ๋ฉด ๋”์šฑ ์ค‘์š”ํ•ด! ์žฌ๋Šฅ๋„ท์—์„œ ๋ฒ•๋ฅ  ์ž๋ฌธ ์„œ๋น„์Šค๋ฅผ ์ฐพ์•„๋ณผ ์ˆ˜๋„ ์žˆ์–ด.

๐Ÿ”ง ์‹ค์ „ ๋ฌธ์ œ ํ•ด๊ฒฐ

ํฌ๋กค๋งํ•˜๋‹ค ๋ณด๋ฉด ์ž์ฃผ ๋งˆ์ฃผ์น˜๋Š” ๋ฌธ์ œ๋“ค๊ณผ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ๐Ÿ”

โŒ ๋ฌธ์ œ 1: 403 Forbidden ์—๋Ÿฌ

์›์ธ: ์„œ๋ฒ„๊ฐ€ ๋ด‡์œผ๋กœ ์ธ์‹ํ•ด์„œ ์ ‘๊ทผ์„ ์ฐจ๋‹จํ–ˆ์–ด.

ํ•ด๊ฒฐ:

c.OnRequest(func(r *colly.Request) {
    r.Headers.Set("User-Agent", "Mozilla/5.0...")
    r.Headers.Set("Accept", "text/html,application/xhtml+xml")
    r.Headers.Set("Accept-Language", "ko-KR,ko;q=0.9")
    r.Headers.Set("Referer", "https://www.google.com")
    r.Headers.Set("Connection", "keep-alive")
})

โŒ ๋ฌธ์ œ 2: ๋นˆ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜

์›์ธ: JavaScript๋กœ ๋™์ ์œผ๋กœ ๋กœ๋“œ๋˜๋Š” ์ฝ˜ํ…์ธ ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์•„.

ํ•ด๊ฒฐ: Chromedp ๊ฐ™์€ ํ—ค๋“œ๋ฆฌ์Šค ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ๋„คํŠธ์›Œํฌ ํƒญ์—์„œ ์‹ค์ œ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ฐพ์•„์„œ ์ง์ ‘ ํ˜ธ์ถœํ•ด.

โŒ ๋ฌธ์ œ 3: ๋ฉ”๋ชจ๋ฆฌ ๋ถ€์กฑ

์›์ธ: ๋Œ€๋Ÿ‰์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜๊ณ  ์žˆ์–ด.

ํ•ด๊ฒฐ:

// ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ํŒŒ์ผ์ด๋‚˜ DB์— ์ €์žฅ
c.OnHTML("div.item", func(e *colly.HTMLElement) {
    data := extractData(e)
    saveToDatabase(data) // ์ฆ‰์‹œ ์ €์žฅ
    // ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅํ•˜์ง€ ์•Š์Œ
})

// ์ฃผ๊ธฐ์ ์œผ๋กœ GC ์‹คํ–‰
if count%1000 == 0 {
    runtime.GC()
}

โŒ ๋ฌธ์ œ 4: IP ์ฐจ๋‹จ

์›์ธ: ๊ฐ™์€ IP์—์„œ ๋„ˆ๋ฌด ๋งŽ์€ ์š”์ฒญ์„ ๋ณด๋ƒˆ์–ด.

ํ•ด๊ฒฐ:

  • โ€ข ์š”์ฒญ ์†๋„๋ฅผ ์ค„์—ฌ
  • โ€ข ํ”„๋ก์‹œ๋ฅผ ์‚ฌ์šฉํ•ด
  • โ€ข VPN์„ ํ™œ์šฉํ•ด
  • โ€ข ์‹œ๊ฐ„๋Œ€๋ฅผ ๋ถ„์‚ฐํ•ด์„œ ํฌ๋กค๋งํ•ด

โŒ ๋ฌธ์ œ 5: CAPTCHA

์›์ธ: ์‚ฌ์ดํŠธ๊ฐ€ ๋ด‡ ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด CAPTCHA๋ฅผ ์‚ฌ์šฉํ•ด.

ํ•ด๊ฒฐ:

  • โ€ข ์š”์ฒญ ํŒจํ„ด์„ ๋” ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋งŒ๋“ค์–ด
  • โ€ข ์ฟ ํ‚ค์™€ ์„ธ์…˜์„ ์ ์ ˆํžˆ ๊ด€๋ฆฌํ•ด
  • โ€ข CAPTCHA ํ•ด๊ฒฐ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•ด (2Captcha ๋“ฑ)
  • โ€ข ๊ฐ€๋Šฅํ•˜๋ฉด API๋ฅผ ์‚ฌ์šฉํ•ด

๐Ÿ“š ์ถ”๊ฐ€ ํ•™์Šต ๋ฆฌ์†Œ์Šค

๋” ๊นŠ์ด ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋Ÿฐ ์ž๋ฃŒ๋“ค์„ ์ฐธ๊ณ ํ•ด๋ด! ๐Ÿ“–

๐Ÿ”— ์œ ์šฉํ•œ ๋งํฌ๋“ค

๊ณต์‹ ๋ฌธ์„œ:

โ€ข Colly ๊ณต์‹ ๋ฌธ์„œ: http://go-colly.org/
โ€ข Go ๊ณต์‹ ๋ฌธ์„œ: https://golang.org/doc/

GitHub ์ €์žฅ์†Œ:

โ€ข Colly: https://github.com/gocolly/colly
โ€ข Chromedp: https://github.com/chromedp/chromedp

์ปค๋ฎค๋‹ˆํ‹ฐ:

โ€ข Go ํ•œ๊ตญ ์ปค๋ฎค๋‹ˆํ‹ฐ
โ€ข Reddit r/golang
โ€ข Stack Overflow

๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ:

โ€ข goquery: jQuery ์Šคํƒ€์ผ์˜ HTML ํŒŒ์‹ฑ
โ€ข soup: Python BeautifulSoup ์Šคํƒ€์ผ
โ€ข rod: ๋˜ ๋‹ค๋ฅธ ํ—ค๋“œ๋ฆฌ์Šค ๋ธŒ๋ผ์šฐ์ € ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

๐ŸŽฏ ์‹ค์ „ ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด

๋ฐฐ์šด ๋‚ด์šฉ์„ ํ™œ์šฉํ•ด์„œ ์ด๋Ÿฐ ํ”„๋กœ์ ํŠธ๋“ค์„ ๋งŒ๋“ค์–ด๋ณผ ์ˆ˜ ์žˆ์–ด! ๐Ÿ’ก

1. ๋ถ€๋™์‚ฐ ๊ฐ€๊ฒฉ ๋ชจ๋‹ˆํ„ฐ๋ง ์‹œ์Šคํ…œ ๐Ÿ 

๋ถ€๋™์‚ฐ ์‚ฌ์ดํŠธ๋ฅผ ์ฃผ๊ธฐ์ ์œผ๋กœ ํฌ๋กค๋งํ•ด์„œ ๊ฐ€๊ฒฉ ๋ณ€๋™์„ ์ถ”์ ํ•˜๊ณ  ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ์‹œ์Šคํ…œ. ์›ํ•˜๋Š” ์กฐ๊ฑด์˜ ๋งค๋ฌผ์ด ๋‚˜์˜ค๋ฉด ์ฆ‰์‹œ ์•Œ๋ ค์ค˜!

2. ๋‰ด์Šค ์• ๊ทธ๋ฆฌ๊ฒŒ์ดํ„ฐ ๐Ÿ“ฐ

์—ฌ๋Ÿฌ ๋‰ด์Šค ์‚ฌ์ดํŠธ์—์„œ ํŠน์ • ํ‚ค์›Œ๋“œ ๊ด€๋ จ ๊ธฐ์‚ฌ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์š”์•ฝํ•ด์„œ ๋ณด์—ฌ์ฃผ๋Š” ์„œ๋น„์Šค. ํŠธ๋ Œ๋“œ ๋ถ„์„๋„ ๊ฐ€๋Šฅํ•ด!

3. ์ฑ„์šฉ ๊ณต๊ณ  ์•Œ๋ฆผ ์„œ๋น„์Šค ๐Ÿ’ผ

์—ฌ๋Ÿฌ ์ฑ„์šฉ ์‚ฌ์ดํŠธ๋ฅผ ํฌ๋กค๋งํ•ด์„œ ์›ํ•˜๋Š” ์กฐ๊ฑด์˜ ๊ณต๊ณ ๊ฐ€ ์˜ฌ๋ผ์˜ค๋ฉด ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ์„œ๋น„์Šค. ์ทจ์—… ์ค€๋น„์ƒ๋“ค์—๊ฒŒ ์œ ์šฉํ•ด!

4. ์†Œ์…œ ๋ฏธ๋””์–ด ๋ถ„์„ ๋„๊ตฌ ๐Ÿ“Š

ํŠน์ • ์ฃผ์ œ๋‚˜ ๋ธŒ๋žœ๋“œ์— ๋Œ€ํ•œ ์†Œ์…œ ๋ฏธ๋””์–ด ๋ฐ˜์‘์„ ์ˆ˜์ง‘ํ•˜๊ณ  ๋ถ„์„ํ•˜๋Š” ๋„๊ตฌ. ๋งˆ์ผ€ํŒ…์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

5. ํ•™์ˆ  ๋…ผ๋ฌธ ๊ฒ€์ƒ‰ ์—”์ง„ ๐ŸŽ“

์—ฌ๋Ÿฌ ํ•™์ˆ  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ํฌ๋กค๋งํ•ด์„œ ํ†ตํ•ฉ ๊ฒ€์ƒ‰ ์„œ๋น„์Šค๋ฅผ ๋งŒ๋“ค์–ด. ์—ฐ๊ตฌ์ž๋“ค์—๊ฒŒ ์œ ์šฉํ•  ๊ฑฐ์•ผ!

๐Ÿš€ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ

์—ฌ๊ธฐ๊นŒ์ง€ ์™”๋‹ค๋ฉด ์ด๋ฏธ Go ํฌ๋กค๋ง์˜ ๊ธฐ์ดˆ๋Š” ๋งˆ์Šคํ„ฐํ•œ ๊ฑฐ์•ผ! ์ถ•ํ•˜ํ•ด! ๐ŸŽ‰

๐ŸŽฏ ๋” ๋‚˜์•„๊ฐ€๊ธฐ

1. ๋ถ„์‚ฐ ํฌ๋กค๋ง
์—ฌ๋Ÿฌ ์„œ๋ฒ„์—์„œ ๋™์‹œ์— ํฌ๋กค๋งํ•˜๋Š” ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•ด๋ด. Kafka๋‚˜ RabbitMQ ๊ฐ™์€ ๋ฉ”์‹œ์ง€ ํ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์ข‹์•„.

2. ๋จธ์‹ ๋Ÿฌ๋‹ ํ†ตํ•ฉ
์ˆ˜์ง‘ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ์— ํ™œ์šฉํ•ด๋ด. ๊ฐ์„ฑ ๋ถ„์„, ๋ถ„๋ฅ˜, ์˜ˆ์ธก ๋“ฑ ๋‹ค์–‘ํ•œ ์‘์šฉ์ด ๊ฐ€๋Šฅํ•ด!

3. ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง
์›น์†Œ์ผ“์ด๋‚˜ Server-Sent Events๋ฅผ ํ™œ์šฉํ•ด์„œ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์ฒ˜๋ฆฌํ•ด๋ด.

4. ํด๋ผ์šฐ๋“œ ๋ฐฐํฌ
AWS Lambda, Google Cloud Functions ๊ฐ™์€ ์„œ๋ฒ„๋ฆฌ์Šค ํ™˜๊ฒฝ์—์„œ ํฌ๋กค๋Ÿฌ๋ฅผ ์‹คํ–‰ํ•ด๋ด. ๋น„์šฉ ํšจ์œจ์ ์ด์•ผ!

5. ๋ชจ๋‹ˆํ„ฐ๋ง๊ณผ ์•Œ๋ฆผ
Prometheus, Grafana ๊ฐ™์€ ๋„๊ตฌ๋กœ ํฌ๋กค๋Ÿฌ์˜ ์ƒํƒœ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ , ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด Slack์ด๋‚˜ ์ด๋ฉ”์ผ๋กœ ์•Œ๋ฆผ์„ ๋ฐ›๋„๋ก ์„ค์ •ํ•ด๋ด.

๐Ÿ’ฌ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€! ์ •๋ง ๊ธด ์—ฌ์ •์ด์—ˆ์ง€? ๐ŸŽŠ Go์–ธ์–ด์™€ Colly๋ฅผ ํ™œ์šฉํ•œ ์›น ํฌ๋กค๋ง์— ๋Œ€ํ•ด ์ •๋ง ๋งŽ์€ ๊ฑธ ๋ฐฐ์› ์–ด. ๊ธฐ์ดˆ๋ถ€ํ„ฐ ๊ณ ๊ธ‰ ๊ธฐ๋Šฅ, ์‹ค์ „ ํ”„๋กœ์ ํŠธ๊นŒ์ง€ ๋‹ค๋ค˜์œผ๋‹ˆ ์ด์ œ ์ง์ ‘ ํฌ๋กค๋Ÿฌ๋ฅผ ๋งŒ๋“ค์–ด๋ณผ ์ฐจ๋ก€์•ผ!

ํ•ต์‹ฌ ์š”์•ฝ ์ •๋ฆฌ:

โœ… Go์–ธ์–ด๋Š” ๋น ๋ฅธ ์†๋„์™€ ๋›ฐ์–ด๋‚œ ๋™์‹œ์„ฑ ์ฒ˜๋ฆฌ๋กœ ํฌ๋กค๋ง์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์–ด
โœ… Colly๋Š” ์ง๊ด€์ ์ด๊ณ  ๊ฐ•๋ ฅํ•œ ์›น ์Šคํฌ๋ž˜ํ•‘ ํ”„๋ ˆ์ž„์›Œํฌ์•ผ
โœ… ์ ์ ˆํ•œ ์†๋„ ์ œํ•œ๊ณผ ์—๋Ÿฌ ์ฒ˜๋ฆฌ๋Š” ํ•„์ˆ˜์•ผ
โœ… robots.txt๋ฅผ ํ™•์ธํ•˜๊ณ  ๋ฒ•์ , ์œค๋ฆฌ์  ์ธก๋ฉด์„ ๊ณ ๋ คํ•ด์•ผ ํ•ด
โœ… ์‹ค์ „์—์„œ๋Š” ๋กœ๊น…, ์žฌ์‹œ๋„, ์บ์‹ฑ ๋“ฑ์˜ ๊ธฐ๋Šฅ์ด ์ค‘์š”ํ•ด
โœ… JavaScript ๋ Œ๋”๋ง ํŽ˜์ด์ง€๋Š” ํ—ค๋“œ๋ฆฌ์Šค ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•„์š”ํ•ด
โœ… ๋ฐ์ดํ„ฐ๋Š” ์ ์ ˆํ•œ ํ˜•์‹(JSON, CSV, DB)์œผ๋กœ ์ €์žฅํ•˜์ž

ํฌ๋กค๋ง์€ ์ •๋ง ์œ ์šฉํ•œ ๊ธฐ์ˆ ์ด์ง€๋งŒ, ์ฑ…์ž„๊ฐ ์žˆ๊ฒŒ ์‚ฌ์šฉํ•ด์•ผ ํ•ด. ํ•ญ์ƒ ๋Œ€์ƒ ์›น์‚ฌ์ดํŠธ์˜ ์ด์šฉ์•ฝ๊ด€์„ ํ™•์ธํ•˜๊ณ , ์„œ๋ฒ„์— ๋ถ€๋‹ด์„ ์ฃผ์ง€ ์•Š๋„๋ก ์กฐ์‹ฌํ•˜์ž. ๊ทธ๋ฆฌ๊ณ  ๊ฐœ์ธ์ •๋ณด๋‚˜ ์ €์ž‘๊ถŒ์ด ์žˆ๋Š” ์ฝ˜ํ…์ธ ๋Š” ํŠนํžˆ ์ฃผ์˜ํ•ด์•ผ ํ•ด! ๐Ÿ›ก๏ธ

ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ๋ฐฐ์šฐ๋Š” ๊ฑด ํ˜ผ์ž์„œ๋„ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋•Œ๋กœ๋Š” ์ „๋ฌธ๊ฐ€์˜ ๋„์›€์ด ํ•„์š”ํ•  ๋•Œ๊ฐ€ ์žˆ์–ด. ๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ๊ฑฐ๋‚˜ ๋” ๊นŠ์ด ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ๊ฒฝํ—˜ ๋งŽ์€ ๊ฐœ๋ฐœ์ž๋“ค์—๊ฒŒ ๋ฉ˜ํ† ๋ง์„ ๋ฐ›๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์•ผ. ์‹ค๋ฌด ๊ฒฝํ—˜์„ ๊ฐ€์ง„ ์‚ฌ๋žŒ๋“ค์˜ ์กฐ์–ธ์€ ์ •๋ง ๊ฐ’์ง€๊ฑฐ๋“ ! ๐Ÿ˜Š

๐Ÿ’ก ๋งˆ์ง€๋ง‰ ์กฐ์–ธ: ์ž‘์€ ํ”„๋กœ์ ํŠธ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ ์  ๋ณต์žกํ•œ ๊ฑธ ๋งŒ๋“ค์–ด๋ด. ์‹คํŒจ๋ฅผ ๋‘๋ ค์›Œํ•˜์ง€ ๋ง๊ณ , ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๋ฅผ ์นœ๊ตฌ์ฒ˜๋Ÿผ ๋Œ€ํ•˜์ž. ์—๋Ÿฌ๋Š” ์šฐ๋ฆฌ๊ฐ€ ๋ญ”๊ฐ€๋ฅผ ๋ฐฐ์šฐ๊ณ  ์žˆ๋‹ค๋Š” ์ฆ๊ฑฐ๋‹ˆ๊นŒ! ํ™”์ดํŒ…! ๐Ÿ’ช

์ด์ œ ๋‹น์‹ ๋„ Go ํฌ๋กค๋ง ๋งˆ์Šคํ„ฐ๊ฐ€ ๋  ์ค€๋น„๊ฐ€ ๋์–ด! ๋ฉ‹์ง„ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๊ณ , ๋ฐฐ์šด ๋‚ด์šฉ์„ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค๊ณผ๋„ ๊ณต์œ ํ•ด๋ด. ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์ปค๋ฎค๋‹ˆํ‹ฐ๋Š” ์„œ๋กœ ๋„์šฐ๋ฉฐ ์„ฑ์žฅํ•˜๋Š” ๊ณณ์ด๋‹ˆ๊นŒ! ๐ŸŒŸ

ํ–‰์šด์„ ๋นŒ์–ด! ๊ทธ๋ฆฌ๊ณ  ์ฆ๊ฑฐ์šด ์ฝ”๋”ฉ ๋˜๊ธธ! Happy Coding! ๐Ÿš€โœจ

Happy Crawling! ๐Ÿ•ท๏ธ ๐Ÿ’ป ๐Ÿš€ Keep Learning, Keep Growing

์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๋‹ค๋ฉด ์ฃผ๋ณ€ ์‚ฌ๋žŒ๋“ค๊ณผ ๊ณต์œ ํ•ด์ฃผ์„ธ์š”! ๐Ÿ™Œ
์งˆ๋ฌธ์ด๋‚˜ ํ”ผ๋“œ๋ฐฑ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“  ํ™˜์˜์ด์—์š”! ๐Ÿ˜Š
ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด๊ฐ€์š”! ๐Ÿ’™

๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.