有个朋友想用Go语言编写一个多线程爬虫,并且使用代理IP来应对反爬措施。多线程在Go中通常是通过goroutine实现的,所以应该使用goroutine来并发处理多个网页的抓取。然后,代理IP的话,可能需要一个代理池,从中随机选择代理来发送请求,避免同一个IP被封锁。大体思路就是这样,具体看我下面实操吧。

以下是一个使用Go语言编写的多线程爬虫程序,结合代理IP和反爬策略的示例代码:
go
package main
import (
"bufio"
"crypto/tls"
"fmt"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"sync"
"time"
)
var (
proxyPool []*url.URL
proxyMux sync.Mutex
userAgents = []string{
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36",
}
visitedURLs sync.Map
)
func main() {
// 初始化随机种子
rand.Seed(time.Now().UnixNano())
// 加载代理列表
if err := loadProxies("proxies.txt"); err != nil {
fmt.Println("Error loading proxies:", err)
return
}
// 创建任务通道
taskChan := make(chan string, 100)
// 启动worker
var wg sync.WaitGroup
workerCount := 10
for i := 0; i < workerCount; i++ {
wg.Add(1)
go worker(taskChan, &wg)
}
// 添加初始任务
go func() {
taskChan <- "https://httpbin.org/ip"
taskChan <- "https://example.com"
}()
// 等待所有worker完成(实际可能需要其他停止机制)
wg.Wait()
close(taskChan)
}
func loadProxies(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
proxyURL, err := url.Parse("http://" + line)
if err != nil {
continue
}
proxyPool = append(proxyPool, proxyURL)
}
return nil
}
func worker(taskChan <-chan string, wg *sync.WaitGroup) {
defer wg.Done()
for targetURL := range taskChan {
// URL去重
if _, loaded := visitedURLs.LoadOrStore(targetURL, true); loaded {
continue
}
// 随机延迟(1-3秒)
time.Sleep(time.Duration(1+rand.Intn(2)) * time.Second)
// 获取随机代理
proxy := getRandomProxy()
if proxy == nil {
fmt.Println("No available proxies")
continue
}
// 创建自定义Transport
transport := &http.Transport{
Proxy: http.ProxyURL(proxy),
DialContext: (&net.Dialer{
Timeout: 10 * time.Second,
KeepAlive: 10 * time.Second,
}).DialContext,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
// 创建HTTP客户端
client := &http.Client{
Transport: transport,
Timeout: 15 * time.Second,
}
// 创建请求
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
fmt.Printf("Error creating request: %v\n", err)
continue
}
// 设置随机User-Agent
req.Header.Set("User-Agent", userAgents[rand.Intn(len(userAgents))])
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Error fetching %s: %v\n", targetURL, err)
markBadProxy(proxy)
continue
}
defer resp.Body.Close()
// 处理响应
if resp.StatusCode != http.StatusOK {
fmt.Printf("Non-200 status code: %d for %s\n", resp.StatusCode, targetURL)
continue
}
// 读取响应内容
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Printf("Error reading response body: %v\n", err)
continue
}
// 处理页面内容(示例:输出内容长度)
fmt.Printf("Successfully fetched %s (%d bytes) using proxy %s\n",
targetURL, len(body), proxy.Host)
// 这里可以添加链接提取和任务添加逻辑
// parseLinks(body, taskChan)
}
}
func getRandomProxy() *url.URL {
proxyMux.Lock()
defer proxyMux.Unlock()
if len(proxyPool) == 0 {
return nil
}
return proxyPool[rand.Intn(len(proxyPool))]
}
func markBadProxy(badProxy *url.URL) {
proxyMux.Lock()
defer proxyMux.Unlock()
for i, p := range proxyPool {
if p.String() == badProxy.String() {
// 移除无效代理
proxyPool = append(proxyPool[:i], proxyPool[i+1:]...)
fmt.Printf("Removed bad proxy: %s\n", badProxy.String())
break
}
}
}
// 示例链接解析函数(需要根据实际情况实现)
func parseLinks(body []byte, taskChan chan<- string) {
// 这里可以添加HTML解析逻辑,提取链接并发送到taskChan
// 注意:需要处理相对路径转换和域名限制
}
关键特性说明:
-
代理管理:
- 从文件加载代理列表(格式:ip:port)
- 随机选择代理进行请求
- 自动移除失效代理
-
反爬策略:
- 随机User-Agent轮换
- 请求间随机延迟(1-3秒)
- 自动代理失效检测和移除
- 支持HTTPS和跳过证书验证
-
并发控制:
- 使用带缓冲的channel作为任务队列
- 可配置的worker数量
- 自动URL去重(使用sync.Map)
-
健壮性设计:
- 连接超时设置(15秒)
- 自定义Transport参数优化
- 完善的错误处理
使用说明:
- 创建代理文件
proxies.txt
,格式为每行一个代理地址(ip:port) - 根据需求调整以下参数:
workerCount
:并发worker数量taskChan
缓冲大小- 各种超时参数
- 随机延迟时间范围
- 实现
parseLinks
函数来提取新链接 - 添加具体的页面解析逻辑
注意事项:
- 代理质量直接影响爬虫效果,建议使用优质代理服务
- 根据目标网站的robots.txt和版权政策合理使用
- 添加适当的速率限制避免对目标网站造成过大压力
- 对于复杂反爬机制可能需要添加更多策略:
- Cookie管理
- JavaScript渲染支持
- 验证码识别
- 请求头随机化
- 请求参数随机化
上面代码就是我提供了基础的爬虫框架,大家在使用的时候可以根据具体需求进行扩展和优化,有了这样的代码示例能让你们省很多事情。