기술나눔

Go1.19 크롤러 프레임워크: 사이트 템플릿의 자동 크롤링을 단순화합니다.

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

소개

Web Scraper는 웹사이트에서 자동으로 데이터를 추출하는 도구입니다. 이는 데이터 수집, 검색 엔진 최적화, 시장 조사 및 기타 분야에서 널리 사용됩니다. 이 기사에서는 Go 1.19를 사용하여 개발자가 효율적으로 데이터를 수집할 수 있도록 단순화된 자동화된 사이트 템플릿 크롤링 도구를 구현하는 방법을 자세히 소개합니다.

목차

  1. 환경 준비
  2. 웹 크롤러의 기본 개념
  3. 크롤러 프레임워크 선택 이동
  4. 크롤러 설계의 기본 프로세스
  5. 간단한 웹 크롤러 구현
  6. HTML 콘텐츠 구문 분석
  7. 크롤러의 동시 처리
  8. 데이터 저장고
  9. 오류 처리 및 재시도 메커니즘
  10. 실제 사례: 뉴스 웹사이트 크롤링
  11. 고급 기능 및 최적화
  12. 결론적으로

1. 환경 준비

시작하기 전에 시스템에 Go 1.19가 설치되어 있는지 확인하세요. 다음 명령을 사용하여 Go 버전을 확인할 수 있습니다.

go version
  • 1

아직 Go를 설치하지 않았다면 다음에서 다운로드할 수 있습니다. 공식 홈페이지로 이동 최신 버전을 다운로드하여 설치하세요.

2. 웹 크롤러의 기본 개념

웹 크롤러의 기본 작업 흐름은 다음과 같습니다.

  1. 요청 보내기: 대상 웹페이지에 HTTP 요청을 보냅니다.
  2. 응답 받기: 서버가 반환한 HTTP 응답을 받습니다.
  3. 내용 분석: 응답에서 필요한 데이터를 추출합니다.
  4. 데이터 저장: 추출된 데이터를 로컬 파일이나 데이터베이스에 저장합니다.
  5. 링크 처리: 웹페이지에서 링크를 추출하고 계속해서 다른 페이지를 크롤링합니다.

3. 크롤러 프레임워크 선택 이동

Go 언어에는 다음과 같은 여러 가지 인기 있는 크롤러 프레임워크가 있습니다.

  • Colly: 풍부한 기능과 우수한 성능을 제공하는 빠르고 우아한 크롤러 프레임워크입니다.
  • Goquery: HTML 문서를 구문 분석하고 조작하기 위한 jQuery와 유사한 라이브러리입니다.
  • HTTP 클라이언트: 표준 라이브러리의 net/http 패키지는 가장 간단한 HTTP 요청 요구 사항을 충족할 수 있습니다.

이 글에서는 주로 웹 크롤링과 콘텐츠 파싱을 위해 Colly와 Goquery를 사용할 것입니다.

4. 크롤러 설계의 기본 프로세스

우리는 단순화된 자동화된 사이트 템플릿 크롤링 도구를 설계할 것입니다. 기본 프로세스는 다음과 같습니다.

  1. 크롤러 구성을 초기화합니다.
  2. 웹페이지 콘텐츠를 얻기 위해 HTTP 요청을 보냅니다.
  3. Goquery를 사용하여 HTML 콘텐츠를 구문 분석하고 필요한 데이터를 추출합니다.
  4. 데이터를 로컬 파일이나 데이터베이스에 저장합니다.
  5. 오류 처리 및 재시도 메커니즘.
  6. 동시 처리를 사용하여 크롤링 효율성을 향상합니다.

5. 간단한 웹 크롤러 구현

먼저 새 Go 프로젝트를 만듭니다.

mkdir go_scraper
cd go_scraper
go mod init go_scraper
  • 1
  • 2
  • 3

그런 다음 Colly와 Goquery를 설치합니다.

go get -u github.com/gocolly/colly
go get -u github.com/PuerkitoBio/goquery
  • 1
  • 2

다음으로 웹 콘텐츠를 크롤링하는 간단한 크롤러를 작성합니다.

package main

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

func main() {
    // 创建一个新的爬虫实例
    c := colly.NewCollector()

    // 设置请求时的回调函数
    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL.String())
    })

    // 设置响应时的回调函数
    c.OnResponse(func(r *colly.Response) {
        fmt.Println("Visited", r.Request.URL)
        fmt.Println("Response:", string(r.Body))
    })

    // 设置错误处理的回调函数
    c.OnError(func(r *colly.Response, err error) {
        fmt.Println("Error:", err)
    })

    // 设置HTML解析时的回调函数
    c.OnHTML("title", func(e *colly.HTMLElement) {
        fmt.Println("Title:", e.Text)
    })

    // 开始爬取
    c.Visit("http://example.com")
}
  • 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

위 코드를 실행하면 http://example.com의 콘텐츠가 크롤링되고 페이지 제목이 인쇄됩니다.

6. HTML 콘텐츠 구문 분석

웹페이지에서 필요한 데이터를 추출하려면 Goquery를 사용하여 HTML 콘텐츠를 구문 분석해야 합니다. 다음 예에서는 Goquery를 사용하여 웹페이지에서 링크와 텍스트를 추출하는 방법을 보여줍니다.

package main

import (
    "fmt"
    "github.com/gocolly/colly"
    "github.com/PuerkitoBio/goquery"
)

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

    c.OnHTML("body", func(e *colly.HTMLElement) {
        e.DOM.Find("a").Each(func(index int, item *goquery.Selection) {
            link, _ := item.Attr("href")
            text := item.Text()
            fmt.Printf("Link #%d: %s (%s)n", index, text, link)
        })
    })

    c.Visit("http://example.com")
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

7. 크롤러의 동시 처리

크롤러의 효율성을 향상시키기 위해 Colly의 동시성 기능을 사용할 수 있습니다.

package main

import (
    "fmt"
    "github.com/gocolly/colly"
    "github.com/PuerkitoBio/goquery"
    "log"
    "time"
)

func main() {
    c := colly.NewCollector(
        colly.Async(true), // 启用异步模式
    )

    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 2, // 设置并发数
        Delay:       2 * time.Second,
    })

    c.OnHTML("body", func(e *colly.HTMLElement) {
        e.DOM.Find("a").Each(func(index int, item *goquery.Selection) {
            link, _ := item.Attr("href")
            text := item.Text()
            fmt.Printf("Link #%d: %s (%s)n", index, text, link)
            c.Visit(e.Request.AbsoluteURL(link))
        })
    })

    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL.String())
    })

    c.OnError(func(r *colly.Response, err error) {
        log.Println("Error:", err)
    })

    c.Visit("http://example.com")

    c.Wait() // 等待所有异步任务完成
}
  • 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

8. 데이터 저장

캡처된 데이터를 로컬 파일이나 데이터베이스에 저장합니다. 다음은 CSV 파일의 예입니다.

package main

import (
    "encoding/csv"
    "fmt"
    "github.com/gocolly/colly"
    "github.com/PuerkitoBio/goquery"
    "log"
    "os"
    "time"
)

func main() {
    file, err := os.Create("data.csv")
    if err != nil {
        log.Fatalf("could not create file: %v", err)
    }
    defer file.Close()

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

    c := colly.NewCollector(
        colly.Async(true),
    )

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

    c.OnHTML("body", func(e *colly.HTMLElement) {
        e.DOM.Find("a").Each(func(index int, item *goquery.Selection) {
            link, _ := item.Attr("href")
            text := item.Text()
            fmt.Printf("Link #%d: %s (%s)n", index, text, link)
            writer.Write([]string{text, link})
            c.Visit(e.Request.AbsoluteURL(link))
        })
    })

    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL.String())
    })

    c.OnError(func(r *colly.Response, err error) {
        log.Println("Error:", err)
    })

    c.Visit("http://example.com")

    c.Wait()
}
  • 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

9. 오류 처리 및 재시도 메커니즘

크롤러의 안정성을 향상하려면 요청 오류를 처리하고 재시도 메커니즘을 구현해야 합니다.

package main

import (
    "fmt"
    "github.com/gocolly/colly"
    "github.com/PuerkitoBio/goquery"
    "log"
    "os"
    "time"
)

func main() {
    file, err := os.Create("data.csv")
    if err != nil {
        log.Fatalf("could not create file: %v", err)
    }
    defer file.Close()

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

    c := colly.NewCollector(
        colly.Async(true),
        colly.MaxDepth(1),
    )

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

    c.OnHTML("body", func(e *colly.HTMLElement) {
        e.DOM.Find("a").Each(func(index int, item *goquery.Selection) {
            link, _ := item.Attr("href")
            text := item.Text()
            fmt.Printf("Link #%d: %s (%s)

n", index, text, link)
            writer.Write([]string{text, link})
            c.Visit(e.Request.AbsoluteURL(link))
        })
    })

    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL.String())
    })

    c.OnError(func(r *colly.Response, err error) {
        log.Println("Error:", err)
        // 重试机制
        if r.StatusCode == 0 || r.StatusCode >= 500 {
            r.Request.Retry()
        }
    })

    c.Visit("http://example.com")

    c.Wait()
}
  • 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

10. 실제 사례: 뉴스 웹사이트 크롤링

다음 예에서는 뉴스 웹사이트의 제목과 링크를 스크랩하여 CSV 파일에 저장하는 방법을 보여줍니다.

package main

import (
    "encoding/csv"
    "fmt"
    "github.com/gocolly/colly"
    "log"
    "os"
    "time"
)

func main() {
    file, err := os.Create("news.csv")
    if err != nil {
        log.Fatalf("could not create file: %v", err)
    }
    defer file.Close()

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

    writer.Write([]string{"Title", "Link"})

    c := colly.NewCollector(
        colly.Async(true),
    )

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

    c.OnHTML(".news-title", func(e *colly.HTMLElement) {
        title := e.Text
        link := e.ChildAttr("a", "href")
        writer.Write([]string{title, e.Request.AbsoluteURL(link)})
        fmt.Printf("Title: %snLink: %sn", title, e.Request.AbsoluteURL(link))
    })

    c.OnRequest(func(r *colly.Request) {
        fmt.Println("Visiting", r.URL.String())
    })

    c.OnError(func(r *colly.Response, err error) {
        log.Println("Error:", err)
        if r.StatusCode == 0 || r.StatusCode >= 500 {
            r.Request.Retry()
        }
    })

    c.Visit("http://example-news-site.com")

    c.Wait()
}
  • 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

11. 고급 기능 및 최적화

프록시 사용

대상 웹사이트의 차단을 방지하려면 프록시를 사용할 수 있습니다.

c.SetProxy("http://proxyserver:port")
  • 1

사용자 에이전트 가장

사용자 에이전트를 설정하여 다른 브라우저인 것처럼 가장합니다.

c.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
  • 1

분산 크롤러

Colly의 확장 라이브러리 Colly-Redis를 사용하여 분산 크롤러를 구현할 수 있습니다.

import (
    "github.com/gocolly/redisstorage"
)

func main() {
    c := colly.NewCollector()
    redisStorage := &redisstorage.Storage{
        Address:  "localhost:6379",
        Password: "",
        DB:       0,
        Prefix:   "colly",
    }
    c.SetStorage(redisStorage)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

동적 웹 스크래핑

동적 웹페이지의 경우 chromedp와 같은 헤드리스 브라우저를 사용할 수 있습니다.

import (
    "context"
    "github.com/chromedp/chromedp"
)

func main() {
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()

    var res string
    err := chromedp.Run(ctx,
        chromedp.Navigate("http://example.com"),
        chromedp.WaitVisible(`#some-element`),
        chromedp.InnerHTML(`#some-element`, &res),
    )

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(res)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

12. 결론

이 기사의 자세한 소개를 통해 Go 1.19를 사용하여 단순화된 자동화된 사이트 템플릿 크롤링 도구를 구현하는 방법을 배웠습니다. 기본적인 크롤러 설계 프로세스부터 시작하여 HTML 구문 분석, 동시 처리, 데이터 저장 및 오류 처리와 같은 주요 측면을 점차적으로 살펴보고 특정 코드 예제를 통해 웹 페이지 데이터를 크롤링하고 처리하는 방법을 시연했습니다.

Go 언어의 강력한 동시성 처리 기능과 풍부한 타사 라이브러리는 효율적이고 안정적인 웹 크롤러를 구축하는 데 이상적인 선택입니다. 지속적인 최적화와 확장을 통해 더욱 복잡하고 발전된 크롤러 기능을 구현하여 다양한 데이터 수집 요구에 맞는 솔루션을 제공할 수 있습니다.

이 기사가 Go 언어로 웹 크롤러를 구현하는 데 유용한 참고 자료를 제공하고 이 분야에서 더 많은 탐색과 혁신을 수행하도록 영감을 줄 수 있기를 바랍니다.