查看原文
其他

作为一只爬虫,如何科学有效地处理短信验证码?

The following article is from 进击的Coder Author 崔庆才

之前我们了解了一些验证码的处理流程,比如图形验证码、滑块验证码、点选验证码等等,但是这些验证码都有一种共同的特点,那就是这些验证码的处理流程通常只需要在 PC 上完成即可,比如图形验证码如果在 PC 上出现,那么在 PC 上直接验证通过就好了,所有的识别、验证输入的流程都是在 PC 上完成的。

但还有一种验证码和此种情况不同,那就是手机验证码,比如 PC 上需要输入手机号,然后短信验证码需要发到手机上,然后再在 PC 上把收到的验证码输入即可通过验证。

那遇到这种情况,我们如何才能将这个流程给自动化呢?

验证码收发

通常来说,我们的自动化脚本会运行在 PC 上,比如打开一个网页,然后模拟输入手机号,然后点击获取验证码,接下来就需要输入验证码了。打开页面,输入手机号、点击获取验证码等流程我们可以非常容易地实现自动化,但是验证码被发送到手机上了,我们怎么能把它转到 PC 上呢?

为了自动化整个验证码收发的流程,这时候我们想要完成的就是——当手机收到一条短信的时候,它能够自动将短信转发到某处,比如一台远程服务器上或者直接发到 PC 上,在 PC 上我们可以通过一些方法再把短信获取下来并提取验证码的内容,然后自动化填充验证码即可。

那这里关键的部分其实就是怎样完成这两个步骤:

  • 如何监听手机收到了短信
  • 如何将手机短信转发到想要的位置

这两个步骤缺一不可,而且都需要在手机上完成。

解决思路自然很简单了,我们以 Android 手机为例,如果有 Android 开发经验的话,其实这两个功能实现起来还是蛮简单的。

注意:这里我们仅仅简单介绍基本的思路,不会完全详细展开介绍具体的代码实现,感兴趣的话可以自行尝试。

首先如何监听手机收到了短信呢?

在 Android 开发中,整体就分为三个必要环节:

  • 注册读取短信的权限:在一个 Android App 中,读取短信是需要特定的权限的,所以我们需要在 Andriod App 的 AndroidManifest.xml 中将读取短信的权限配置好,比如接收短信的权限配置如下:

    <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
  • 注册广播事件:Android 有一个基本组件叫做 BroadcastReceiver,也就是广播接收者的意思,我们可以用它来监听来自系统的各种事件广播,比如系统电量不足的广播、系统来电的广播,当然系统收到短信的广播也就不在话下了。所以这就类似我们注册一个监听器,用来监听系统收到短信的事件。

    比如这里我们可以同样在  AndroidManifest.xml 里面注册一个 BroadcastReceiver,叫做 SmsReciver:

    <receiver android:name=".receive.SmsReciver">
        <intent-filter android:priority="999">
            <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
        </intent-filter>
    </receiver>
  • 实现短信广播接收:这里就需要我们真正实现短信接收的逻辑了,这里只需要实现一个 SmsReceiver 类来继承一个 BroadcastReceiver 然后实现其 onReceive 方法即可,其中 intent 参数里面便包含了我们想要的短信息内容,实现如下:

    public class SmsReciver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle bundle = intent.getExtras();
            SmsMessage msg = null;
            if (null != bundle) {
                Object[] smsObj = (Object[]) bundle.get("pdus");
                for (Object object : smsObj) {
                    msg = SmsMessage.createFromPdu((byte[]) object);
                    Log.e("短信号码""" + msg.getOriginatingAddress());
                    Log.e("短信内容""" + msg.getDisplayMessageBody());
                    Log.e("短信时间""" + msg.getTimestampMillis());
                }
            }
        }

如此一来,我们便实现了短信的接收。

短信收到之后,发送自然也就很简单了,比如服务器提供一个 API,我们通过请求该 API 即可实现数据的发送,这个通过 Android 的一些 HTTP 请求库就可以实现,比如 OkHttp 等构造一个 HTTP 请求即可,这里就不再赘述了。

不过总的来说,整个流程下来其实还需要花费一些开发成本的,对于如此常用的功能,有没有现成的解决方案呢?自然是有的。我们可以借助于于一些开源实现,我们就没必要重复造轮子了。

这里我们就介绍一个开源软件,叫做 SmsForwarder,中文翻译过来叫做短信转发器,其 GitHub 仓库地址为:https://github.com/pppscn/SmsForwarder。

它的基本流程架构图如下:

架构图非常清晰,SmsForwarder 可以监听监听收到短信的事件,获取到短信的来源号码、接受卡槽、短信内容、接收时间等内容,然后将其通过一定的规则转发出去,支持转发到邮箱、微信群机器人、企业微信、Telegram 机器人、Webhook 等。

比如我们可以配置类似这样的规则,如图所示:

转发规则

比如当手机号符合一定的规则就转发到 QQ 邮箱,比如内容包含“报警”就转发到阿里企业邮箱,比如内容开头是“测试”就发动给叫做 TSMS 的 Webhook。

其中QQ邮箱、阿里企业邮箱都是我们已经配置好的发送方,都属于邮箱类型,TSMS 也是一种发送方,属于 Webhook 类型,如图所示:

发送方

我们也可以点击添加发送方按钮来添加对应的发送方,比如添加邮箱的发送方,我们可以设置 SMTP 配置下发件邮箱、SMTP 服务器、SMTP 端口、授权密码等内容:

添加/编辑发送方邮箱

设置 Webhook 我们可以选择是 GET 还是 POST 请求,然后填入对应的 URL、密钥等内容:

添加/编辑发送方网页通知

设置转发规则页面如图所示:

支持正则匹配规则 & 支持卡槽匹配规则

比如这里我们可以选择匹配卡槽、匹配的字段、匹配的模式,还可以配置正则来设置匹配的值,这里就配置了尾号是 4566 的手机号来执行一定的发送操作,收到的短信会发送到钉钉这个发送方。

实战演示

比如这里我们来尝试下,这里我们用 Flask 写一个 API,实现如下:

from flask import Flask, request, jsonify
from loguru import logger

app = Flask(__name__)


@app.route('/sms', methods=['POST'])
def receive():
    sms_content = request.form.get('content')
    logger.debug(f'received {sms_content}')
    # parse content and save to db or mq
    return jsonify(status='success')


if __name__ == '__main__':
    app.run(debug=True)

代码很简单,这里设置了一个路由,接收 POST 请求,然后读取了 Request 表单的内容,其中 content 就是短信的详情内容,然后将其打印出来。

我们将代码保存为 server.py,然后将其运行起来:

python3 server.py

运行结果输出如下:

 * Debug mode: on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 269-657-055

为了方便测试,我们可以用 Ngrok 将该服务暴露到公网:

ngrok http 5000

注意:Ngrok 可以方便地将任何非公网的服务暴露到公网访问,并配置特定的临时二级域名,但一个域名有时长限制,所以通常仅供测试使用。试用前请先安装 Ngrok,具体可以参考 https://ngrok.com/。

运行之后,可以看到输入结果如下:

Session Status                online                                                                                                   
Session Expires               1 hour, 59 minutes                                                                                       
Update                        update available (version 2.3.40, Ctrl-U to update)                                                      
Version                       2.3.35                                                                                                   
Region                        United States (us)                                                                                       
Web Interface                 http://127.0.0.1:4040                                                                                    
Forwarding                    http://1259539cb974.ngrok.io -> http://localhost:5000                                                    
Forwarding                    https://1259539cb974.ngrok.io -> http://localhost:5000                                                   
                                                                                                                                       
Connections                   ttl     opn     rt1     rt5     p50     p90                                                              
                              9       0       0.00    0.00    0.00    0.00

这里我们可以看到 Ngrok 为我们配置了一个公网地址,比如访问 https://1259539cb974.ngrok.io 即相当于访问了我们本地的 http://localhost:5000 服务,这样手机上只需要配置这个地址即可将数据发送到 PC 了。

接下来我们手机上打开 SmsForder,添加一个 Webhook 类型的发送方,配置如下:

这里 Server 的地址我们就直接设置了刚才 Ngrok 提供的公网地址了,记得 URL 路径后面加上 sms。

接着我们添加一个转发规则:

这里我们设置了内容匹配规则,比如匹配到内容开头为测试的时候,那就将短信内容转发到 Webhook 这个发送方,即发送到我们刚刚搭建的 Flask 服务器上。

OK,配置完成之后,然后我们给该手机尝试发送一个验证码,内容如下:

测试验证码593722,一分钟有效。

这时候就可以发现刚才的 Flask 服务器接收结果是这样的:

received +8617xxxxxxxx
测试验证码593722,一分钟有效。
SIM2_China Unicom_
2021-03-27 18:47:54
SM-G9860

可以看到刚才验证码的内容就成功由手机发送到 PC 了,接着我们便可以对此消息进行解析和处理,然后存入数据库或者消息队列即可。爬虫一端监听消息队列或者数据库改动即可将其填写并进行一些模拟登录操作了,该步骤就不再赘述了。

批量收发

当然以上只针对于一部手机的情况,如果我们有大量的手机和手机卡,我们可以实现手机的群控处理,比如统一安装短信接收软件,统一配置相同的转发规则,从而实现大量手机号验证码的接收和处理。

比如一个群控系统就是这样的:

卡池

当然还有更专业的解决方案,比如有专业的手机卡池,配合以专业的软件设备实现短信的监听。

比如如下的设备支持插 128 张 SIM 卡,就可以实现同时监听 128 个手机号的验证码,如图所示:

具体的技术这里不再阐述,详细可以自行查询相关的设备供应商。

接码平台

当然如上的方案成本还是比较高的,而且这些方案其实已经不限于简单接收短信验证码了,比如手机群控系统一般都会做手机群控爬虫,而卡池也可以用来做 4G/5G 蜂窝代理,如果仅仅做短信收发是可以的,但未免有些浪费了。

如果我们不想耗费过多成本想实现短信验证码的自动化,还有一种方案就是接码平台,其基本思路是这样的:

  • 平台会维护大量的手机号,并可能开放一些 API 或者提供网页供我们调用来获取手机号和查看短信的内容。
  • 我们调用 API 或者爬取网页获取手机号,然后在对应的站点输入该手机号来获取验证码。
  • 通过调用 API 或者爬取网页获取对应手机号短信的内容,并交由爬虫处理。

具体的操作步骤这里就不再详细阐述了,这里简单列几个接码平台:

  • 番薯云:https://guanfangdiping.com/
  • 云际云短信:https://yunjisms.xyz/
  • 接码号:https://jiemahao.com/
  • KaKa 接码:http://www.kakasms.com/

由于接码平台管控比较严格,所以可能随时不可用,请自行搜集对应的平台进行使用。

- EOF -

推荐阅读  点击标题可跳转

1、我去!爬虫遇到字体反爬,哭了!

2、Scrapy 源码剖析(4):Scrapy 如何完成抓取任务?

3、SQL 查找是否"存在",别再 count 了!


觉得本文对你有帮助?请分享给更多人

推荐关注「Python开发者」,提升Python技能

点赞和在看就是最大的支持❤️

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存