手摸手教你用go寫爬蟲之二(準備知識:正則解析有效資訊)
本文介紹正則提取字串中的相關資訊
1. 正則
字串中的資訊提取我們使用 regexp
包的相關函式來解決
假設有一段html程式碼如下
<a target="_blank" title="錢江國際商務中心蛋殼公寓直租 月付無壓力 無 南星" _soj="Filter_56&hfilter=filterlist" href="https://hz.zu.anjuke.com/fangyuan/1272631701" >錢江國際商務中心蛋殼公寓直租 月付無壓力 無 南星</a>
我們要提取其中的連結地址和標籤裡的文字內容
step1 編譯正則
pattern := `href="(https://hz.zu.anjuke.com/fangyuan/\d+)"\s*>([^<]+)</a>`` re := regexp.MustCompile(pattern)
編譯正則有兩個函式分別是 regexp.MustCompile
和 regexp.Compile
,第一個遇到錯誤會直接 panic
,第二個 則會返回錯誤資訊,通常兩者的使用是根據是否是輸入正則(選後面)還是固定正則(選前面)
step2 提取內容
正則提取內容有很多函式,常見的有以下幾個,我們根據需要選擇相應的函式
//1. 位元組流查詢單個匹配 然後終止 re.Find(b []byte) //2. 位元組流查詢多個匹配 re.FindAll(b []byte , n int) //3. 位元組流查詢多個匹配 並提取子匹配項 re.FindAllSubmatch(b []byte , n int) //4. 字串匹配單個 re.FindString(s string) //5. 字串匹配多個 re.FindAllString(s string, n int) //6. 字串匹配單個 並提取子匹配項 re.FindStringSubmatch(s string) //7. 字串匹配多個 並提取子匹配項 re.FindAllStringSubmatch((s string,n int)
這裡我們選擇 FindAllStringSubmatch
// 第二個引數為 -1 時 表示搜尋所有匹配的子串 resName := HouseName.FindAllStringSubmatch(contents,-1) for k,m := range resName { fmt.Printf("名稱 : %slink : %s \n",m[2],m[1]) }
正則匹配返回的是一個字串切片,每個切片裡包含了一個子切片,子切片中儲存了對應的子匹配項,我們用 for...range
即可迴圈出來
列印部分結果:

2. 例項
結合前一節講的網頁抓取 以及上面提到的正則,我們寫了一個例項 將提取以下資訊:

例項程式碼:
var ( /* `<a target="_blank" title="錢江國際商務中心蛋殼公寓直租 月付無壓力 無 南星" _soj="Filter_56&hfilter=filterlist" href="https://hz.zu.anjuke.com/fangyuan/1272631701" >錢江國際商務中心蛋殼公寓直租 月付無壓力 無 南星</a>`*/ HouseName = regexp.MustCompile(`href="(https://hz.zu.anjuke.com/fangyuan/\d+)"\s*>([^<]+)</a>`) /* <p class="details-item tag"> 1室1廳<span>|</span>35平米<span>|</span>12/19層<i class="iconfont jjr-icon"></i>姚禮東</p> */ HouseInfo = regexp.MustCompile(`<p class="details-item tag">\s+([^<]+)<span>\|</span>(\d+平米)<span>\|</span>(\d+\/\d+層)<i class="iconfont jjr-icon"></i>([^<]+)</p>`) HousePrice = regexp.MustCompile(`<p><strong>(\d+)</strong> 元/月</p>`) ) func GetContents(url string) (string ,error) { resp,err := http.Get(url) if err != nil { return "",err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("get content failed status code is %d ",resp.StatusCode) } bytes,err := ioutil.ReadAll(resp.Body) if err != nil { return "" , nil } return string(bytes),nil } func main() { //1. 獲取網頁內容部分 url := "https://hz.zu.anjuke.com/fangyuan/xiaoshan/" contents,err := GetContents(url) if err != nil { fmt.Println(err) return } //解析網頁內容部分 resName := HouseName.FindAllStringSubmatch(contents,-1) resInfo := HouseInfo.FindAllStringSubmatch(contents,-1) resPrice := HousePrice.FindAllStringSubmatch(contents,-1) if err != nil { panic(err) return } for k,m := range resName { username := strings.TrimSpace(resInfo[k][4]) fmt.Printf("名稱 : %s資訊 : %s %s %s %s 價格:%s\n",m[2],resInfo[k][1],resInfo[k][2],resInfo[k][3],username,resPrice[k][1]) } }