Go語言爬蟲系列教程4:使用正則表達式解析HTML內容

Go語言爬蟲系列教程4:使用正則表達式解析HTML內容

正則表達式(Regular Expression,簡稱RegEx)是處理文本數據的利器。在網絡爬蟲中,我們經常需要從HTML頁面中提取特定的信息,正則表達式就像一個智能的"文本篩子",能夠精確地找到我們需要的內容。

想象你在一本厚厚的電話簿中找所有以"138"開頭的手機號碼。如果用人工查找會很累,但如果有一個魔法篩子,你告訴它"找所有138開頭的11位數字",它就能快速找出所有符合條件的號碼。正則表達式就是這樣的"魔法篩子"。

一、簡單演示

package mainimport ("fmt""regexp"
)// 演示基礎正則表達式語法
func main() {// 1\. 字面量匹配(最簡單的情況)text1 := "Hello World"pattern1 := "Hello"matched, _ := regexp.MatchString(pattern1, text1)fmt.Printf("文本: %s\n模式: %s\n匹配結果: %t\n\n", text1, pattern1, matched)

輸出結果:

文本: Hello World
模式: Hello
匹配結果: true

這個代碼演示的是最簡單的字面量匹配

二、基礎語法

正則表達式由兩種基本元素組成:

  • 字面量:直接匹配文本中的字符
  • 元字符:具有特殊含義的字符,用于描述模式

2.1 字面量匹配 - 最簡單的精確匹配

    text1 := "Hello World"pattern1 := "Hello"matched, _ := regexp.MatchString(pattern1, text1)fmt.Printf("文本: %s\n模式: %s\n匹配結果: %t\n\n", text1, pattern1, matched)

運行結果

文本: Hello World
模式: Hello
匹配結果: true

作用:直接匹配指定的字符串,不使用任何特殊符號

2.2 點號(.) - 萬能字符匹配器

    text2 := "cat bat hat"pattern2 := ".at"  // 匹配任意字符+atre2 := regexp.MustCompile(pattern2)matches2 := re2.FindAllString(text2, -1)fmt.Printf("文本: %s\n模式: %s (點號匹配任意字符)\n匹配結果: %v\n\n", text2, pattern2, matches2)

運行結果

文本: cat bat hat
模式: .at (點號匹配任意字符)
匹配結果: [cat bat hat]

作用:點號.是一個特殊字符,可以匹配除換行符外的任意單個字符

  • .at匹配"任意字符+at"的模式
  • 在文本中找到了:cat(c+at)、bat(b+at)、hat(h+at)
  • 所有三個單詞都符合模式

2.3 星號(*) - 貪婪的重復匹配

    text3 := "goooooogle"pattern3 := "go*gle"  // g + 0個或多個o + glematched3, _ := regexp.MatchString(pattern3, text3)fmt.Printf("文本: %s\n模式: %s (星號表示0次或多次)\n匹配結果: %t\n\n", text3, pattern3, matched3)

運行結果

文本: goooooogle
模式: go*gle (星號表示0次或多次)
匹配結果: true

作用:星號*表示前面的字符可以出現0次或多次

  • go*gle意思是:g + (0個或多個o) + gle
  • "goooooogle"中有g + 5個o + gle,符合模式
  • 這個模式也能匹配"ggle"(0個o)、“google”(2個o)等

2.4 加號(+) - 至少一次的重復匹配

    text4 := "ggle google gooogle"pattern4 := "go+gle"  // g + 1個或多個o + glere4 := regexp.MustCompile(pattern4)matches4 := re4.FindAllString(text4, -1)fmt.Printf("文本: %s\n模式: %s (加號表示1次或多次)\n匹配結果: %v\n\n", text4, pattern4, matches4)

運行結果

文本: ggle google gooogle
模式: go+gle (加號表示1次或多次)
匹配結果: [google gooogle]

作用:加號+表示前面的字符必須出現1次或多次

  • go+gle意思是:g + (1個或多個o) + gle
  • "ggle"不匹配(沒有o)
  • "google"匹配(2個o)
  • "gooogle"匹配(3個o)

2.5 問號(?) - 可選字符匹配

    text5 := "color colour"pattern5 := "colou?r"  // colou + 0個或1個rre5 := regexp.MustCompile(pattern5)matches5 := re5.FindAllString(text5, -1)fmt.Printf("文本: %s\n模式: %s (問號表示0次或1次)\n匹配結果: %v\n\n", text5, pattern5, matches5)

運行結果

文本: color colour
模式: colou?r (問號表示0次或1次)
匹配結果: [color colour]

作用:問號?表示前面的字符可以出現0次或1次(可選)

  • colou?r意思是:colou + (0個或1個u) + r
  • "color"匹配(0個u)
  • "colour"匹配(1個u)

2.6 方括號[] - 字符集合匹配

text1 := "cat bat rat mat"
pattern1 := "[cbr]at"  // 匹配cat、bat或rat

運行結果

文本: cat bat rat mat
模式: [cbr]at ([cbr]匹配c、b或r中的一個)
匹配結果: [cat bat rat]

作用:方括號[]定義一個字符集合,匹配其中任意一個字符

  • [cbr]at表示:(c或b或r) + at
  • 匹配到:catbatrat
  • 不匹配:mat(因為m不在[cbr]中)

2.7 范圍表示法 - 連續字符的簡寫

text2 := "a1 b2 c3 d4"
pattern2 := "[a-c][1-3]"  // 匹配a-c中的一個字母 + 1-3中的一個數字

運行結果

文本: a1 b2 c3 d4
模式: [a-c][1-3] ([a-c]匹配a到c,[1-3]匹配1到3)
匹配結果: [a1 b2 c3]

作用:用連字符-表示字符范圍,避免逐個列舉

  • [a-c]等同于[abc]
  • [1-3]等同于[123]
  • 匹配到:a1b2c3
  • 不匹配:d4(d不在a-c范圍,4不在1-3范圍)

常用范圍

  • [a-z]:小寫字母
  • [A-Z]:大寫字母
  • [0-9]:數字
  • [a-zA-Z0-9]:字母和數字

2.8 否定字符類[^] - 排除匹配

text3 := "cat bat rat mat"
pattern3 := "[^m]at"  // 匹配不是m開頭的at

運行結果

文本: cat bat rat mat
模式: [^m]at ([^m]匹配除了m以外的字符)
匹配結果: [cat bat rat]

作用^在方括號內表示否定,匹配除了指定字符外的任意字符

  • [^m]at表示:(除了m以外的任意字符) + at
  • 匹配到:catbatrat
  • 不匹配:mat(因為開頭是m)

2.9 預定義字符類 - 常用模式的簡寫

text4 := "abc 123 XYZ !@#"
patterns := map[string]string{`\d`:  "匹配數字 [0-9]",`\w`:  "匹配單詞字符 [a-zA-Z0-9_]",`\s`:  "匹配空白字符",`\D`:  "匹配非數字字符",`\W`:  "匹配非單詞字符",`\S`:  "匹配非空白字符",
}

2.10 精確量詞{n} - 確切次數匹配

text1 := "1 12 123 1234"
pattern1 := `\d{3}`  // 匹配恰好3個數字

運行結果

文本: 1 12 123 1234
模式: \d{3} (恰好3個數字)
匹配結果: [123 123]

作用{n}表示前面的字符或模式必須出現恰好n次

  • \d{3}匹配恰好3個連續數字
  • 在"1234"中找到"123"(前3位)
  • 注意:會優先匹配較長的數字串中的前3位

2.11 范圍量詞{n,m} - 區間次數匹配

text2 := "1 12 123 1234 12345"
pattern2 := `\d{2,4}`  // 匹配2到4個數字

運行結果

文本: 1 12 123 1234 12345
模式: \d{2,4} (2到4個數字)
匹配結果: [12 123 1234 1234]

作用{n,m}表示前面的字符出現n到m次(包含n和m)

  • 匹配2-4個連續數字
  • "1"不匹配(只有1個數字)
  • “12345"中匹配前4個"1234”(貪婪匹配,取最大值)

2.12 最少量詞{n,} - 至少n次匹配

text3 := "a aa aaa aaaa"
pattern3 := `a{3,}`  // 匹配3個或更多的a

運行結果

文本: a aa aaa aaaa
模式: a{3,} (3個或更多的a)
匹配結果: [aaa aaaa]

作用{n,}表示前面的字符至少出現n次,沒有上限

  • "a"和"aa"不匹配(少于3個)
  • "aaa"和"aaaa"匹配(3個或更多)

2.13 行邊界^和$ - 位置匹配

text4 := "hello world\nworld hello\nhello"// 匹配行開始的hello
pattern4a := `^hello`
// 運行結果: [hello hello]// 匹配行結束的hello  
pattern4b := `hello$`
// 運行結果: [hello hello]

作用

  • ^:匹配行的開始位置^hello匹配每行開頭的"hello"
  • $:匹配行的結束位置hello$匹配每行結尾的"hello"

2.14 單詞邊界\b - 完整單詞匹配

text5 := "cat catch catch22"
pattern5 := `\bcat\b`  // 完整的單詞cat

運行結果

文本: cat catch catch22
模式: \bcat\b (完整單詞cat)
匹配結果: [cat]

作用\b表示單詞邊界,確保匹配完整的單詞

  • \bcat\b只匹配作為獨立單詞的"cat"
  • "catch"中的"cat"不匹配(后面還有字母ch)
  • "catch22"中的"cat"也不匹配

三、regexp包常用方法介紹

3.1 regexp包的核心結構

package mainimport ("fmt""regexp""strings"
)// RegexpDemo 正則表達式演示結構體
type RegexpDemo struct {name    stringpattern stringtext    string
}// 演示regexp包的主要方法
func demonstrateRegexpPackage() {fmt.Println("=== regexp包方法演示 ===")// 1\. 編譯正則表達式的兩種方式fmt.Println("1\. 編譯正則表達式:")// 方式1:Compile - 返回錯誤pattern := `\d+`re1, err := regexp.Compile(pattern)if err != nil {fmt.Printf("編譯錯誤: %v\n", err)return}fmt.Printf("Compile成功: %v\n", re1)// 方式2:MustCompile - 編譯失敗會panic(適合確定正確的模式)re2 := regexp.MustCompile(pattern)fmt.Printf("MustCompile成功: %v\n\n", re2)// 2\. 基本匹配方法text := "我的電話是138-1234-5678,朋友的是139-8765-4321"phonePattern := `1[3-9]\d-\d{4}-\d{4}`phoneRe := regexp.MustCompile(phonePattern)// 2.1 MatchString - 檢查是否匹配fmt.Println("2\. 基本匹配方法:")matched := phoneRe.MatchString(text)fmt.Printf("MatchString: %t\n", matched)// 2.2 FindString - 找到第一個匹配firstMatch := phoneRe.FindString(text)fmt.Printf("FindString: %s\n", firstMatch)// 2.3 FindAllString - 找到所有匹配allMatches := phoneRe.FindAllString(text, -1)  // -1表示找到所有fmt.Printf("FindAllString: %v\n", allMatches)// 2.4 FindStringIndex - 找到第一個匹配的位置firstIndex := phoneRe.FindStringIndex(text)fmt.Printf("FindStringIndex: %v\n", firstIndex)if firstIndex != nil {fmt.Printf("第一個匹配的內容: %s\n", text[firstIndex[0]:firstIndex[1]])}// 2.5 FindAllStringIndex - 找到所有匹配的位置allIndexes := phoneRe.FindAllStringIndex(text, -1)fmt.Printf("FindAllStringIndex: %v\n\n", allIndexes)
}

3.2 分組和捕獲

// 演示分組和捕獲
func demonstrateGroupsAndCapture() {fmt.Println("=== 分組和捕獲演示 ===")// 1\. 基本分組()text := "張三的郵箱是zhangsan@example.com,李四的郵箱是lisi@gmail.com"// 使用分組捕獲郵箱的用戶名和域名emailPattern := `(\w+)@(\w+\.\w+)`emailRe := regexp.MustCompile(emailPattern)// FindStringSubmatch - 返回第一個匹配及其分組firstSubmatch := emailRe.FindStringSubmatch(text)fmt.Printf("郵箱分組匹配:\n")if len(firstSubmatch) > 0 {fmt.Printf("完整匹配: %s\n", firstSubmatch[0])fmt.Printf("用戶名: %s\n", firstSubmatch[1])fmt.Printf("域名: %s\n", firstSubmatch[2])}fmt.Println()// FindAllStringSubmatch - 返回所有匹配及其分組allSubmatches := emailRe.FindAllStringSubmatch(text, -1)fmt.Printf("所有郵箱分組匹配:\n")for i, submatch := range allSubmatches {fmt.Printf("第%d個郵箱:\n", i+1)fmt.Printf("  完整匹配: %s\n", submatch[0])fmt.Printf("  用戶名: %s\n", submatch[1])fmt.Printf("  域名: %s\n", submatch[2])}fmt.Println()// 2\. 命名分組(?P<name>pattern)urlText := "訪問我們的網站:https://www.example.com:8080/path?query=value"urlPattern := `(?P<protocol>https?)://(?P<domain>[\w.]+):?(?P<port>\d*)/(?P<path>[\w/]*)\??(?P<query>.*)`urlRe := regexp.MustCompile(urlPattern)matches := urlRe.FindStringSubmatch(urlText)names := urlRe.SubexpNames()fmt.Printf("URL命名分組匹配:\n")for i, name := range names {if i != 0 && name != "" && i < len(matches) {fmt.Printf("%s: %s\n", name, matches[i])}}fmt.Println()// 3\. 非捕獲分組(?:pattern) - 分組但不捕獲text3 := "電話: 010-12345678 或 021-87654321"// 使用非捕獲分組匹配區號格式,但只捕獲完整號碼phonePattern2 := `(?:010|021)-(\d{8})`phoneRe2 := regexp.MustCompile(phonePattern2)allPhoneMatches := phoneRe2.FindAllStringSubmatch(text3, -1)fmt.Printf("非捕獲分組示例:\n")for i, match := range allPhoneMatches {fmt.Printf("第%d個匹配:\n", i+1)fmt.Printf("  完整匹配: %s\n", match[0])fmt.Printf("  后8位號碼: %s\n", match[1])}
}

3.3 替換操作

// 演示替換操作
func demonstrateReplacement() {fmt.Println("=== 替換操作演示 ===")// 1\. ReplaceAllString - 簡單替換text1 := "今天是2023年12月25日,明天是2023年12月26日"datePattern := `\d{4}年\d{1,2}月\d{1,2}日`dateRe := regexp.MustCompile(datePattern)replaced1 := dateRe.ReplaceAllString(text1, "[日期]")fmt.Printf("原文: %s\n", text1)fmt.Printf("替換后: %s\n\n", replaced1)// 2\. ReplaceAllStringFunc - 使用函數替換text2 := "蘋果5元,香蕉3元,橙子8元"pricePattern := `(\w+)(\d+)元`priceRe := regexp.MustCompile(pricePattern)replaced2 := priceRe.ReplaceAllStringFunc(text2, func(match string) string {// 可以在這里做復雜的處理return "[商品價格]"})fmt.Printf("原文: %s\n", text2)fmt.Printf("函數替換后: %s\n\n", replaced2)// 3\. 使用分組進行替換text3 := "聯系人:張三,電話:138-1234-5678"contactPattern := `聯系人:(\w+),電話:([\d-]+)`contactRe := regexp.MustCompile(contactPattern)// $1, $2引用捕獲的分組replaced3 := contactRe.ReplaceAllString(text3, "姓名: $1, 手機: $2")fmt.Printf("原文: %s\n", text3)fmt.Printf("分組替換后: %s\n\n", replaced3)// 4\. 復雜的分組替換 - 格式化電話號碼text4 := "電話號碼:13812345678, 15987654321, 18611112222"phonePattern3 := `(\d{3})(\d{4})(\d{4})`phoneRe3 := regexp.MustCompile(phonePattern3)formatted := phoneRe3.ReplaceAllString(text4, "$1-$2-$3")fmt.Printf("原文: %s\n", text4)fmt.Printf("格式化后: %s\n", formatted)
}

3.4 常用方法表

方法名功能描述性能建議
MatchString直接判斷是否匹配,內部自動編譯正則表達式適用于單次匹配,頻繁調用需預編譯
Compile / MustCompile預編譯正則表達式,返回可重復使用的*Regexp對象必須用于高頻匹配場景
FindString / FindAllString查找匹配的子串,不捕獲分組內容簡單查找場景
FindStringSubmatch / FindAllStringSubmatch查找匹配的子串并捕獲分組內容需要提取分組時使用
ReplaceAllString / ReplaceAllStringFunc替換匹配的子串,支持靜態替換或函數式動態替換替換場景通用方法
Split按正則表達式分割字符串替代strings.Split處理復雜分隔符
標志位(?i)(?s)(?m)修改正則表達式匹配規則(不區分大小寫、點號匹配換行、多行模式)復雜文本匹配時使用

四、常見的正則表達式

以下是一些常見的正則表達式示例,用于匹配不同類型的文本。
注意:這些示例是基于常見的文本格式,可能需要根據實際需求進行調整。

1. 匹配郵箱

^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$

2. 匹配手機號(以中國大陸為例)

^1[3-9][0-9]{9}$

3. 匹配身份證號(中國18位)

^\d{6}(18|19|20)?\d{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])\d{3}(\d|X|x)$

4. 匹配URL

^https?://[^\s/$.?#].[^\s]*$

5. 匹配IP地址(IPv4)

^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$

??注意:上面的正則沒有嚴格驗證0-255,可用下面更嚴格的版本:

^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$

6. 匹配數字字符串

^\d+$

7. 匹配僅包含字母(不區分大小寫)

^[a-zA-Z]+$

8. 匹配密碼復雜性(8-20位,包含字母和數字)

^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,20}$

9. 匹配日期(YYYY-MM-DD)

^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$

10. 匹配正整數

^[1-9]\d*$

11. 匹配負整數

^-[1-9]\d*$

12. 匹配浮點數(正負均可)

^-?\d+(\.\d+)?$

13. 匹配由字母、數字和下劃線組成的字符串

^\w+$

14. 匹配中文字符

[\p{Han}]+

需要 go 1.13+ 支持,更通用。

15. 匹配郵政編碼(中國6位)

^\d{6}$

16. 匹配含有 HTML 標簽的文本

<([a-zA-Z][a-zA-Z0-9]*)\b[^>]*>(.*?)</\1>

17. 匹配空白行

^\s*$

18. 匹配 MAC 地址

^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$

19. 匹配單行注釋

^//.*$

20. 匹配多行注釋

(?s)/\*.*?\*/

4厄3

五、實戰演示

package mainimport ("fmt""regexp""strings"
)// Link 鏈接結構體
type Link struct {URL  stringText string
}// String 返回鏈接的字符串表示
func (l Link) String() string {return fmt.Sprintf("URL: %s, 文本: %s", l.URL, l.Text)
}// HTMLDataExtractor HTML數據提取器
type HTMLDataExtractor struct {patterns map[string]*regexp.Regexp
}// NewHTMLDataExtractor 創建HTML數據提取器,帶錯誤處理
func NewHTMLDataExtractor() (*HTMLDataExtractor, error) {extractor := &HTMLDataExtractor{patterns: make(map[string]*regexp.Regexp),}// 編譯正則表達式并處理錯誤if err := extractor.compilePatterns(); err != nil {return nil, fmt.Errorf("編譯正則表達式失敗: %w", err)}return extractor, nil
}// compilePatterns 編譯常用的正則表達式模式,帶錯誤處理
func (e *HTMLDataExtractor) compilePatterns() error {patterns := map[string]string{"title": `<title[^>]*>([^<]+)</title>`,// 優化meta標簽匹配,支持更靈活的屬性順序和引號"meta_desc": `(?i)<meta\s+(?:[^>]*\s+)?name\s*=\s*["']description["'][^>]*\s+content\s*=\s*["']([^"']*)["']`,// 優化鏈接匹配,支持單引號/雙引號和更多屬性格式"links":  `<a[^>]*href\s*=\s*(?:["'])([^"']+)["'][^>]*>([^<]*)</a>`,"images": `<img[^>]*src\s*=\s*(?:["'])([^"']+)["'][^>]*>`,"emails": `[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`,"phones": `1[3-9]\d{9}`,"prices": `¥\s*(\d+(?:\.\d{2})?)`,// 修復:使用十六進制范圍表示中文漢字"chinese_text": `[\x{4e00}-\x{9fa5}]+`,}for name, pattern := range patterns {re, err := regexp.Compile(pattern)if err != nil {return fmt.Errorf("編譯模式 %s 失敗: %w", name, err)}e.patterns[name] = re}return nil
}// ExtractTitle 提取標題
func (e *HTMLDataExtractor) ExtractTitle(html string) string {matches := e.patterns["title"].FindStringSubmatch(html)if len(matches) > 1 {return strings.TrimSpace(matches[1])}return ""
}// ExtractMetaDescription 提取meta描述
func (e *HTMLDataExtractor) ExtractMetaDescription(html string) string {matches := e.patterns["meta_desc"].FindStringSubmatch(html)if len(matches) > 1 {return strings.TrimSpace(matches[1])}return ""
}// ExtractLinks 提取所有鏈接
func (e *HTMLDataExtractor) ExtractLinks(html string) []Link {matches := e.patterns["links"].FindAllStringSubmatch(html, -1)var links []Linkfor _, match := range matches {if len(match) > 2 {links = append(links, Link{URL:  strings.TrimSpace(match[1]),Text: strings.TrimSpace(match[2]),})}}return links
}// ExtractImages 提取所有圖片URL
func (e *HTMLDataExtractor) ExtractImages(html string) []string {matches := e.patterns["images"].FindAllStringSubmatch(html, -1)var images []stringfor _, match := range matches {if len(match) > 1 {images = append(images, strings.TrimSpace(match[1]))}}return images
}// ExtractEmails 提取所有郵箱地址
func (e *HTMLDataExtractor) ExtractEmails(html string) []string {return e.patterns["emails"].FindAllString(html, -1)
}// ExtractPhones 提取所有手機號碼
func (e *HTMLDataExtractor) ExtractPhones(html string) []string {return e.patterns["phones"].FindAllString(html, -1)
}// ExtractPrices 提取所有價格
func (e *HTMLDataExtractor) ExtractPrices(html string) []string {matches := e.patterns["prices"].FindAllStringSubmatch(html, -1)var prices []stringfor _, match := range matches {if len(match) > 1 {prices = append(prices, match[1])}}return prices
}// ExtractChineseText 提取中文文本
func (e *HTMLDataExtractor) ExtractChineseText(html string) []string {return e.patterns["chinese_text"].FindAllString(html, -1)
}// 使用示例
func main() {fmt.Println("=== HTML數據提取演示 ===\n")// 示例HTML內容sampleHTML := `<!DOCTYPE html><html><head><title>購物網站 - 最優質的商品</title><meta NAME="description" content='這是一個專業的購物網站,提供優質商品和服務'></head><body><h1>歡迎來到我們的商店</h1><p>聯系我們:info@example.com 或者 support@shop.com</p><p>客服電話:13812345678,18611112222</p><div class="products"><h2>熱門商品</h2><div class="product"><img src='/images/phone.jpg' alt='手機'><h3>智能手機</h3><p>價格:¥2999.00</p><a href='/product/phone' class='btn'>查看詳情</a></div><div class="product"><img src=/images/laptop.jpg alt=筆記本><h3>筆記本電腦</h3><p>價格:¥5999.99</p><a href='/product/laptop'>立即購買</a></div></div><footer><p>更多信息請訪問 <a href='https://www.example.com'>我們的官網</a></p></footer></body></html>`// 創建提取器并處理錯誤extractor, err := NewHTMLDataExtractor()if err != nil {fmt.Printf("錯誤: %s\n", err)return}// 提取各種數據fmt.Println("📌 頁面標題:")fmt.Printf("  %s\n\n", extractor.ExtractTitle(sampleHTML))fmt.Println("📝 頁面描述:")fmt.Printf("  %s\n\n", extractor.ExtractMetaDescription(sampleHTML))fmt.Println("📧 郵箱地址:")emails := extractor.ExtractEmails(sampleHTML)for i, email := range emails {fmt.Printf("  %d. %s\n", i+1, email)}fmt.Println()fmt.Println("📞 手機號碼:")phones := extractor.ExtractPhones(sampleHTML)for i, phone := range phones {fmt.Printf("  %d. %s\n", i+1, phone)}fmt.Println()fmt.Println("🔗 鏈接:")links := extractor.ExtractLinks(sampleHTML)for i, link := range links {fmt.Printf("  %d. %s\n", i+1, link)}fmt.Println()fmt.Println("🖼? 圖片:")images := extractor.ExtractImages(sampleHTML)for i, img := range images {fmt.Printf("  %d. %s\n", i+1, img)}fmt.Println()fmt.Println("💰 價格:")prices := extractor.ExtractPrices(sampleHTML)for i, price := range prices {fmt.Printf("  %d. ¥%s\n", i+1, price)}fmt.Println()fmt.Println("中文文本片段:")chineseTexts := extractor.ExtractChineseText(sampleHTML)for i, text := range chineseTexts {if len(text) > 2 { // 過濾短文本fmt.Printf("  %d. %s\n", i+1, text)}}
}

記住:正則表達式是一把雙刃劍,用好了事半功倍,用錯了可能帶來性能問題。關鍵是要在合適的場景使用合適的工具!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/82526.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/82526.shtml
英文地址,請注明出處:http://en.pswp.cn/web/82526.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

筆記 | docker構建失敗

筆記 | docker構建失敗 構建報錯LOG1 rootThinkPad-FLY:/mnt/e/02-docker/ubunutu-vm# docker build -t ubuntu16.04:v1 . [] Building 714.5s (6/11) docker:default> [internal] load …

CentOS 7.9 安裝 寶塔面板

在 CentOS 7.9 上安裝 寶塔面板&#xff08;BT Panel&#xff09; 的完整步驟如下&#xff1a; 1. 準備工作 系統要求&#xff1a; CentOS 7.x&#xff08;推薦 7.9&#xff09;內存 ≥ 1GB&#xff08;建議 2GB&#xff09;硬盤 ≥ 20GBroot 權限&#xff08;需使用 root 用戶…

第 86 場周賽:矩陣中的幻方、鑰匙和房間、將數組拆分成斐波那契序列、猜猜這個單詞

Q1、[中等] 矩陣中的幻方 1、題目描述 3 x 3 的幻方是一個填充有 從 1 到 9 的不同數字的 3 x 3 矩陣&#xff0c;其中每行&#xff0c;每列以及兩條對角線上的各數之和都相等。 給定一個由整數組成的row x col 的 grid&#xff0c;其中有多少個 3 3 的 “幻方” 子矩陣&am…

【AI News | 20250604】每日AI進展

AI Repos 1、jaaz Jaaz是一款免費開源的AI設計代理&#xff0c;作為Lovart的本地替代品&#xff0c;它能實現圖像、海報、故事板的設計、編輯和生成。Jaaz集成了LLM&#xff0c;可智能生成提示并批量生成圖像&#xff0c;支持Ollama、Stable Diffusion等本地及API模型。用戶可…

Docker load 后鏡像名稱為空問題的解決方案

在使用 docker load命令從存檔文件中加載Docker鏡像時&#xff0c;有時會遇到鏡像名稱為空的情況。這種情況通常是由于在保存鏡像時未正確標記鏡像名稱和標簽&#xff0c;或者在加載鏡像時出現了意外情況。本文將介紹如何診斷和解決這一問題。 一、問題描述 當使用 docker lo…

SQL進階之旅 Day 14:數據透視與行列轉換技巧

【SQL進階之旅 Day 14】數據透視與行列轉換技巧 開篇 歡迎來到“SQL進階之旅”系列的第14天&#xff01;今天我們將探討數據透視與行列轉換技巧&#xff0c;這是數據分析和報表生成中的核心技能。無論你是數據庫開發工程師、數據分析師還是后端開發人員&#xff0c;行轉列或列…

haribote原型系統改進方向

在時鐘中斷、計時器和鍵盤輸入方面&#xff0c;一些創新性的改進方向&#xff1a; 時鐘中斷 (PIT / inthandler20) 動態節拍 (Tickless Kernel)&#xff1a;當前的 PIT 中斷以固定頻率&#xff08;約 100Hz&#xff09;觸發&#xff0c;即使系統空閑或沒有即將到期的計時器&…

LabVIEW基于 DataSocket從 OPC 服務器讀取數據

LabVIEW 中基于 DataSocket 函數從 OPC 服務器讀取數據的功能&#xff0c;為工業自動化等場景下的數據交互提供了解決方案。通過特定函數實現 URL 指定、連接建立與管理、數據讀取&#xff0c;相比傳統 Socket 通信和 RESTful API &#xff0c;在 OPC 服務器數據交互場景有適配…

SimpleDateFormat 和 DateTimeFormatter 的異同

在Java開發中Date類型轉String類型是比較常見的&#xff0c;其中最常用的是以下幾種方式&#xff1a; 1. 使用SimpleDateFormat&#xff08;Java 8之前&#xff09; import java.text.SimpleDateFormat; import java.util.Date;public class DateToStringExample {public sta…

《前端面試題:CSS對瀏覽器兼容性》

CSS瀏覽器兼容性完全指南&#xff1a;從原理到實戰 跨瀏覽器兼容性是前端開發的核心挑戰&#xff0c;也是面試中的高頻考點。查看所有css屬性對各個瀏覽器兼容網站&#xff1a;https://caniuse.com 一、瀏覽器兼容性為何如此重要&#xff1f; 在當今多瀏覽器生態中&#xff0c…

【stm32開發板】單片機最小系統原理圖設計

一、批量添加網絡標簽 可以選擇浮動工具中的N&#xff0c;單獨為引腳添加網絡標簽。 當芯片引腳非常多的時候&#xff0c;選中芯片&#xff0c;右鍵選擇扇出網絡標簽/非連接標識 按住ctrl鍵即可選中多個引腳 點擊將引腳名稱填入網絡名 就完成了引腳標簽的批量添加 二、電源引…

golang連接sm3認證加密(app)

文章目錄 環境文檔用途詳細信息 環境 系統平臺&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;4.5 文檔用途 golang連接安全版sm3認證加密數據庫,驅動程序詳見附件。 詳細信息 1.下載Linux golang安裝包 go1.17.3.linux-amd64.tar.gz 1.1. 解壓安…

node實例應用

打開vscode,創建node項目,直接進入一個干凈的文件夾&#xff0c;打開控制臺 一 項目初始化 1. 初始化包管理 npm init -y2. 安裝express npm install express4.17.1 3. 根目錄下創建app.js,引入express // 引入expree const express require(express)// 創建實例 const …

Springboot——整合websocket并根據type區別處理

文章目錄 前言架構思想項目結構代碼實現依賴引入自定義注解定義具體的處理類定義 TypeAWebSocketHandler定義 TypeBWebSocketHandler 定義路由處理類配置類&#xff0c;綁定point制定前端頁面編寫測試接口方便跳轉進入前端頁面 測試驗證結語 前言 之前寫過一篇類似的博客&…

vscode命令行debug

vscode命令行debug 一般命令行debug會在遠程連服務器的時候用上&#xff0c;命令行debug的本質是在執行時暴露一個監聽端口&#xff0c;通過進入這個端口&#xff0c;像本地調試一樣進行。 這里提供兩種方式&#xff1a; 直接在命令行中添加debugpy&#xff0c;適用于python…

Hot100 Day02(移動0,乘最多水的容器、三數之和、接雨水)

移動零 題目鏈接 題目描述&#xff1a; 思路&#xff1a;上述藍色箭頭代表當前遍歷的元素&#xff0c;紅色數字則是當前空位0的位置&#xff0c;每一次遇到非0元素&#xff0c;就是講該元素的位置和空位0的位置進行交換&#xff0c;同時空位0的下標1. 代碼 class Solution …

(eNSP)配置WDS手拉手業務

1.實驗拓撲 2.基礎配置 [SW1]dis cu # sysname SW1 # vlan batch 10 100 110 120 # dhcp enable # interface Vlanif10ip address 192.168.10.2 255.255.255.0 # interface Vlanif100ip address 192.168.100.2 255.255.255.0dhcp select interfacedhcp server excluded-ip-add…

lua的筆記記錄

類似python的eval和exec 可以偽裝成其他格式的文件&#xff0c;比如.dll 希望在異常發生時&#xff0c;能夠讓其沉默&#xff0c;即異常捕獲。而在 Lua 中實現異常捕獲的話&#xff0c;需要使用函數 pcall&#xff0c;假設要執行一段 Lua 代碼并捕獲里面出現的所有錯誤&#xf…

【DeepSeek】【Dify】:用 Dify 對話流+標題關鍵詞注入,讓 RAG 準確率飛躍

1 構建對話流處理數據 初始準備 文章大綱摘要 數據標注和清洗 代碼執行 特別注解 2 對話流測試 準備工作 大綱生成 清洗片段 整合分段 3 構建知識庫 構建 召回測試 4 實戰應用測試 關鍵詞提取 智能總結 測試 1 構建對話流處理數據 初始準備 構建對話變量 用…

RabbitMQ 開機啟動配置教程

RabbitMQ 開機啟動配置教程 在本教程中&#xff0c;我們將詳細介紹如何配置 RabbitMQ 以實現開機自動啟動。此配置適用于手動安裝的 RabbitMQ 版本。 環境準備 操作系統&#xff1a;CentOS 7RabbitMQ 版本&#xff1a;3.8.4Erlang 版本&#xff1a;21.3 步驟 1. 安裝 Erla…