如何使用 Golang 2025 从网络上抓取数据

用 go 从网上抓取数据

Web scraping 是一种从网站中提取数据的强大技术,而 Golang (Go) 是完成这项任务的绝佳语言。Go 以其性能和效率而闻名,可以轻松处理网络搜刮。那么,如何使用 Golang 从网页中抓取数据呢?本指南将引导您完成使用 Golang 进行网页刮擦的过程,并介绍相关技术和技巧。

Golang 适合从网络上抓取数据吗?

在了解更多有关使用 Golang 从网络中抓取数据的知识之前,有必要先了解为什么要选择 Golang 进行网络抓取,以及 Golang 有哪些优势。

Golang 因其高性能、高效的并发模型和强大的标准库而成为网络搜刮的首选。Go 能够使用 goroutines 并发处理多个请求,并内置了用于 HTTP 请求和 HTML 解析的包,因此可以高效地抓取大量数据。Go 的简洁性和错误处理能力进一步简化了开发过程,而 Colly 和 Goquery 等第三方库则提供了额外的功能。虽然 Go 在网络搜刮方面的应用不如 Python 普遍,但它的优势使其成为熟悉 Python 语言的人的首选。

使用 Golang 抓取网络数据的基本配置

使用 Go (Golang) 从网页中抓取数据涉及发出 HTTP 请求以检索网页,然后解析 HTML 内容以提取所需信息。以下是使用 Go 从网络中抓取数据的分步指南:

    1. 设置环境

      首先,确保系统已安装 Go。人们也可以从 官方网站.

      golang
    2. 安装必要的软件包

      需要一些软件包来帮助处理 HTTP 请求和 HTML 解析。最常用的软件包是用于 HTTP 请求的 net/http,以及用于解析 HTML 的 goquery。

      运行以下命令获取特定软件包

      go get github.com/PuerkitoBio/goquery

      编写刮刀

      下面是如何使用 Golang 从网站上抓取数据的简单演示:

      主包
      
      导入 (
          "fmt"
          日志
          "net/http"
      
          "github.com/PuerkitoBio/goquery
      )
      
      func main() {
          // 要搜索的网站的 URL
          url := "https://example.com"
      
          // 发送 HTTP GET 请求
          res, err := http.Get(url)
          if err != nil {
              log.Fatal(err)
          }
          defer res.Body.Close()
      
          // 检查响应状态代码
          if res.StatusCode != 200 {
              log.Fatalf("Failed to fetch data: %d %s", res.StatusCode, res.Status)
          }
      
          // 解析 HTML
          doc, err := goquery.NewDocumentFromReader(res.Body)
          if err != nil {
              log.Fatal(err)
          }
      
          // 查找并打印数据
          doc.Find("h1").Each(func(index int, item *goquery.Selection) {
              heading := item.Text()
              fmt.Println(heading)
          })
      }

      提出 HTTP 请求:

      http.Get(url) 向指定的 URL 发送 HTTP GET 请求。
      res.Body.Close() 可确保在读取后关闭响应正文。

      解析 HTML:

      goquery.NewDocumentFromReader(res.Body) 解析 HTML 响应并返回 goquery.Document 对象。

      提取数据:

      doc.Find("h1").Each() 查找 HTML 中的所有 h1 元素并遍历它们。
      item.Text() 提取每个 h1 元素的文本内容。

    3. 运行刮刀

      将上述代码保存到一个文件中,例如,main.go,然后使用:

      go run main.go

其他考虑因素

处理错误:始终妥善处理错误,确保您的刮板不会意外崩溃。

尊重 robots.txt:检查网站的 robots.txt 文件,确保您可以对其进行抓取。

速率限制:实施速率限制,避免服务器被请求淹没。

User-Agent(用户代理):设置自定义的 User-Agent 标头,以识别您的搜刮器,例如

req, err := http.NewRequest("GET", url, nil)
if err != nil {
    log.Fatal(err)
}
req.Header.Set("User-Agent", "Golang_Scraper/1.0")

客户端 := &http.Client{}
res, err := client.Do(req)
if err != nil {
    log.Fatal(err)
}
延迟 res.Body.Close()

// 像以前一样解析 HTML

使用 Golang 抓取网络数据的高级技术

处理分页

许多网站都使用分页来分割多个页面的内容。要抓取所有数据,需要按顺序向每个页面发出请求来处理分页。

下面是一个处理分页的示例:

主包

导入 (
    "fmt"
    日志
    "net/http"
    "strconv"

    "github.com/PuerkitoBio/goquery
)

func main() {
    baseURL := "https://example.com/page/"
    page := 1

    为 {
        url := baseURL + strconv.Itoa(page)
        res, err := http.Get(url)
        if err != nil {
            log.Fatal(err)
        }
        延迟 res.Body.Close()

        if res.StatusCode != 200 {
            log.Println("No more pages to fetch, stopping.")
            断开
        }

        doc, err := goquery.NewDocumentFromReader(res.Body)
        if err != nil {
            log.Fatal(err)
        }

        doc.Find(".item").Each(func(index int, item *goquery.Selection) {
            title := item.Find(".title").Text()
            fmt.Println(title)
        })

        页码
    }
}

处理 JavaScript 渲染的内容

有些网站使用 JavaScript 来动态呈现内容。Go 没有执行 JavaScript 的内置方法,但您可以使用 无头浏览器 像 Chromedp.

go get -u github.com/chromedp/chromedp

使用 Chromedp 抓取 JavaScript 渲染内容的示例:

主包

导入 (
    "上下文"
    "fmt"
    "日志"

    "github.com/chromedp/chromedp"
)

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

    字符串

    err := chromedp.Run(ctx、
        chromedp.Navigate("https://example.com")、
        chromedp.OuterHTML("body", &htmlContent)、
    )
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(htmlContent)
}

管理会话和 Cookie

如果网站需要登录或会话管理,可以使用 http.CookieJar 来处理 cookie 和会话。

管理 cookie 的示例:

主包

导入 (
    "fmt"
    日志
    "net/http"
    "net/http/cookiejar"

    "github.com/PuerkitoBio/goquery
)

func main() {
    jar, _ := cookiejar.New(nil)
    客户端 := &http.Client{Jar: jar}

    // 登录并保存 cookie
    loginURL := "https://example.com/login"
    loginForm := url.Values{}
    loginForm.Set("username", "your_username")
    loginForm.Set("password", "your_password")

    res, err := client.PostForm(loginURL, loginForm)
    if err != nil {
        log.Fatal(err)
    }
    res.Body.Close()

    // 访问受保护页面
    url := "https://example.com/protected-page"
    res, err = client.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    推迟 res.Body.Close()

    doc, err := goquery.NewDocumentFromReader(res.Body)
    if err != nil {
        log.Fatal(err)
    }

    doc.Find(".protected-content").Each(func(index int, item *goquery.Selection) {
        content := item.Text()
        fmt.Println(content)
    })
}

节流和速率限制

为避免被网站屏蔽,可通过在请求之间引入延迟来实施速率限制。

速率限制示例

主包

导入 (
    "fmt"
    日志
    "net/http"
    "时间"

    "github.com/PuerkitoBio/goquery
)

func main() {
    urls := []string{"https://example.com/page1", "https://example.com/page2"}

    for _, url := range urls {
        res, err := http.Get(url)
        if err != nil {
            log.Fatal(err)
        }
        延迟 res.Body.Close()

        doc, err := goquery.NewDocumentFromReader(res.Body)
        if err != nil {
            log.Fatal(err)
        }

        doc.Find(".item").Each(func(index int, item *goquery.Selection) {
            title := item.Find(".title").Text()
            fmt.Println(title)
        })

        // 延迟以避免阻塞
        time.Sleep(2 * time.Second)
    }
}

处理 AJAX 请求

有些网站通过 AJAX 请求动态加载数据。您可以使用浏览器开发工具等工具捕获并复制这些请求,以找到 API 端点。

从 AJAX API 端点获取数据的示例:

主包

导入 (
    "编码/json"
    "fmt"
    日志
    "net/http"
)

类型 项目 结构 {
    Title string `json: "title"`
}

func main() {
    url := "https://example.com/api/items"

    res, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    推迟 res.Body.Close()

    var items []Item
    if err := json.NewDecoder(res.Body).Decode(&items); err != nil {
        log.Fatal(err)
    }

    for _, item := range items {
        fmt.Println(item.Title)
    }
}

处理验证码和反捕获机制

网站经常使用验证码和其他反搜索机制。虽然以编程方式解决验证码问题非常复杂,而且往往违反服务条款,但您可以使用轮换用户代理和代理等技术来避免检测。

旋转用户代理示例

主包

导入 (
    "fmt"
    日志
    "net/http"
    "math/rand"
    "时间"
)

func main() {
    userAgents := []string{
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"、
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:54.0) Gecko/20100101 Firefox/54.0"、
        // 在此添加更多用户代理
    }

    客户端 := &http.Client{}
    rand.Seed(time.Now().UnixNano())

    for i := 0; i < 5; i++ {
        req, err := http.NewRequest("GET", "https://example.com", nil)
        if err != nil {
            log.Fatal(err)
        }

        req.Header.Set("User-Agent", userAgents[rand.Intn(len(userAgents))])
        res, err := client.Do(req)
        if err != nil {
            log.Fatal(err)
        }
        res.Body.Close()

        fmt.Println("Request sent with user-agent:", req.Header.Get("User-Agent"))
    }
}

使用代理

为了进一步保护您的 IP 不被封禁,您可以使用代理服务器。OkeyProxy 或 MacroProxy 等服务可提供代理解决方案。

作为最好的代理供应商之一、 OkeyProxy 由 HTTP/HTTPS/SOCKS 支持,并提供 1.5 亿多个真实住宅 IP,覆盖 200 多个国家/地区,可以 避免 IP 禁止 尽可能确保网络连接的安全性、可靠性和稳定性。

okeyproxy

使用 代理数据搜刮 使用 http.Client.Net:

主包

导入 (
    "fmt"
    日志
    "net/http"
    "net/url"
)

func main() {
    proxyURL, _ := url.Parse("http://proxyusername:proxypassword@proxyserver:port")
    Transport := &http.Transport{
        代理:http.ProxyURL(proxyURL)、
    }

    客户端 := &http.Client{Transport: transport}

    res, err := client.Get("https://example.com")
    if err != nil {
        log.Fatal(err)
    }
    推迟 res.Body.Close()

    fmt.Println("Response status:", res.Status)
}

并发扫描

为了加快刮擦速度,可以使用 goroutines 同时处理多个请求。这对搜索大型数据集非常有用。

使用 goroutines 进行并发刮擦的示例:

主包

导入 (
    "fmt"
    日志
    "net/http"
    "同步"

    "github.com/PuerkitoBio/goquery
)

func scrape(url string, wg *sync.WaitGroup) {
    延迟 wg.Done()

    res, err := http.Get(url)
    if err != nil {
        log.Println(err)
        返回
    }
    推迟 res.Body.Close()

    doc, err := goquery.NewDocumentFromReader(res.Body)
    if err != nil {
        log.Println(err)
        返回
    }

    doc.Find(".item").Each(func(index int, item *goquery.Selection) {
        title := item.Find(".title").Text()
        fmt.Println(title)
    })
}

func main() {
    urls := []string{
        "https://example.com/page1"、
        "https://example.com/page2"、
        // 添加更多 URL
    }

    var wg sync.WaitGroup

    for _, url := range urls {
        wg.Add(1)
        go scrape(url, &wg)
    }

    wg.Wait()
}

从应用程序接口抓取数据

许多网站都提供 API 来访问数据。使用 API 通常比刮擦 HTML 更简单、更高效。

调用 API 的示例:

主包

导入 (
    "编码/json"
    "fmt"
    日志
    "net/http"
)

func main() {
    url := "https://api.example.com/data"

    res, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    推迟 res.Body.Close()

    var data map[string]interface{}
    if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
        log.Fatal(err)
    }

    fmt.Println("API Data:", data)
}

存储数据

根据你的要求,你可能需要将搜刮到的数据存储到数据库或文件中。下面是一个将数据写入 CSV 文件的示例:

主包

导入 (
    "编码/csv"
    "fmt"
    日志
    "操作系统"
    "net/http"
    "github.com/PuerkitoBio/goquery
)

func main() {
    file, err := os.Create("data.csv")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

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

    urls := []string{"https://example.com/page1", "https://example.com/page2"}

    for _, url := range urls {
        res, err := http.Get(url)
        if err != nil {
            log.Fatal(err)
        }
        延迟 res.Body.Close()

        doc, err := goquery.NewDocumentFromReader(res.Body)
        if err != nil {
            log.Fatal(err)
        }

        doc.Find(".item").Each(func(index int, item *goquery.Selection) {
            title := item.Find(".title").Text()
            writer.Write([]string{title})
        })
    }

    fmt.Println("Data written to data.csv")
}

错误处理和日志记录

强大的错误处理和日志记录对于排除故障和维护刮擦程序至关重要。您可以使用 Go 的日志功能或 logrus 等外部库进行高级日志记录。

使用 Golang 进行网络抓取的基本库

  1. 科利安装:go get -u github.com/gocolly/colly
  2. 查询安装:go get -u github.com/PuerkitoBio/goquery
  3. 要求安装: go get -u github.com/imroc/req
  4. 征集高级 HTTP 请求库,类似于 Python 的 Requests。安装:go get -u github.com/levigross/grequests
  5. 铬化p安装:go get -u github.com/chromedp/chromedp
  6. 罗德安装: go get -u github.com/ysmood/rod
  7. Go-Selenium安装: go get -u github.com/tebeka/selenium
  8. 斯科利安装: go get -u github.com/scolly/scolly
  9. 眉射安装:go get -u github.com/browshot/browshot-go
  10. 傀儡廻安装:go get -u github.com/chromedp/puppeteer-go
  11. 转到请求安装:go get -u github.com/deckarep/golang-set
  12. Httpproxy安装:go get -u github.com/henrylee2cn/httpproxy
  13. 爬行安装:go get -u github.com/whyrusleeping/crawling
  14. K6安装:go get -u github.com/loadimpact/k6
  15. Net/http:Go 中进行 HTTP 请求的标准库:Go 内置,无需单独安装。
  16. Goquery-html安装: go get -u github.com/PuerkitoBio/goquery-html
  17. Httpclient安装: go get -u github.com/aymerick/raymond

这些库和工具涵盖一系列功能,从简单的 HTTP 请求到完全的浏览器自动化,使它们成为满足不同网络刮擦需求的多功能工具。

摘要

使用 Golang 从网络上抓取数据有几个优点,包括性能效率高和易于并发。Go 的轻量级 goroutines 和通道能以最小的资源开销同时处理多个请求,因此非常适合大规模数据提取任务。此外,Go 强大的标准库支持强大的 HTTP 和 HTML 解析功能,简化了高效、可靠的网络抓取应用程序的开发。速度、并发性和内置工具的完美结合,使 Golang 成为需要高性能和可扩展性的网络抓取项目的不二之选。

评论

还没有评论。为什么不开始讨论?

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注