自从舍弃PHP语言后,又开始对Go这个工具下手了,由于PHP对协程的支持并不是特别好,这也是我渐渐选择Golang的原因。Java由于多线程的开起,内存吃紧的厉害,这也是我不选择Java的原因。
此次练手,也谈不上技术,只是为了练手多协程下的爬虫调度,我用它爬取珍爱网各个城市地区首页的用户信息,记录在记事本文档里。
如下图所示:


珍爱网做了一部分的反爬措施,一次性我只能开2条协程,开到4条,珍爱网就会封我IP,这爬的其实也挺憋屈,每一次被封IP,我只能连着我的代理,换IP继续爬
贴上代码,记录一下
下面是这个工程的类文件代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
package zhenaiwang import ( "fmt" "github.com/PuerkitoBio/goquery" "io" "net/http" "os" "sync" ) type GetZhenAiController struct { CityListUrl string //入口url CityList []map[string]string //获取城市名字+城市对应的url } type User struct { NickName string //昵称 Soliloquy string //内心独白 Description string //描述 UserInfo []string //用户个人信息标签 Conditions []string //选择对象条件的标签 } //处理错误函数 func (this *GetZhenAiController) HandleError(err error, when string) { if err != nil { fmt.Println(when) os.Exit(1) } } //单独爬取页面 func (this *GetZhenAiController) GetHtml(url string) io.ReadCloser { client := http.Client{} request, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Printf("wrong http request: %s", err.Error()) return nil } request.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(request) if err != nil { return nil } if resp.StatusCode != http.StatusOK { fmt.Println("Error status code:", resp.StatusCode) return nil } return resp.Body } //获取城市节点 func (this *GetZhenAiController) GetCityList(selector string) { res, err := http.Get(this.CityListUrl) this.HandleError(err, "http请求获取失败") defer res.Body.Close() //io资源需要关闭 if res.StatusCode != 200 { fmt.Println("error status code", res.StatusCode) return } //开始从ioReader中获取dom节点,用选择器选中 dom, err := goquery.NewDocumentFromReader(res.Body) dom.Find(selector).Each(func(i int, selection *goquery.Selection) { item := make(map[string]string) //这一行不能写外面,因为底层是指针引用 href, _ := selection.Attr("href") //获取标签中的属性 text := selection.Text() //获取对象中的文本 item["href"], item["cityName"] = href, text this.CityList = append(this.CityList, item) }) } //用户信息url选择器 func (this *GetZhenAiController) UserUrlSelector(selector string, reader io.Reader) []string { dom, err := goquery.NewDocumentFromReader(reader) this.HandleError(err, "用户url选择器出错") var userInfo_url []string dom.Find(selector).Each(func(i int, selection *goquery.Selection) { href, _ := selection.Attr("href") //获取标签中的属性 userInfo_url = append(userInfo_url, href) }) return userInfo_url } //用户信息选择器 func (this *GetZhenAiController) UserInfoSelector(reader io.Reader) User { dom, err := goquery.NewDocumentFromReader(reader) this.HandleError(err, "用户选择器出错") nick_name := dom.Find(".nickName").First().Text() //昵称 soliloquy := dom.Find(".m-des").First().Text() //获取内心独白 description := dom.Find("div.des").Text() //描述 var user_info []string //个人标签 dom.Find(".m-content-box").Eq(1).Find(".m-btn").Each(func(i int, selection *goquery.Selection) { res := selection.Text() user_info = append(user_info, res) }) var condition []string //择偶条件 dom.Find(".gray-btns").Find(".m-btn").Each(func(i int, selection *goquery.Selection) { res := selection.Text() condition = append(condition, res) }) return User{ NickName: nick_name, Soliloquy: soliloquy, Description: description, UserInfo: user_info, Conditions: condition, } } //用户信息写入文件 func (this *GetZhenAiController) WriteFile(filePath string, info []byte) { fl, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE, 0644) if err != nil { return } defer fl.Close() n, err := fl.Write(info) if err == nil && n < len(info) { } this.HandleError(err, "写入文件出错") } //根据城市节点获取每一页的用户信息 func (this *GetZhenAiController) GetUserInfoByCity() { queue:=make(chan int,2) //珍爱网3条协程撑死了,再多就封IP了 var wg sync.WaitGroup var wr_lock sync.RWMutex for _, v := range this.CityList { city := v["cityName"] res := this.GetHtml(v["href"]) //如果没有返回结果 defer res.Close() if res == nil { continue } queue<-1 wg.Add(1) go func(res io.ReadCloser) { userInfo_urls := this.UserUrlSelector(".g-list th a", res) //该城市用户信息url列表 users := make([]User, 0) for _, v := range userInfo_urls { userInfo_io := this.GetHtml(v) if userInfo_io==nil { continue } defer userInfo_io.Close() user := this.UserInfoSelector(userInfo_io) fmt.Println(user) users = append(users, user) } wr_lock.Lock() for key, user := range users { nick_name := user.NickName //昵称 description := user.Description //描述 solioquy := user.Soliloquy //内心独白 user_info := fmt.Sprintln(user.UserInfo) //用户个人标签 condition := fmt.Sprintln(user.Conditions) //择偶条件 var user_block string if key == 0 { //判断是否是第一个用户,如果是,我们就标记城市信息 user_block += "==========城市:" + city + "==========" + "\n" } user_block += "用户名:" + nick_name + "\n" user_block += "个人描述:" + description + "\n" user_block += "内心独白:" + solioquy + "\n" user_block += "个人详细标签:" + user_info + "\n" user_block += "择偶条件:" + condition + "\n" byte := []byte(user_block) //开启只写模式,与上面的wr_lock.RLock()排斥,要么只读,要么只写,二者不能同时进行 this.WriteFile("F:/笔记/Go笔记/ceshi.txt", byte) } wr_lock.Unlock() wg.Done() <-queue }(res) } wg.Wait() } |
启动爬虫函数
1 2 3 4 5 6 7 8 |
package zhenaiwang func Run(url string){ obj:=new(GetZhenAiController) obj.CityListUrl=url obj.GetCityList(".g-container a") obj.GetUserInfoByCity() } |
主入口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package main import ( "./zhenaiwang" ) const CityList ="http://www.zhenai.com/zhenghun"//获取城市的url地址 func main() { zhenaiwang.Run(CityList) } |
© 著作权归作者所有
文章评论(0)