X開発者プラットフォームへ登録して公式X API v2の利用に必要なキー情報を入手、Python Tweepyライブラリを使ってWordPressのRSSフィードから得た新着記事情報を、自動ポストできるようにします。
X開発者プラットフォームへの登録
Twitter/Xにログインしている状態でX開発者プラットフォームへアクセス、右上のアイコンより開発者ポータルへ。
BasicもProも有償プランには違いないので目をくれず、下にある無償の Sign up for Free Account をクリック。
この手続きをする上で面倒に思っていたのがこれ。 Describe all of your use cases of Twitter's data and API で、APIの使用目的を記述するところ。先人の皆さまたちの例を参考に日本語文をDeepLで英訳、それを修正して貼り付けました。
I am an amateur tech-blogger which covers programming and app development as my hobby.
I would like to use X/Twitter API to send posts to my account on X when I publish new articles on my WordPress site for information dissemination.
When using X/Twitter API, I will ensure that I comply with X’s terms and conditions and all applicable laws and regulations.
送信後、特に審査もなく自動的に開発者ポータルのダッシュボードページへ、リダイレクトされます。
ユーザ認証設定
ダッシュボードに既に存在している、デフォルトプロジェクトAPPの歯車アイコンをクリックします。
ここユーザ認証設定では、アプリ権限をRead and writeに、アプリの種類はWeb App, Automated App or Botを指定、
アプリ情報ではコールバック用のURLと自身のサイトURLが求められるも、今回は特にコールバック処理を伴わないので、それっぽいURLを記しておきます。
フォームを送信すると、 OAuth 2.0 Client ID と OAuth 2.0 Client Secret が表示されるので、これを手元に保存。以降も生成されるこの手のキーやトークンは後から参照できないので、必ず手元に全て控えておきましょう。
キーとトークンの生成
次に、図04の鍵アイコンをクリックして、キーとトークンの設定ページを開きます。
先ほど生成したOAuth 2.0の認証情報は一番下に表示されており、ここではそれ以外の上2つを生成するのですが、 Consumer Keys に、おそらく私が昔利用していたAPI v1当時のキー情報が残っていたので、 Regenerate で再生成することに。
次に Bearer Token を生成、保存します。
最後に Access Token & Secret を生成、保存して、APIアクセスに必要な情報が手元に揃いました。
ちなみに今回手続きした無償プランと他の有償プランとの違いは以下の通り。現時点では、ブログ記事の更新を知らせるツイートを数日に一度、発信する程度なので十分。
Tweepyインストール
前回、feedparserとtwikitを導入したOracle Cloud上のUbuntu 20.04ベースの仮想マシンへ、Tweepyをインストールします。
twikitと異なりTweepyはPython 3.7以降での動作が謳われていますが、今回もPython 3.10環境で進めます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
~$ pip3.10 install tweepy Collecting tweepy Downloading tweepy-4.14.0-py3-none-any.whl.metadata (3.8 kB) Collecting oauthlib<4,>=3.2.0 (from tweepy) Downloading oauthlib-3.2.2-py3-none-any.whl.metadata (7.5 kB) Collecting requests<3,>=2.27.0 (from tweepy) Downloading requests-2.32.3-py3-none-any.whl.metadata (4.6 kB) Collecting requests-oauthlib<2,>=1.2.0 (from tweepy) Downloading requests_oauthlib-1.3.1-py2.py3-none-any.whl.metadata (10 kB) Collecting charset-normalizer<4,>=2 (from requests<3,>=2.27.0->tweepy) Downloading charset_normalizer-3.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.metadata (34 kB) Requirement already satisfied: idna<4,>=2.5 in /usr/lib/python3/dist-packages (from requests<3,>=2.27.0->tweepy) (2.8) Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/lib/python3/dist-packages (from requests<3,>=2.27.0->tweepy) (1.25.8) Requirement already satisfied: certifi>=2017.4.17 in /usr/lib/python3/dist-packages (from requests<3,>=2.27.0->tweepy) (2019.11.28) Installing collected packages: oauthlib, charset-normalizer, requests, requests-oauthlib, tweepy Successfully installed charset-normalizer-3.4.0 oauthlib-3.2.2 requests-2.32.3 requests-oauthlib-1.3.1 tweepy-4.14.0 ~$ pip3.10 list | grep tweepy tweepy 4.14.0 |
Tweepyでツイートポスト
X開発者プラットフォームの手続きで得た総勢7個のキーやトークンのうち、Tweepyで使うのは以下の5個。
- Consumer API Key
- Consumer API Secret
- Bearer Token
- Access Token
- Access Token Secret
一見面倒くさそうですが、TweepyのClientオブジェクト生成時にこれらをまとめて付与すれば良いだけなので、テストツイートも簡単に送れます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
Python 3.10.15 (main, Sep 7 2024, 18:35:33) [GCC 9.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import tweepy >>> >>> c_key = "############################################" >>> c_secret = "############################################" >>> b_token = "############################################" >>> a_token = "############################################" >>> a_secret = "############################################" >>> >>> client = tweepy.Client( ... bearer_token=b_token, ... consumer_key=c_key, ... consumer_secret=c_secret, ... access_token=a_token, ... access_token_secret=a_secret ... ) >>> >>> client.create_tweet(text='tweepy via X-APIv2 TEST') Response(data={'edit_history_tweet_ids': ['1861598822071443673'], 'text': 'tweepy via X-APIv2 TEST', 'id': '1861598822071443673'}, includes={}, errors=[], meta={}) >>> |
ほか、公式ドキュメントに文法のほか、基本的な例がいくつか掲載されています。
feedparserとの連携
早速、WordPress新着記事をRSSフィードから検出してXへポストするスクリプトを、前回のtwikitからTweepyへ入れ換えました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#!/usr/bin/env python3.10 # -*- coding: utf-8 -*- import feedparser, pickle, os, sys, time from datetime import datetime as dt import tweepy ## VARs feedfile = 'lastfeed.bin' lastfeed = {} feedurl = "https://servercan.net/blog/feed/" dtformat = '%a, %d %b %Y %H:%M:%S %z' ## FUNCs def str2date(str): return dt.strptime(str, dtformat) def pickle_rb(file): with open(file, 'rb') as f: return pickle.load(f) def pickle_wb(file, obj): with open(file, 'wb') as f: pickle.dump(obj, f) ## LOAD LAST FEED FILE IF EXISTS print('batch start at '+dt.now().strftime('%Y-%m-%d %H:%M:%S')) if os.path.isfile(feedfile): print('feed file found, loading...') lastfeed = pickle_rb(feedfile) ## GET RSS FEED feed = feedparser.parse(feedurl) if feed.entries == 0: print('feedparser returns err, aborting...') sys.exit(0) ## INITIAL RUN : SAVE LATEST ENTRY ONLY if len(lastfeed) == 0: print('no feed file fould, creating new.') pickle_wb(feedfile, feed.entries[0]) print('feed file created, batch end.') sys.exit(0) ## FEED LOOP lastdate = str2date(lastfeed.published) newentries = [] print('loop entries to check new') for idx,entry in enumerate(feed.entries): if lastdate >= str2date(entry.published): print(str(idx)+' '+entry.published+' : OLD') else: print(str(idx)+' '+entry.published+' : NEW') newentries.append(entry) ## SORT NEW ENTRIES IF MORE THAN 1 if len(newentries) > 1: print('more than 1 entries detected. entries list reversing...') newentries.reverse() elif len(newentries) == 0: print('new post NOT found, batch end.'); sys.exit(0) #DEBUG print('New Entries: '+str(len(newentries))) for idx,entry in enumerate(newentries): print(str(idx)+' '+entry.published) ## TWEEPY c_key = "############################################" c_secret = "############################################" b_token = "############################################" a_token = "############################################" a_secret = "############################################" client = tweepy.Client( bearer_token=b_token, consumer_key=c_key, consumer_secret=c_secret, access_token=a_token, access_token_secret=a_secret ) for idx,entry in enumerate(newentries): #print(str(idx)+' '+entry.published) msg = '[Fun Scripting 2.0]\n' msg += entry.title + '\n' + entry.link print(msg) res = client.create_tweet(text=msg) print(res) pickle_wb(feedfile, entry) time.sleep(5) print('batch end properly at '+dt.now().strftime('%Y-%m-%d %H:%M:%S')) |
RSSフィードから新着記事を見つけてツイートする様子は次の通り。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
batch start at 2024-12-01 13:45:01 feed file found, loading... loop entries to check new 0 Sun, 01 Dec 2024 04:34:52 +0000 : NEW 1 Wed, 27 Nov 2024 04:34:26 +0000 : OLD 2 Sat, 23 Nov 2024 04:34:12 +0000 : OLD 3 Tue, 19 Nov 2024 04:34:09 +0000 : OLD 4 Fri, 15 Nov 2024 04:34:01 +0000 : OLD 5 Mon, 11 Nov 2024 04:49:51 +0000 : OLD 6 Wed, 06 Nov 2024 04:48:28 +0000 : OLD 7 Sat, 02 Nov 2024 04:34:53 +0000 : OLD 8 Tue, 29 Oct 2024 04:34:10 +0000 : OLD 9 Fri, 25 Oct 2024 04:34:02 +0000 : OLD New Entries: 1 0 Sun, 01 Dec 2024 04:34:52 +0000 [Fun Scripting 2.0] Hematバウチャーをオンライン購入してHK MobiデータSIMのサービス更新 https://servercan.net/blog/2024/12/hemat%e3%83%90%e3%82%a6%e3%83%81%e3%83%a3%e3%83%bc%e3%82%92%e3%82%aa%e3%83%b3%e3%83%a9%e3%82%a4%e3%83%b3%e8%b3%bc%e5%85%a5%e3%81%97%e3%81%a6hk-mobi%e3%83%87%e3%83%bc%e3%82%bfsim%e3%81%ae%e3%82%b5/ Response(data={'text': '[Fun Scripting 2.0]\nHematバウチャーをオンライン購入してHK MobiデータSIMのサービス更新\nhttps://t.co/6z3HbV8iS9', 'id': '1863096949051937138', 'edit_history_tweet_ids': ['1863096949051937138']}, includes={}, errors=[], meta={}) batch end properly at 2024-12-01 13:45:07 |
いまいちツイートの成功率が低いtwikitに比べ、やはり公式APIを使うだけあって動作は安定しています。















