ADSL拨号代理的搭建方式

我们在 9.2 节尝试维护过一个代理池,从中可以挑选出许多可用的代理,但这些代理常常稳定性不高、响应速度慢,而且大概率是公共代理,意味着同一时间可能有多个人使用,故被封的概率很大。另外,这些代理的有效时间可能比较短,虽然代理池一直在筛选可用代理,但不免存在没有及时更新状态的情况,这样有可能导致我们得到不可用的代理。

在 9.3 节,我们也了解了付费代理,其质量相对免费代理会好不少,的确算是一个相对不错的方案,但本节要介绍的方案可以使我们既能不断更换代理,又可以保证代理的稳定性。

大家可能会在一些付费代理套餐中注意到这样一个套餐一一独享代理或私密代理,这种代理其实是使用专用服务器搭建了代理服务,相对一般的付费代理来说,稳定性更好,速度也更快,同时 IP 可以动态变化。这种代理的 IP 切换大多是基于 ADSL 拨号机制实现的,一台云主机每拨号一次就可以换一个 IP,同时云主机上搭建了代理服务,我们可以直接使用该云主机的 HTTP 代理来进行数据爬取。

本节就来讲解搭建一个 ADSL 拨号代理服务的方法。

什么是ADSL

ADSL 的英文全称是 Asymmetric Digital Subscriber Line,即非对称数字用户环路。它的上行带宽和下行带宽不对称,采用频分复用技术把普通电话线分成了电话、上行和下行 3 个相对独立的信道,从而避免了相互之间的干扰。

ADSL 通过拨号的方式上网,拨号时需要输入 ADSL 账号和密码,每拨号一次就更换一个IP。IP 分布在多个 A 段,如果这些IP都能使用,意味着 IP 量级可达千万。如果我们将 ADSL 主机作为代理,每隔一段时间云主机拨号换一个 IP,就可以有效防止 IP 被封禁。另外,由于我们直接使用专有的云主机搭建代理服务,所以代理的稳定性相对更好,响应速度也相对更快。

准备工作

在本节开始之前,需要先购买几台 ADSL 代理云主机,建议购买2台或以上。因为在云主机拨号的一瞬间,服务器正在切换IP,所以拨号之后代理是不可用的状态,需要2台及以上云主机做负载均衡。

ADSL代理云主机的服务商还是比较多的,个人推荐阿斯云和云立方,官网分别为https://asiyun.cn 和https://www.yunlifang.cn/。

本节以阿斯云为例,我购买了一台电信型云服务器,同时安装了 CentOSLinux 系统的云主机。购买成功后,可以在后台找到服务器的连接IP、端口、用户名、密码,以及拨号所用的用户名和密码,如图 9-9 所示。

图9-9 查看云服务器的相关信息

再找到远程管理面板一远程连接的用户名和密码,也就是SSH远程连接服务器的信息。例如我使用的 IP 和端口是 zhongweidx01.jsq.bz:30042,用户名是 root。在命令行下输入如下内容:

ssh root@zhongweidx01.jsq.bz -p 30042

输入连接密码,就可以连接到远程服务器了,如图 9-10 所示。

图9-10 连接到远程服务器

测试拨号

云主机默认已经配置了拨号相关的信息,如宽带用户名和密码等,所以我们无须额外进行配置,只需要调用相应的拨号命令即可实现拨号和 IP 地址的切换。

可以输入如下拨号命令来拨号: pppoe-start

拨号命令成功运行,没有报错信息,耗时约几秒,结束之后整个主机就获得了一个有效的IP地址。如果要停止拨号,可以输入如下命令:

pppoe-stop

运行完该命令后,网络就会断开,之前的IP地址也会被释放。

不同云主机的拔号命令和停止命令可能不同,如云立方主机的拨号命令和停止命令为 adsl-start 和 adsl-stop,请以官方文档的说明为准。

所以,如果想切换IP,只需要先执行pppoe-stop,再执行pppoe-start即可。每次拨号前,可以用ifconfig命令查看主机的IP,如图9-11所示。

图9-11拨号前后的IP变化

可以看到,执行了pppoe-stop和pppoe-start命令之后,通过ifconfig命令获取的网卡信息中的IP地址就变了,代表我们成功实现了IP地址的切换。

那么要想将这台云主机设置为可以实时变化IP的代理服务器,主要得做这几件事情:

  • 在云主机上运行代理服务软件,使之可以提供HTTP代理服务;

  • 使云主机定时拨号,更换IP:

  • 实时获取云主机的代理IP和端口信息。

设置代理服务器

当前云主机使用的是 Linux 的 CentOS 系统,它是无法作为一个 HTTP 代理服务器使用的,因为该云主机上目前并没有运行相关的代理软件。要想让它提供 HTTP 代理服务,需要安装并运行相关的代理服务软件。

那么什么软件能提供这种代理服务呢?目前业界比较流行的有 Squid 和 TinyProxy,在云主机上配置好它们后,它们会在特定端口上运行一个 HTTP 代理。知道了云主机当前的 IP,我们就能使用 Squid 或 TinyProxy 提供的 HTTP 代理了。

这里以 Squid 为例演示一下配置过程。首先安装 Squid,在 CentOS 系统上安装 Squid 的命令如下:

sudo yum -y update
yum -y install squid

运行完这两行命令行,Squid 就安装成功了。

如果想启动 Squid,可以运行如下命令:

systemctl start squid

如果想配置开机自动启动,可以运行如下命令:

systemctl enable squid

成功启动 Squid 后,可以使用如下命令查看当前的运行状态:

systemctl status squid

结果如图 9-12 所示,可以看到 Squid 已经成功运行了。

图9-12 成功运行的Squid

Squid 默认会运行在 3128 端口,相当于在云主机的 3128 端口启动了代理服务。接下来我们测试一下 Squid 的代理效果,在该云主机上运行 curl 命令,请求 https://www.httpbin.org ,并使用在云主机上配置的代理服务:

curl -x http://127.0.0.1:3128 https://www.httpbin.org/get

这里 curl 的 -x 参数代表设置 HTTP 代理,由于现在是在云主机上运行,所以直接将代理设置为了 http://127.0.0.1:3128 。运行完毕之后,再用 ifconfig 命令查看下当前云主机的 IP,结果如图9-13 所示。

以下是图片中的文本:

图 9-13 使用代理请求测试网站

可以看到测试网站的返回结果中的 origin 字段的 IP 和用 ifconfig 获取的 IP 是一致的。

接下来,在自己的本机上 (非云主机) 运行如下命令行测试 Squid 的连通情况,这里的 IP 就需要更换为云主机本身的 IP,从图 9-13 可以看到云主机当前拨号的 IP 是 106.45.104.166,所以需要运行如下命令:

curl -x 106.45.104.166:3128 https://www.httpbin.org/get

然而发现并没有输出对应的结果,代理连接失败。其实失败的原因在于 Squid 默认不开启“允许外网访问”,对此我们可以修改 Squid 的相关配置,例如修改当前代理的运行端口、允许连接的 IP、配置高匿名代理等,这些都需要用到配置文件 /etc/squid/squid.conf。

要开启 “允许公网访问”,最简单的方法就是将配置文件中的这行:

http_access deny all

修改为:

http_access allow all

意思是允许来自所有 IP 的请求连接本代理。另外还需要在配置文件的开头配置 acl 的部分添加:

acl all src 0.0.0.0/0

然后,将 Squid 配置成高匿名代理,这样目标网站就无法通过一些参数 (如 X-Forwarded-For) 得知爬虫本身的 IP 了,所以,在配置文件中再添加如下内容:

request_header_access Via deny all
request_header_access X-Forwarded-For deny all

考虑到有些云主机厂商默认封禁了 Squid 代理所在的 3128 端口,因此建议更换一个端口,例如 3328,修改这行即可:

http_port 3128

将其中的 3128 修改为 3328。

以下是图片中的文本:

http_port 3328

修改完这些配置信息后,保存配置文件,重新启动 Squid 代理:

systemctl restart squid

此时重新在本机上 (非云主机) 运行刚才的 curl 命令 (将端口修改为 3328):

curl -x http://106.45.104.166:3328 https://www.httpbin.org/get

返回结果如下:

{
  "args": {},
  "headers": {
    "Accept": "*/*",
    "Host": "www.httpbin.org",
    "User-Agent": "curl/7.64.1",
    "X-Amzn-Trace-Id": "Root=1-60ea8fc0-0701b1494e4680b95889cdb1"
  },
  "origin": "106.45.104.166",
  "url": "https://www.httpbin.org/get"
}

现在就可以在本机上直接使用云主机的代理了!

动态获取IP

我们现在已经执行命令令让云主机切换 IP,也在云主机上搭建好代理服务器了,接下来只需要知道拨号后的 IP 就可以使用代理了。

那么动态获取拨号主机的 IP?怎么维护这些代理?又怎么保证获取的代理一定可用呢?还可能出现下面的问题。

  • 如果我们只有一台拨号云主机,并且设置了定时拨号,那么在拨号的几秒钟,这台云主机提供的代理服务是不可用的。

  • 如果我们不使用定时拨号的方法,而是在爬虫端控制拨号云主机的拨号操作,那么爬虫端还需要定义复杂的逻辑和重连问题,这会带来额外的开销。

综合考虑下来,一个比较好的解决方案如下。

  • 为了不增加爬虫的逻辑开销,无须爬虫端关心拨号云主机的拨号操作,它只要保证爬虫通过某个接口获取的代理是可用的就行,即拨号云主机的代理维护逻辑和爬虫毫无相关。

  • 为了解决一台拨号云主机在拨号时代理不可用的问题,让多台云主机同时提供代理服务,可以将不同云主机的拨号时段错开,当一台云主机正在拨号时,其他云主机顶替提供服务。

  • 为了更方便地维护和使用代理,可以像 9.2 节介绍的代理池一样把这些云主机的代理统一维护起来,使云主机的代理统一存储到一个公共的 Redis 数据库中,可以使用 Redis 的 Hash 存储方式,存好每台云主机和对应代理的映射关系。拨号云主机在拨号前会清空自己的对应代理内容,拨号成功后再将更新代理,这样 Redis 数据库中的代理就一定是实时可用的了。

利用这种思路,我们要做到如下几点。

  • 配置一个可以公网访问的 Redis 数据库,每台云主机都将自己的代理存储到 Redis 数据库的对应位置,由 Redis 数据库维护这些代理。

  • 申请多台拨号云主机,并按照上文所讲内容,在这些主机上配置好 Squid 代理服务,为每台云主机设置定时拨号来更换 IP。

  • 每台云主机在拨号前先删除 Redis 数据库中原来的代理,拨号成功后测试一下代理的可用性,并将最新的代理更新到 Redis 数据库中。

接下来就实操一把。我们使用 Python 语言,在云主机上安装 Python 库:

yum -y install python3

关于自动拨号、连接 Redis 数据库、获取本机代理、设置 Redis 数据库的操作,我已经写好了一个 Python 包并发布到 PyPi 了,大家可以直接使用这个包完成如上操作,这个包叫 adslproxy,可以在云主机上使用 pip3 工具安装:

pip3 install adslproxy

安装完毕后,可以使用 export 命令设置环境变量:

export REDIS_HOST=<REDIS 数据库的地址>
export REDIS_PORT=<REDIS 数据库的端口>
export REDIS_PASSWORD=<REDIS 数据库的密码>
export PROXY_PORT=<拨号云主机配置的代理端口>
export DIAL_BASH=<拨号脚本>
export DIAL_IFNAME=<网卡名称>
export CLIENT_NAME=<云主机的唯一标识>
export DIAL_CYCLE=<拨号间隔>

这里的 REDIS_HOST、REDIS_PORT、REDIS_PASSWORD 是远程 Redis 的连接信息,就不再赘述了。PROXY_PORT 是云主机上代理服务的端口,我们已经设置为 3328。DIAL_BASH 是拨号命令,即 pppoe-stop;pppoe-start,当然对于不同的云主机厂商来说,该脚本的内容也可能不同,以实际为准。DIAL_IFNAME 是拨号云主机上的网卡名称,程序可以通过获取该网卡的信息来获取当前拨号主机的 IP 地址,从之前的操作可以发现,网卡名称是 ppp0,当然这个名称也以实际为准。CLIENT_NAME 是云主机的唯一标识,用来在 Redis 数据库中存储主机和代理的映射,因为我们有多台云主机,所以应该把不同云主机的名称设置的不同的字符串,例如 adsl1、adsl2 等。这里我们的设置如图 9-14 所示。

图 9-14 我们设置的环境变量

设置好环境变量之后,就可以运行 adslproxy send 命令进行拨号了,命令如下:

adslproxy send

运行结果如下:

2021-07-11 15:30:03.062 | INFO | adslproxy.sender.loop:90 - Starting dial...
2021-07-11 15:30:03.063 | INFO | adslproxy.sender.run:99 - Dial started, remove proxy
2021-07-11 15:30:03.063 | INFO | adslproxy.sender.remove_proxy:62 - Removing adsl1...
2021-07-11 15:30:04.065 | INFO | adslproxy.sender.remove_proxy:69 - Removed adsl1 successfully
2021-07-11 15:30:05.373 | INFO | adslproxy.sender.run:111 - Get new IP 106.45.105.3328
2021-07-11 15:30:15.552 | INFO | adslproxy.sender.run:120 - Valid proxy 106.45.105.3328
2021-07-11 15:30:16.501 | INFO | adslproxy.sender.set_proxy:82 - Successfully set proxy
106.45.105.3328
2021-07-11 15:33:36.678 | INFO | adslproxy.sender.loop:90 - Starting dial...
2021-07-11 15:33:36.679 | INFO | adslproxy.sender.run:99 - Dial started, remove proxy
2021-07-11 15:33:36.680 | INFO | adslproxy.sender.remove_proxy:62 - Removing adsl1...
2021-07-11 15:33:37.214 | INFO | adslproxy.sender.remove_proxy:69 - Removed adsl1 successfully
2021-07-11 15:33:38.617 | INFO | adslproxy.sender.run:111 - Get new IP 106.45.105.219
2021-07-11 15:33:48.750 | INFO | adslproxy.sender.run:120 - Valid proxy 106.45.105.219:3328
...

从中可以看到,因为在云主机拨号之后,当前代理就失效了,所以程序在拨号之前会尝试从 Redis 数据库中删除当前云主机的代理。然后开始执行拨号操作,拨号成功之后如果验证代理是可用的,再将该代理存储到 Redis 数据库中。这样循环往复运行,就达到了定时更换IP的效果,同时 Redis 数据库中存储的也是实时可用的代理。

最后,们可以购买多台拨号云主机,并都按前面那样配置,这样就有多个稳定且定时更新的代理可用了,Redis 数据库会实时更新各台云主机的代理,如图 9-15 所示。

图9-15 Redis 数据库中存储的各个代理

图9-15显示的是4台 ADSL 拨号云主机经配置并运行后,Redis 数据库中的内容,其中的代理都是实时可用的。

使用代理

如何使用代理呢?在任意支持公网访问的云主机上连接刚才的 Redis 数据库并搭建一个 API 服务即可。怎么搭建呢?同样可以使用 adslproxy 库,该库也提供 API 服务。

为了方便测试,我们在本机进行测试,安装好 adslproxy 库之后,设置 REDIS 相关的环境变量:

export REDIS_HOST=<REDIS 数据库的地址>
export REDIS_PORT=<REDIS 数据库的端口>
export REDIS_PASSWORD=<REDIS 数据库的密码>

然后运行如下命令启动 adslproxy

2020-07-11 16:31:58.651 | INFO | adslproxy.server.server:serve:68 - API listening on http://0.0.0.0:8425

可以看到 API 服务就运行在 8425 端口,我们打开浏览器即可访问首页,如图 9-16 所示。

其中最重要的就是 random 接口,使用这个接口即可获取 Redis 数据库中的一个随机代理,如图 9-17 所示。

图9-16 访问代理服务的首页 图9-17 获取一个随机代理

经过测试,这个代理的可用性没有问题,这样爬虫就可以使用它爬取数据了。

最后,我们部署一下 API 服务,就可以像代理池一样使用这个 ADSL 代理服务了。每请求一次 API 服务,就可以获取一个实时可用代理,在不同的时间段,这个代理会不同,不仅连接稳定,速度也快,实在是网络爬虫的最佳搭档。

总结

本节我们介绍了 ADSL 拨号代理的搭建过程。通过这种代理,我们可以无限次更换 IP,而且线路非常稳定,爬虫的爬取效果也会好很多。