做这个 bot 的起因是前几天在微博上看到了推主藤原麻里菜(@togenkyoo)的作品:只要监测到有新推文提到「別れました」,灯泡就会亮。感觉很有意思,而且发现原 po 是用 IFTTT 实现的,因为我之前也玩过一段时间 IFTTT,就想自己也弄一个玩玩。

藤原麻里菜原推

最初的想法是从微博上爬取相关内容,然后在微博上开一个 bot 账号实时汇总统计。但是,由于微博不开放搜索 API,再加上各种反爬虫策略,导致我们很难获取到想要的数据。于是放弃微博,继续从 Twitter 获取数据。

原理分析

首先打开 IFTTT,在 「Twitter」 Service 里看到了 「New tweet from search」 这个 Trigger,这个就是藤原麻里菜所用的 Service 了,其官方简介为:

This Trigger fires every time a new tweet matches your search query.

Twitter Trigger

每当有新的推文与您的搜索查询匹配时,就会触发此触发器,完美地符合了需求。有个这个,也就不用费力气去写 Twitter 的爬虫了。

接下来是如何用匹配到的 Twitter 数据来自动发微博,实现 bot 的功能,也就是「If This Then That」中的 「That」。虽然 IFTTT 中是有 「Sina Weibo」这个 Service,而且其曾经确实可以用来发微博,但是大概是新浪为了限制第三方微博客户端,在近几年关闭了大量 API,其中就包括发微博的相关 API,导致 「Publish a new post」这个 Action 失效。

但是查阅微博现有的 API 后发现,其还有一个「statuses/share」接口,作用是第三方分享到微博,虽然相比直接发微博来说有一定的限制,但是对于我们的 bot 来说已经足够了。

statuses/share 接口

有了发微博的 API,但是如何与 IFTTT 的 Trigger 对接呢?经过简单的搜索筛选,我在 IFTTT 的 Service 中找到了 「Webhooks」,其官方简介为:

Integrate other services on IFTTT with your DIY projects. You can create Applets that work with any device or app that can make or receive a web request.

划重点,that can make or receive a web request,这个可玩性一下子就提高了不少,我们可以通过它向自己的服务器发送请求来实现各种操作。

Webhooks

好了现在整理一下思路,首先 IFTTT 在 Twitter 上搜索相关推文,然后将推文信息发送给自己的服务器,再由服务器汇总整理最后发微博。

具体实现

首先在 IFTTT 上创建一个新 Applet,「This」也就是 Trigger 选择 「Twitter」 Service 中的 「New tweet from search」,打开后其中的 Search for 也就是搜索的关键词,这里设置为「別れました」,点击 「Create trigger」。

Create Trigger

之后是「That」部分也就 Trigger 触发后执行的 Action,选择 「Webhooks」中的「Make a web request」。但是目前服务器上还没有任何接口来接受请求,所以还需要在服务器上搭建一个简单的 RESTful API。

Create action

因为最近正好在学习 Flask,所以我就使用它来进行搭建。

from flask import Flask, request, abort, jsonify
import os
import csv

app = Flask(__name__)

lang_list = ('zh', 'jp', 'en')

@app.route('/something_you_want', methods=['POST'])
def new_tweet():
    print(request.json)

    if not request.json or 'lang' not in request.json or 'link' not in request.json or 'time' not in request.json:
        return jsonify({'status': 'error'}), 500
    else:
        result = {
            'status': 'success',
            'lang': request.json['lang'],
            'link': request.json['link'],
            'time': request.json['time'],
        }
        text = [result['lang'], result['time'], result['link']]

        if(request.json['lang'] in lang_list):
            lang_file = result_dir+request.json['lang']+'.csv'

            with open(result_dir+'temp.csv', 'a') as csvfile:
                writer = csv.writer(csvfile)
                writer.writerow(text)
        else:
            lang_file = result_dir+'other.csv'

        with open(lang_file, 'a') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow(text)

    return jsonify(result), 200

if __name__ == '__main__':
    result_dir = app.root_path+'/result/'
    if not os.path.exists(result_dir):
        os.makedirs(result_dir)

    app.run(debug=True)

@app.route 中为 API 的地址,这里 request method 为 POST

我考虑以一个 json 传入数据,其中 lang link time 分别代表推文的语言(因为我准备同时进行「別れました」、「broke up」、「分手了」三个关键词的搜索,所以区分一下语言)、推文链接以及发送时间。当服务器收到一个请求,会根据语言区分储存在不同文件中,并在 temp.csv 保存一份汇总信息,这个后面会用到。

API 完成后开始运行,就可以来继续刚才 IFTTT 的配置了,「URL」就是 API 的请求地址,「Method」为 POST,「Content Type」选择 application/json,最后的 「body」,也就是 json 的内容按照如下格式:

{ "lang" : "jp", "time" : "{{CreatedAt}}", "link" : " {{LinkToTweet}}" }

lang 这里直接写的 jp,因为前面 Trigger 中的关键词为「別れました」,之后还会再创建两个相同的任务替换关键词来实现不同语言的获取。其中的 {{CreatedAt}}{LinkToTweet}} 是从 Trigger 传入的变量(ingredient),分别代表推文的时间及链接。完成之后保存并启用,到这里推文的获取及保存就完成了,接下来就是汇总信息并定时发送微博。

为了使用微博的 API,我们首先要在微博开放平台申请注册一个应用,这里我选择了网页应用。

微博应用

创建成功之后其实就可以使用 API 了,但是由于应用还处于测试状态,会有一部分限制,首先是发送的微博来源会显示为「未经审核的应用」,其次是应用只能在后台添加测试账户进行授权而且授权有效期非常短,并且 API 调用次数会受到限制。

完善应用的基本信息,注意其中的安全域名,我们使用的「statuses/share」接口有如下规定:

用户分享到微博的文本内容,...,文本中必须包含至少一个第三方分享到微博的网页URL,且该URL只能是该第三方(调用方)绑定域下的URL链接。

也就是说我们通过该接口发送的微博中,一定要带上这里的安全域名,而且这里的域名,还必须是 .com.org 等传统域名,我最开始使用本博客的 .moe 就一直报错,在这里还要感谢 Jimmy Tian 大佬借给我的域名。

应用基本信息完善之后,就可以提交审核了,审核的速度还是非常快的,我是在上课的时候提交的审核(好孩子不要学我上课摸鱼),不到一节课的功夫就提醒我审核通过了。现在,我们通过该应用发送的微博,来源就会显示为应用的名称。

发送微博的脚本,Python 有现成的 SDK 「Weibopy」可以使用,安装也非常简单,直接 pip3 install weibopy 即可。

首先要通过微博的 OAuth2.0 认证获取 access token。先在微博开放平台 - 应用信息 - 高级信息 - OAuth2.0 授权设置 里填写回调 URL,这里直接用微博提供的默认 URL: https://api.weibo.com/oauth2/default.html

from weibopy import WeiboOauth2

client_id="xxx",
client_secret="xxx"
redirect_url="http://"
client = WeiboOauth2(client_id,client_secret,redirect_url)

authorize_url = client.authorize_url

其中 client_id 是应用的 「App Key」,client_secret 是「App Secret」,两者均可在应用信息中查看,而 redirect_url 是上面填写的回调地址。

然后我们就可以得到应用的认证地址,打开并登录,将 URL 复制下来,得到其中的 code

client.auth_access(auth_code)

{
   "access_token": "ACCESS_TOKEN",
   "expires_in": 1234,
    "remind_in":"798114",
    "uid":"12341234"
}

发送该 code,我们就拿到了该账号的 access token,现在就可以开始发微博了。

from weibopy import WeiboClient

access_token = "ACCESS_TOKEN"
client = WeiboClient(access_token)
safe_domain = "SAFE_DOMAIN"
data = {
    'status': text+safe_domain
}

result = client.post(suffix="statuses/share.json", data=data)

注意发送的内容中一定要包含之前应用基本信息中填写的安全域名。

最开始我一直在想怎么在脚本里控制定时发微博,后来转念一想,脚本只用来发微博,定时控制我直接用 crontab 不就完了...

另外之前在分语言保存结果的时候,还保存了一份汇总的文件 temp.csv,在每次执行发微博操作的时候从该文件读取信息然后删除该文件,下次收到新请求之后又会重新创建。这样就可以在每次发微博的时候统计距上次发微博之间的信息。

微博

全部配置成功之后,一个简单的 bot 就算完成了,期待其在圣诞节正式上线~

Last modification:December 23rd, 2019 at 12:07 am
If you think my article is useful to you, please feel free to appreciate