1. 程式人生 > >用go語言爬取珍愛網 | 第三回

用go語言爬取珍愛網 | 第三回

前兩節我們獲取到了城市的URL和城市名,今天我們來解析使用者資訊。

用go語言爬取珍愛網 | 第一回

用go語言爬取珍愛網 | 第二回

爬蟲的演算法:

我們要提取返回體中的城市列表,需要用到城市列表解析器;

需要把每個城市裡的所有使用者解析出來,需要用到城市解析器;

還需要把每個使用者的個人資訊解析出來,需要用到使用者解析器。

爬蟲整體架構:

Seed把需要爬的request送到engine,engine負責將request裡的url送到fetcher去爬取資料,返回utf-8的資訊,然後engine將返回資訊送到解析器Parser裡解析有用資訊,返回更多待請求requests和有用資訊items,任務佇列用於儲存待請求的request,engine驅動各模組處理資料,直到任務佇列為空。

程式碼實現:

按照上面的思路,設計出城市列表解析器citylist.go程式碼如下:

package parser

import (
 "crawler/engine"
 "regexp"
 "log"
)

const (
 //<a href="http://album.zhenai.com/u/1361133512" target="_blank">怎麼會迷上你</a>
 cityReg = `<a href="(http://album.zhenai.com/u/[0-9]+)"[^>]*>([^<]+)</a>`
)

func ParseCity(contents []byte) engine.ParserResult {
 compile := regexp.MustCompile(cityReg)

 submatch := compile.FindAllSubmatch(contents, -1)

 //這裡要把解析到的每個URL都生成一個新的request

 result := engine.ParserResult{}

 for _, m := range submatch {
   name := string(m[2])
   log.Printf("UserName:%s URL:%s\n", string(m[2]), string(m[1]))

   //把使用者資訊人名加到item裡
   result.Items = append(result.Items, name)

   result.Requests = append(result.Requests,
     engine.Request{
       //使用者資訊對應的URL,用於之後的使用者資訊爬取
       Url : string(m[1]),
       //這個parser是對城市下面的使用者的parse
       ParserFunc : func(bytes []byte) engine.ParserResult {
         //這裡使用閉包的方式;這裡不能用m[2],否則所有for迴圈裡的使用者都會共用一個名字
         //需要拷貝m[2] ---- name := string(m[2])
         return ParseProfile(bytes, name)
       },
     })
 }

 return result
}

城市解析器city.go如下:

package parser

import (
 "crawler/engine"
 "regexp"
 "log"
)

const (
 //<a href="http://album.zhenai.com/u/1361133512" target="_blank">怎麼會迷上你</a>
 cityReg = `<a href="(http://album.zhenai.com/u/[0-9]+)"[^>]*>([^<]+)</a>`
)

func ParseCity(contents []byte) engine.ParserResult {
 compile := regexp.MustCompile(cityReg)

 submatch := compile.FindAllSubmatch(contents, -1)

 //這裡要把解析到的每個URL都生成一個新的request

 result := engine.ParserResult{}

 for _, m := range submatch {
   name := string(m[2])
   log.Printf("UserName:%s URL:%s\n", string(m[2]), string(m[1]))

   //把使用者資訊人名加到item裡
   result.Items = append(result.Items, name)

   result.Requests = append(result.Requests,
     engine.Request{
       //使用者資訊對應的URL,用於之後的使用者資訊爬取
       Url : string(m[1]),
       //這個parser是對城市下面的使用者的parse
       ParserFunc : func(bytes []byte) engine.ParserResult {
         //這裡使用閉包的方式;這裡不能用m[2],否則所有for迴圈裡的使用者都會共用一個名字
         //需要拷貝m[2] ---- name := string(m[2])
         return ParseProfile(bytes, name)
       },
     })
 }

 return result
}

 使用者解析器profile.go如下:

package parser

import (
 "crawler/engine"
 "crawler/model"
 "regexp"
 "strconv"
)

var (
 // <td><span class="label">年齡:</span>25歲</td>
 ageReg = regexp.MustCompile(`<td><span class="label">年齡:</span>([\d]+)歲</td>`)
 // <td><span class="label">身高:</span>182CM</td>
 heightReg = regexp.MustCompile(`<td><span class="label">身高:</span>(.+)CM</td>`)
 // <td><span class="label">月收入:</span>5001-8000元</td>
 incomeReg = regexp.MustCompile(`<td><span class="label">月收入:</span>([0-9-]+)元</td>`)
 //<td><span class="label">婚況:</span>未婚</td>
 marriageReg = regexp.MustCompile(`<td><span class="label">婚況:</span>(.+)</td>`)
 //<td><span class="label">學歷:</span>大學本科</td>
 educationReg = regexp.MustCompile(`<td><span class="label">學歷:</span>(.+)</td>`)
 //<td><span class="label">工作地:</span>安徽蚌埠</td>
 workLocationReg = regexp.MustCompile(`<td><span class="label">工作地:</span>(.+)</td>`)
 // <td><span class="label">職業: </span>--</td>
 occupationReg = regexp.MustCompile(`<td><span class="label">職業: </span><span field="">(.+)</span></td>`)
 //  <td><span class="label">星座:</span>射手座</td>
 xinzuoReg = regexp.MustCompile(`<td><span class="label">星座:</span><span field="">(.+)</span></td>`)
 //<td><span class="label">籍貫:</span>安徽蚌埠</td>
 hokouReg = regexp.MustCompile(`<td><span class="label">民族:</span><span field="">(.+)</span></td>`)
 // <td><span class="label">住房條件:</span><span field="">--</span></td>
 houseReg = regexp.MustCompile(`<td><span class="label">住房條件:</span><span field="">(.+)</span></td>`)
 // <td width="150"><span class="grayL">性別:</span>男</td>
 genderReg = regexp.MustCompile(`<td width="150"><span class="grayL">性別:</span>(.+)</td>`)

 // <td><span class="label">體重:</span><span field="">67KG</span></td>
 weightReg = regexp.MustCompile(`<td><span class="label">體重:</span><span field="">(.+)KG</span></td>`)
 //<h1 class="ceiling-name ib fl fs24 lh32 blue">怎麼會迷上你</h1>
 //nameReg = regexp.MustCompile(`<h1 class="ceiling-name ib fl fs24 lh32 blue">([^\d]+)</h1>  `)
 //<td><span class="label">是否購車:</span><span field="">未購車</span></td>
 carReg = regexp.MustCompile(`<td><span class="label">是否購車:</span><span field="">(.+)</span></td>`)
)

func ParseProfile(contents []byte, name string) engine.ParserResult {

 profile := model.Profile{}

 age, err := strconv.Atoi(extractString(contents, ageReg))

 if err != nil {
   profile.Age = 0
 }else {
   profile.Age = age
 }



 height, err := strconv.Atoi(extractString(contents, heightReg))
 if err != nil {
   profile.Height = 0
 }else {
   profile.Height = height
 }

 weight, err := strconv.Atoi(extractString(contents, weightReg))
 if err != nil {
   profile.Weight = 0
 }else {
   profile.Weight = weight
 }

 profile.Income = extractString(contents, incomeReg)

 profile.Car = extractString(contents, carReg)

 profile.Education = extractString(contents, educationReg)
 profile.Gender = extractString(contents, genderReg)

 profile.Hokou = extractString(contents, hokouReg)
 profile.Income = extractString(contents, incomeReg)
 profile.Marriage = extractString(contents, marriageReg)
 profile.Name = name
 profile.Occupation = extractString(contents, occupationReg)
 profile.WorkLocation = extractString(contents, workLocationReg)
 profile.Xinzuo = extractString(contents, xinzuoReg)

 result := engine.ParserResult{
   Items: []interface{}{profile},
 }

 return result
}

//get value by reg from contents
func extractString(contents []byte, re *regexp.Regexp) string {

 m := re.FindSubmatch(contents)

 if len(m) > 0 {
   return string(m[1])
 } else {
   return ""
 }
}

engine程式碼如下:

package engine

import (
 "crawler/fetcher"
 "log"
)

func Run(seeds ...Request){

 //這裡維持一個佇列
 var requestsQueue []Request

 requestsQueue = append(requestsQueue, seeds...)

 for len(requestsQueue) > 0 {
   //取第一個
   r := requestsQueue[0]
   //只保留沒處理的request
   requestsQueue = requestsQueue[1:]

   log.Printf("fetching url:%s\n", r.Url)
   //爬取資料
   body, err := fetcher.Fetch(r.Url)

   if err != nil {
     log.Printf("fetch url: %s; err: %v\n", r.Url, err)
     //發生錯誤繼續爬取下一個url
     continue
   }

   //解析爬取到的結果
   result := r.ParserFunc(body)

   //把爬取結果裡的request繼續加到request佇列
   requestsQueue = append(requestsQueue, result.Requests...)

   //列印每個結果裡的item,即列印城市名、城市下的人名...
   for _, item := range result.Items {
     log.Printf("get item is %v\n", item)
   }
 }
}

Fetcher用於發起http get請求,這裡有一點注意的是:珍愛網可能做了反爬蟲限制手段,所以直接用http.Get(url)方式發請求,會報403拒絕訪問;故需要模擬瀏覽器方式:

client := &http.Client{}
 req, err := http.NewRequest("GET", url, nil)
 if err != nil {
   log.Fatalln("NewRequest is err ", err)
   return nil, fmt.Errorf("NewRequest is err %v\n", err)
 }

 req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36")

 //返送請求獲取返回結果
 resp, err := client.Do(req)

最終fetcher程式碼如下:

package fetcher

import (
 "bufio"
 "fmt"
 "golang.org/x/net/html/charset"
 "golang.org/x/text/encoding"
 "golang.org/x/text/encoding/unicode"
 "golang.org/x/text/transform"
 "io/ioutil"
 "log"
 "net/http"
)

/**
爬取網路資源函式
*/
func Fetch(url string) ([]byte, error) {

 client := &http.Client{}
 req, err := http.NewRequest("GET", url, nil)
 if err != nil {
   log.Fatalln("NewRequest is err ", err)
   return nil, fmt.Errorf("NewRequest is err %v\n", err)
 }

 req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36")

 //返送請求獲取返回結果
 resp, err := client.Do(req)

 //直接用http.Get(url)進行獲取資訊,爬取時可能返回403,禁止訪問
 //resp, err := http.Get(url)

 if err != nil {
   return nil, fmt.Errorf("Error: http Get, err is %v\n", err)
 }

 //關閉response body
 defer resp.Body.Close()

 if resp.StatusCode != http.StatusOK {
   return nil, fmt.Errorf("Error: StatusCode is %d\n", resp.StatusCode)
 }

 //utf8Reader := transform.NewReader(resp.Body, simplifiedchinese.GBK.NewDecoder())
 bodyReader := bufio.NewReader(resp.Body)
 utf8Reader := transform.NewReader(bodyReader, determineEncoding(bodyReader).NewDecoder())

 return ioutil.ReadAll(utf8Reader)
}

/**
確認編碼格式
*/
func determineEncoding(r *bufio.Reader) encoding.Encoding {

 //這裡的r讀取完得保證resp.Body還可讀
 body, err := r.Peek(1024)

 //如果解析編碼型別時遇到錯誤,返回UTF-8
 if err != nil {
   log.Printf("determineEncoding error is %v", err)
   return unicode.UTF8
 }

 //這裡簡化,不取是否確認
 e, _, _ := charset.DetermineEncoding(body, "")
 return e
}

main方法如下:

package main

import (
 "crawler/engine"
 "crawler/zhenai/parser"
)

func main() {

 request := engine.Request{
   Url: "http://www.zhenai.com/zhenghun",
   ParserFunc: parser.ParseCityList,
 }

 engine.Run(request)
}

最終爬取到的使用者資訊如下,包括暱稱、年齡、身高、體重、工資、婚姻狀況等。

如果你想要哪個妹子的照片,可以點開url檢視,然後打招呼進一步發展。

至此單任務版的爬蟲就做完了,後面我們將對單任務版爬蟲做效能分析,然後升級為多工併發版,把爬取到的資訊存到ElasticSearch中,在頁面上查詢



本公眾號免費提供csdn下載服務,海量IT學習資源,如果你準備入IT坑,勵志成為優秀的程式猿,那麼這些資源很適合你,包括但不限於java、go、python、springcloud、elk、嵌入式 、大資料、面試資料、前端 等資源。同時我們組建了一個技術交流群,裡面有很多大佬,會不定時分享技術文章,如果你想來一起學習提高,可以公眾號後臺回覆【2】,免費邀請加技術交流群互相學習提高,會不定期分享程式設計IT相關資源。


掃碼關注,精彩內容第一時間推給你



本公眾號免費提供csdn下載服務,海量IT學習資源,如果你準備入IT坑,勵志成為優秀的程式猿,那麼這些資源很適合你,包括但不限於java、go、python、springcloud、elk、嵌入式 、大資料、面試資料、前端 等資源。同時我們組建了一個技術交流群,裡面有很多大佬,會不定時分享技術文章,如果你想來一起學習提高,可以公眾號後臺回覆【2】,免費邀請加技術交流群互相學習提高,會不定期分享程式設計IT相關資源。


掃碼關注,精彩內容第一時間推給你

相關推薦

go語言珍愛 |

前兩節我們獲取到了城市的URL和城市名,今天我們來解析使用者資訊。 用go語言爬取珍愛網 | 第一回 用go語言爬取珍愛網 | 第二回 爬蟲的演算法: 我們要提取返回體中的城市列表,需要用到城市列表解析器; 需要把每個城市裡的所有使用者解析出來,需要用到城市解析器; 還需要把每個使用者的個人資訊解析出來,

go語言珍愛 | 第一

我們來用go語言爬取“珍愛網”使用者資訊。 首先分析到請求url為: http://www.zhenai.com/zhenghun 接下來用go請求該url,程式碼如下: package main import ( "fmt" "io/ioutil" &

go語言珍愛 | 第二

昨天我們一起爬取珍愛網首頁,拿到了城市列表頁面,接下來在返回體城市列表中提取城市和url,即下圖中的a標籤裡的href的值和innerText值。 提取a標籤,可以通過CSS選擇器來選擇,如下: $('#cityList>dd>a');就可以獲取到470個a標籤: 這裡只提供一個思

珍愛戶資訊展示

golang爬取珍愛網,爬到了3萬多使用者資訊,並存到了elasticsearch中,如下圖,查詢到了3萬多使用者資訊。 先來看看最終效果: 利用到了go語言的html模板庫: 執行模板渲染: func (s SearchResultView) Render (w io.Writer, data mo

go語言椎名真白

regexp highlight defer reg write rul png span link 單任務版: package main import ( "net/http" "regexp" "io/ioutil" "os" "strconv" "ti

go 語言百度貼吧中的內容

涉及到的知識點有 通道chan ,切片的使用 ,os,http 包的使用 package main import ( "fmt" "net/http" "os" "strconv" ) func pachong(start, end int) { //明確爬的地址 url :=

go語言爬蟲】go語言豆瓣電影top250

抓取欄位:電影名稱、評分、評價人數 二、執行: 正在抓取第0頁…… 肖申克的救贖 9.6 824764人 這個殺手不太冷 9.4 791399人 霸王別姬 9.5 589028人 阿甘正傳 9.4 678850人 美麗人生 9.5 3940

crawl spider起點小說信息

models anti arc pub work 全部 see 效率 rand 起點作為主流的小說網站,在防止數據采集反面還是做了準備的,其對主要的數字采用了自定義的編碼映射取值,想直接通過頁面來實現數據的獲取,是無法實現的。 單獨獲取數字還是可以實現的,通過reques

Python爬蟲廣州大學教務系統的成績(內訪問)

enc 用途 css選擇器 狀態 csv文件 表格 area 加密 重要 用Python爬蟲爬取廣州大學教務系統的成績(內網訪問) 在進行爬取前,首先要了解: 1、什麽是CSS選擇器? 每一條css樣式定義由兩部分組成,形式如下: [code] 選擇器{樣式} [/code

python之爬蟲的入門05------實戰:貝殼re匹配需要的資料)

# 第二頁:https://hz.zu.ke.com/zufang/pg2 # 第一頁:https://hz.zu.ke.com/zufang/pg1 import urllib.request import random import re def user_ip(): ''

R語言前程無憂招聘職位

資料的獲取是資料探勘的第一步,如果沒有資料何談資料探勘?有時候在做演算法測試的時候,一個好的資料集也是演算法實驗成功的前提保障。當然我們可以去網上下載大型資料網站整理好的,專業的資料,但是自己動手爬取資料是不是更愜意呢? 說到這裡,給大家推薦一些常用的大型資料集: (1)、Mov

python來中國天氣北京,上海,成都8-15天的天氣

2 爬取北京,上海,成都的天氣            from bs4 import BeautifulSoup import random import requests import socket impo

go語言linux系統絡卡MAC和硬碟序列號

利用cgo,實現在go語言中呼叫c語言函式,取MAC和硬碟序列號。cgo中需要注意的:*/和import "C"兩行之間不能有其它內容! package main /* #include <stdio.h> #include <stdlib.h>

windows下Go語言實現一個hello world

1,下載go編譯器———go編譯器下載地址https://golang.org/dl/ go編譯器下載地址 2,然後點選進行安裝,由於是msi檔案,如果需要.NET元件請自行下載進行安裝

Python 爬蟲步 -- 多執行緒爬蟲噹噹書籍資訊

XPath 的安裝以及使用 1 . XPath 的介紹 剛學過正則表示式,用的正順手,現在就把正則表示式替換掉,使用 XPath,有人表示這太坑爹了,早知道剛上來就學習 XPath 多省事 啊。其實我個人認為學習一下正則表示式是大有益處的,之所以換成 XPa

python爬蟲網頁桌布圖片(彼岸桌面唯美圖片)

今天想給我的電腦裡面多加點桌布,但是嫌棄一個個儲存太慢,於是想著寫個爬蟲直接批量爬取,因為爬蟲只是很久之前學過一些,很多基礎語句都不記得了,於是直接在網上找了個有基礎操作語句的爬蟲程式碼,在這上面進行修改以適應我的要求和爬取的網頁需求 注意:這次爬取的

R語言中國天氣單個城市實時天氣預報資料

在傳統零售行業,雨天天氣大概會影響晴天30%-40%的銷售業績,所以從網上獲取天氣資料來作分析,並根據天氣資料作出預測,提前做好預防措施和提醒業務人員,把損失減少到最低就顯得十分重要,用R語言的rvest包就可以方便抓取天氣資料: 本文章的例子僅用於學習之用,

接口今日頭條圖片

b+ req ace nco ext odin api data utf #encoding:utf8import requestsimport jsonimport redemo = requests.get(‘http://www.toutiao.com/api/pc/

scrapy實戰1分布式有緣

req 年齡 dict ems arch last rem pen war 直接上代碼: items.py 1 # -*- coding: utf-8 -*- 2 3 # Define here the models for your scraped items

多線程版故事

實現 exe don comm value obj nco result nic 前言:為了能以更高效的速度爬取,嘗試采用了多線程本博客參照代碼及PROJECT來源:http://kexue.fm/archives/4385/ 源代碼: 1 #! -*- cod