用golang 实现一个代理池

爬虫的时候总会遇到爬取速度过快而被封IP的情况,这个时候就需要使用代理了。在https://github.com/henson/ProxyPool
的启发下,决定自己实现一个代理池。项目已经开源在github。

https://github.com/AceDarkkinght/GoProxyCollector

开发环境

windows 7,Go 1.8.4

数据来源




https://www.kuaidaili.com



https://proxy.coderbusy.com

项目结构 目录 作用
collector   收集器,抓取各个网站的代理  
result   表示抓取的结果  
scheduler   负责任务调度,包括启动collector和入库  
server   启动一个web服务,提供取结果的API  
storage   存储结果,通过接口可以使用别的数据库  
util   一些常用的工具方法  
verifier   ip的验证与入库出库  
实现

collector

collector 支持两种模式,分别是使用goquery对网页元素进行选择和使用正则表达式匹配我们需要的信息。直接上代码吧。
```go
// github.com\AceDarkkinght\GoProxyCollector\collector\selectorCollector.go
func (c SelectorCollector) Collect(ch chan<- result.Result) {
// 退出前关闭channel。
defer close(ch)

response, _, errs := gorequest.New().Get(c.currentUrl).Set("User-Agent", util.RandomUA()).End()

/* 省略部分代码 */

// 有些网站不是UTF-8编码的,需要进行转码。
var decoder mahonia.Decoder
if c.configuration.Charset != "utf-8" {
decoder = mahonia.NewDecoder(c.configuration.Charset)
}

// 使用goquery。
doc, err := goquery.NewDocumentFromReader(response.Body)
if err != nil {
seelog.Errorf("parse %s error:%v", c.currentUrl, err)
return
}

// 大部分代理网站的代理列表都放在一个table里,先选出table再循环里面的元素。
selection := doc.Find(c.selectorMap["table"][0])
selection.Each(func(i int, sel *goquery.Selection) {
var (
ip string
port int
speed float64
location string
)

// 我们需要的信息的名字和路径存在collectorConfig.xml。 nameValue := make(map[string]string) for key, value := range c.selectorMap { if key != "table" { var temp string if len(value) == 1 { temp = sel.Find(value[0]).Text() } else if len(value) == 2 { temp, _ = sel.Find(value[0]).Attr(value[1]) } // 转码. if temp != "" { if decoder != nil { temp = decoder.ConvertString(temp) } nameValue[key] = temp } } } /* 省略部分代码 */ // 过滤一些不符合条件的结果 if ip != "" && port > 0 && speed >= 0 && speed < 3 { r := &result.Result{ Ip: ip, Port: port, Location: location, Speed: speed, Source: c.currentUrl} // 把符合条件的结果放进channel ch <- r }

})
}

// github.com\AceDarkkinght\GoProxyCollector\collector\regexCollector.go
func (c RegexCollector) Collect(ch chan<- result.Result) {
response, bodyString, errs := gorequest.New().Get(c.currentUrl).Set("User-Agent", util.RandomUA()).End()

/* 省略部分代码 */ // 用正则匹配。 regex := regexp.MustCompile(c.selectorMap["ip"]) ipAddresses := regex.FindAllString(bodyString, -1) if len(ipAddresses) <= 0 { seelog.Errorf("can not found correct format ip address in url:%s", c.currentUrl) return } for _, ipAddress := range ipAddresses { temp := strings.Split(ipAddress, ":") if len(temp) == 2 { port, _ := strconv.Atoi(temp[1]) if port <= 0 { continue } r := &result.Result{ Ip: temp[0], Port: port, Source: c.currentUrl, } ch <- r } }

}
- ##### result result很简单,只是用来表示collector爬取的结果。go
// github.com\AceDarkkinght\GoProxyCollector\result\result.go
type Result struct {
Ip string json:"ip"
Port int json:"port"
Location string json:"location,omitempty"
Source string json:"source"
Speed float64 json:"speed,omitempty"
}
- ##### scheduler scheduler负责完成一些初始化的工作以及调度collector任务。不同的任务在不同的goroutine中运行,goroutine之间通过channel进行通信。go
// github.com\AceDarkkinght\GoProxyCollector\scheduler\scheduler.go
func Run(configs collector.Configs, storage storage.Storage) {
/ 省略部分代码 */

for { var wg sync.WaitGroup for _, configuration := range configs.Configs { wg.Add(1) go func(c collector.Config) { // 防止死锁。 defer wg.Done() // 处理panic。 defer func() { if r := recover(); r != nil { seelog.Criticalf("collector %s occur panic %v", c.Name, r) } }() col := c.Collector() done := make(chan bool, 1) go func() { runCollector(col, storage) // 完成时发送信号。 done <- true }() // 设置timeout防止goroutine运行时间过长。 select { case <-done: seelog.Debugf("collector %s finish.", c.Name) case <-time.After(7 * time.Minute): seelog.Errorf("collector %s time out.", c.Name) } }(configuration) } // 等待所有collector完成。 wg.Wait() seelog.Debug("finish once, sleep 10 minutes.") time.Sleep(time.Minute * 10) }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpgjsg.html