Go語言結(jié)合正則表達(dá)式實(shí)現(xiàn)高效獲取數(shù)據(jù)
Go語言結(jié)合正則表達(dá)式可以構(gòu)建高效的數(shù)據(jù)爬取工具。下面我將提供幾個(gè)完整的實(shí)例,涵蓋不同場(chǎng)景下的數(shù)據(jù)爬取需求。
基礎(chǔ)網(wǎng)頁(yè)內(nèi)容爬取
1.1 獲取網(wǎng)頁(yè)中所有鏈接
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
)
func main() {
// 發(fā)送HTTP請(qǐng)求
resp, err := http.Get("https://example.com")
if err != nil {
fmt.Println("HTTP請(qǐng)求失敗:", err)
return
}
defer resp.Body.Close()
// 讀取響應(yīng)內(nèi)容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("讀取響應(yīng)失敗:", err)
return
}
// 編譯正則表達(dá)式,匹配所有a標(biāo)簽的href屬性
re := regexp.MustCompile(`<a[^>]+href=["'](.*?)["']`)
matches := re.FindAllStringSubmatch(string(body), -1)
// 輸出所有鏈接
fmt.Println("找到的鏈接:")
for _, match := range matches {
if len(match) > 1 {
fmt.Println(match[1])
}
}
}
1.2 提取特定模式的文本
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
)
func main() {
resp, err := http.Get("https://example.com")
if err != nil {
fmt.Println("HTTP請(qǐng)求失敗:", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// 匹配所有<h1>-<h6>標(biāo)簽內(nèi)容
re := regexp.MustCompile(`<h[1-6][^>]*>(.*?)</h[1-6]>`)
titles := re.FindAllStringSubmatch(string(body), -1)
fmt.Println("網(wǎng)頁(yè)標(biāo)題:")
for _, title := range titles {
if len(title) > 1 {
// 去除HTML標(biāo)簽
cleanTitle := regexp.MustCompile(`<[^>]+>`).ReplaceAllString(title[1], "")
fmt.Println(cleanTitle)
}
}
}
結(jié)構(gòu)化數(shù)據(jù)爬取
2.1 爬取表格數(shù)據(jù)
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"
)
func main() {
resp, err := http.Get("https://example.com/table-page")
if err != nil {
fmt.Println("HTTP請(qǐng)求失敗:", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
content := string(body)
// 匹配整個(gè)表格
tableRe := regexp.MustCompile(`<table[^>]*>(.*?)</table>`)
tableMatch := tableRe.FindStringSubmatch(content)
if len(tableMatch) == 0 {
fmt.Println("未找到表格")
return
}
tableContent := tableMatch[1]
// 匹配表格行
rowRe := regexp.MustCompile(`<tr[^>]*>(.*?)</tr>`)
rows := rowRe.FindAllStringSubmatch(tableContent, -1)
// 匹配單元格
cellRe := regexp.MustCompile(`<t[dh][^>]*>(.*?)</t[dh]>`)
fmt.Println("表格數(shù)據(jù):")
for _, row := range rows {
cells := cellRe.FindAllStringSubmatch(row[1], -1)
for _, cell := range cells {
if len(cell) > 1 {
// 清理單元格內(nèi)容
cleanCell := strings.TrimSpace(regexp.MustCompile(`<[^>]+>`).ReplaceAllString(cell[1], ""))
fmt.Printf("%s\t", cleanCell)
}
}
fmt.Println() // 換行
}
}
2.2 爬取JSON數(shù)據(jù)中的特定字段
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"regexp"
)
type Product struct {
Name string `json:"name"`
Price float64 `json:"price"`
}
func main() {
resp, err := http.Get("https://api.example.com/products")
if err != nil {
fmt.Println("HTTP請(qǐng)求失敗:", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// 方法1:直接解析JSON
var products []Product
if err := json.Unmarshal(body, &products); err == nil {
fmt.Println("產(chǎn)品列表(JSON解析):")
for _, p := range products {
fmt.Printf("%s - $%.2f\n", p.Name, p.Price)
}
return
}
// 方法2:當(dāng)JSON結(jié)構(gòu)不確定時(shí)使用正則
fmt.Println("\n嘗試使用正則表達(dá)式提取:")
// 匹配產(chǎn)品名稱和價(jià)格
re := regexp.MustCompile(`"name"\s*:\s*"([^"]+)"[^}]+"price"\s*:\s*(\d+\.?\d*)`)
matches := re.FindAllStringSubmatch(string(body), -1)
for _, match := range matches {
if len(match) >= 3 {
fmt.Printf("%s - $%s\n", match[1], match[2])
}
}
}
高級(jí)爬蟲技巧
3.1 帶并發(fā)控制的爬蟲
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"sync"
)
func main() {
urls := []string{
"https://example.com/page1",
"https://example.com/page2",
"https://example.com/page3",
}
var wg sync.WaitGroup
semaphore := make(chan struct{}, 3) // 并發(fā)限制為3
titleRe := regexp.MustCompile(`<title[^>]*>(.*?)</title>`)
for _, url := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
semaphore <- struct{}{} // 獲取信號(hào)量
resp, err := http.Get(u)
if err != nil {
fmt.Printf("獲取 %s 失敗: %v\n", u, err)
<-semaphore
return
}
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
title := titleRe.FindStringSubmatch(string(body))
if len(title) > 1 {
fmt.Printf("%s 的標(biāo)題: %s\n", u, title[1])
}
<-semaphore // 釋放信號(hào)量
}(url)
}
wg.Wait()
}
3.2 處理分頁(yè)內(nèi)容
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strconv"
)
func main() {
baseURL := "https://example.com/news?page="
pageRe := regexp.MustCompile(`<div class="news-item">(.*?)</div>`)
titleRe := regexp.MustCompile(`<h2>(.*?)</h2>`)
pageNumRe := regexp.MustCompile(`page=(\d+)`)
// 先獲取總頁(yè)數(shù)
totalPages := getTotalPages(baseURL + "1")
fmt.Printf("共發(fā)現(xiàn) %d 頁(yè)內(nèi)容\n", totalPages)
// 爬取每頁(yè)內(nèi)容
for page := 1; page <= totalPages; page++ {
url := baseURL + strconv.Itoa(page)
fmt.Printf("\n正在爬取第 %d 頁(yè): %s\n", page, url)
resp, err := http.Get(url)
if err != nil {
fmt.Printf("獲取第 %d 頁(yè)失敗: %v\n", page, err)
continue
}
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
newsItems := pageRe.FindAllStringSubmatch(string(body), -1)
for _, item := range newsItems {
if len(item) > 1 {
title := titleRe.FindStringSubmatch(item[1])
if len(title) > 1 {
fmt.Println("新聞標(biāo)題:", title[1])
}
}
}
}
}
func getTotalPages(url string) int {
resp, err := http.Get(url)
if err != nil {
return 1 // 默認(rèn)1頁(yè)
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
// 假設(shè)頁(yè)面中有類似 "共 5 頁(yè)" 的文字
re := regexp.MustCompile(`共\s*(\d+)\s*頁(yè)`)
match := re.FindStringSubmatch(string(body))
if len(match) > 1 {
total, _ := strconv.Atoi(match[1])
return total
}
return 1
}
實(shí)用技巧與注意事項(xiàng)
1.User-Agent設(shè)置:
client := &http.Client{}
req, _ := http.NewRequest("GET", "https://example.com", nil)
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; MyBot/1.0)")
resp, _ := client.Do(req)
2.處理相對(duì)鏈接:
import "net/url"
base, _ := url.Parse("https://example.com")
rel, _ := url.Parse("/page1")
absURL := base.ResolveReference(rel).String()
3.正則表達(dá)式優(yōu)化:
預(yù)編譯正則表達(dá)式:re := regexp.MustCompile(pattern)
使用非貪婪匹配:.*?
避免過度復(fù)雜的正則表達(dá)式
4.錯(cuò)誤處理增強(qiáng):
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("請(qǐng)求失敗: %w", err)
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Printf("關(guān)閉響應(yīng)體失敗: %v", err)
}
}()
反爬蟲策略應(yīng)對(duì)
設(shè)置合理的請(qǐng)求間隔:
import "time"
func crawlWithDelay(urls []string, delay time.Duration) {
for _, url := range urls {
go crawlPage(url)
time.Sleep(delay)
}
}
使用代理IP:
proxyUrl, _ := url.Parse("http://proxy-ip:port")
client := &http.Client{
Transport: &http.Transport{
Proxy: http.ProxyURL(proxyUrl),
},
}
resp, _ := client.Get("https://example.com")
處理Cookies:
jar, _ := cookiejar.New(nil)
client := &http.Client{Jar: jar}
// 第一次請(qǐng)求獲取cookie
client.Get("https://example.com/login")
// 后續(xù)請(qǐng)求會(huì)攜帶cookie
client.Get("https://example.com/protected-page")
總結(jié)
以上實(shí)例展示了Go語言結(jié)合正則表達(dá)式進(jìn)行數(shù)據(jù)爬取的多種方法:
- 基礎(chǔ)網(wǎng)頁(yè)爬取:獲取鏈接、提取特定內(nèi)容
- 結(jié)構(gòu)化數(shù)據(jù)提?。罕砀駭?shù)據(jù)、JSON數(shù)據(jù)
- 高級(jí)技巧:并發(fā)控制、分頁(yè)處理
- 實(shí)用技巧:User-Agent設(shè)置、相對(duì)鏈接處理
- 反爬應(yīng)對(duì):請(qǐng)求間隔、代理IP、Cookies處理
在實(shí)際項(xiàng)目中,建議:
- 對(duì)于結(jié)構(gòu)化數(shù)據(jù)優(yōu)先使用API而非HTML解析
- 復(fù)雜的HTML解析考慮使用goquery等專門庫(kù)
- 遵守網(wǎng)站的robots.txt規(guī)則
- 設(shè)置合理的爬取頻率,避免對(duì)目標(biāo)網(wǎng)站造成負(fù)擔(dān)
這些實(shí)例可以作為基礎(chǔ)模板,根據(jù)具體需求進(jìn)行調(diào)整和擴(kuò)展。
到此這篇關(guān)于Go語言結(jié)合正則表達(dá)式實(shí)現(xiàn)高效獲取數(shù)據(jù)的文章就介紹到這了,更多相關(guān)Go獲取數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Go 語言入門學(xué)習(xí)之正則表達(dá)式
- Golang棧結(jié)構(gòu)和后綴表達(dá)式實(shí)現(xiàn)計(jì)算器示例
- 一文帶你全面掌握Go語言中的正則表達(dá)式
- Go語句與表達(dá)式案例手冊(cè)深度解析
- 在?Go?語言中使用?regexp?包處理正則表達(dá)式的操作
- Go語言實(shí)戰(zhàn)之詳細(xì)掌握正則表達(dá)式的應(yīng)用與技巧
- Golang中正則表達(dá)式語法及相關(guān)示例
- Go語言利用正則表達(dá)式處理多行文本
- Go中regexp包常見的正則表達(dá)式操作
- Go正則表達(dá)式匹配字符串,替換字符串方式
- Go expr 通用表達(dá)式引擎的使用
相關(guān)文章
Golang中時(shí)間格式化的實(shí)現(xiàn)詳解
這篇文章主要為大家詳細(xì)介紹了Go語言中進(jìn)行時(shí)間進(jìn)行格式化的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-09-09
go-zero熔斷機(jī)制組件Breaker接口定義使用解析
這篇文章主要為大家介紹了go-zero熔斷機(jī)制組件Breaker接口定義使用解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
GoLang中的timer定時(shí)器實(shí)現(xiàn)原理分析
Timer中對(duì)外暴露的只有一個(gè)channel,這個(gè) channel也是定時(shí)器的核心。當(dāng)計(jì)時(shí)結(jié)束時(shí),Timer會(huì)發(fā)送值到channel中,外部環(huán)境在這個(gè) channel 收到值的時(shí)候,就代表計(jì)時(shí)器超時(shí)了,可與select搭配執(zhí)行一些超時(shí)邏輯2023-02-02
Go如何實(shí)現(xiàn)HTTP請(qǐng)求限流示例
本篇文章主要介紹了Go如何實(shí)現(xiàn)HTTP請(qǐng)求限流示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04
初學(xué)Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全
這篇文章主要給大家介紹了關(guān)于初學(xué)vscode寫Go必備的vscode插件及最常用快捷鍵和代碼自動(dòng)補(bǔ)全的相關(guān)資料,由于vscode是開源免費(fèi)的,而且開發(fā)支持vscode的插件相對(duì)比較容易,更新速度也很快,需要的朋友可以參考下2023-07-07
Go語言CSP并發(fā)模型實(shí)現(xiàn)MPG
這篇文章主要為大家介紹了Go語言CSP并發(fā)模型實(shí)現(xiàn)MPG圖文詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05

