🤖

python×seleniumで、Twitterのいいねと自分のツイートをworkflowyっていうメモツールに自動転記する

2023/01/22に公開

workflowyが好きすぎて

どうも。コスパ効率化が大好物な春眠亭あくびと申します。
ノートアプリマニアでして、それなりに知名度のあるアプリは大抵触ってます。
その中でもボクが愛してやまないのが、「workflowy」です。
https://workflowy.com/features/

アウトライナーアプリでして、自由度の高さとUIの緻密さがほんと最高なのです。
特にお気に入りなのが、ディレクトリという概念がないところで、全てのコンテンツをひとつのページで管理します。
メチャクチャ大きな白紙にガンガンアイデアやメモを書いていくような感覚が他にはなく、本当に手放せません。
様々なノートアプリに浮気しまくってきた尻軽男ですが、最後はやっぱりworkflowyに戻ってきます。

でも。
workflowyにはひとつだけ大きな欠点があります。
それは、APIを解放していないことです。
それはつまり、zapierやmakeで自動化ができないと言うことです。

これはかなりやっかい。
他のアプリと連携したい。
自動化したい。
ストレス。
でもworkflowyはAPI化を全然検討してくれません
多分一生API化しないんじゃないかな。

そこで一念発起。
pythonの教本をよみながら手探りで自動化プログラムを日曜大工しました。
自分の備忘録のため、こちらに残しておきます。

※workflowyやメモアプリ好きの人、ぜひつながりたいです。
 twitterのフォローお待ちしています。
https://twitter.com/haruakubi

やりたいこと

A:twitterでいいねしたツイートを、workflowyに自動転記したい

B:自分のツイートを、workflowyに自動転記したい

ざっくりプログラムの解説

  1. 環境はwindows11、python3
  2. WEBブラウザ自動化ツール「selenium」のchrome用webdriverを使ってworkflowyに自動ログイン
  3. pythonのtwitterクライアント「tweepy」を使っていいねの情報を取得
  4. 前回処理済のツイートを対象外にするようフィルタ(txtファイルに前回の最終ツイートのIDを記載しておくことで実現)
  5. seleniumでworkflowyの該当ページに行き、空白行を挿入
  6. 挿入した空白行に、いいねしたツイート/自分のツイートの情報を記載

実装方法

A:twitterでいいねしたツイートを、workflowyに自動転記したい

  1. pythonをインストール。参考→ http://netsu-n.mep.titech.ac.jp/~Kawaguchi/python/install-win/
  2. 「like_main.py」「like_log.txt」という2つの空ファイルを同じフォルダに作成
  3. 「like_main.py」に下記コードを転記
  4. コードの「変数関連」に、自分のIDやパスなどを記入
  5. 必要なライブラリ(selenium、tweepy、emojiなど)をインストール。詳細はコード内に記載
  6. コマンドプロンプトで、 「python like_main.py」で実行。
  7. 自動実行する場合はバッチ化する。 →参考 https://qiita.com/0yan/items/79a95bb9c9153851bd79

コード↓

# 変数関連
## workflowy関連
#### pythonWEBdriverがインストールされているパスを記載。windowsの場合、パスにバックスペースが入る。バックスペースは特殊な文字なので通常だと処理がうまくいかない。その場合r""で囲むようにする。
driverpath = r"C:\Users\wades\PycharmProjects\pythonProject\chromedriver_win32\chromedriver.exe"
#### workflowyのID/PWを記載
login_id = "YOURIDPW"
login_pw = "YOURIDPW"
#### workflowyで追記したページの固有URLを記載
page = "YOURIDPW"
## twitter関連
#### twitterAPIは英語での申請等が必要。参照→ https://www.itti.jp/web-direction/how-to-apply-for-twitter-api/#chapter-2
consumer_key = "YOURIDPW"
consumer_secret = "YOURIDPW"
access_token = "YOURIDPW"
access_token_secret = "YOURIDPW"
#### like_log.txtのパスを記載
path_w = r"YOURPATH\like_log.txt"

# ライブラリのインポート
#### WEBブラウザ自動化ツール「selenium」のインポート(未インストールなら「pip install selenium」と「https://sites.google.com/chromium.org/driver/」でのダウンロードとドライバインストール必須)
from selenium import webdriver
from selenium.webdriver.chrome import service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
#### pythonのtwitterクライアント「tweepy」のインポート(未インストールなら「pip install tweepy」必須)
import tweepy
#### seleniumが絵文字を扱えないため、絵文字削除のためのライブラリ「emoji」をインポート(未インストールなら「pip install emoji」必須)
import emoji
#### 待機処理をするためのライブラリ
import time

# tweepy前処理
## tweept認証
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
## 転記済ツイートの除外
#### 前回処理したツイートのIDをlike_log.txtの最終行から抽出する
with open(path_w) as f:
    last_line = f.readlines()[-1]
#### api.get_favorites() でTwitterのいいねを取得。オプションで「since_id」を指定することで、このIDより後のツイートのみフィルター出来る(つまり最新のいいねのみに抽出)
public_tweets = api.get_favorites(since_id = last_line)

# selenium前処理
#### ChromeDriverのパスを変数に設定
CHROMEDRIVER = driverpath
#### ChromeDriverのstartとstopを制御するServiceオブジェクトを介してパスを渡す
chrome_service = service.Service(executable_path=CHROMEDRIVER)
#### Chromeを起動
driver = webdriver.Chrome(service=chrome_service)

# seleniumを使用し、folkflowyに自動ログイン
#### 指定したURLに遷移する
driver.get("https://workflowy.com/login")
#### name要素からemailと書いてあるものを取得し、element変数に入れる
element = driver.find_element(By.NAME, "email")
#### ログインID項目にIDを入れる
element.send_keys(login_id)
#### Enterキーを押下
element.send_keys(Keys.ENTER)
#### PW項目表示待ち
time.sleep(3)
#### name要素からpasswordと書いてあるものを取得し、element変数に入れる
element = driver.find_element(By.NAME, "password")
#### パスワード項目にPWを入れる
element.send_keys(login_pw)
#### Enterキーを押下
element.send_keys(Keys.ENTER)
#### ログイン待ち(wokflowyは起動に少し時間がかかるので、少し待ち時間多め)
time.sleep(30)
#### 自動化する対象のページへ移動
driver.get(page)
#### 遷移待ち
time.sleep(30)

# workflowyにいいねしたツイートを自動記載
## ツイートのfor文処理
#### 最初のツイートだけIDを取得してlike_log.txtに書き混む必要あり 最初のツイートというフラグを firstLoop = True で付ける
firstLoop = True
#### ツイートをfor文で取り出す
for tweet in public_tweets:
    #### 文字列の中に変数を入れるときは、f"{変数}文字列" という構文にする
    #### tweet.user.nameでいいねしたツイート主のユーザ名を取得。tweet.textでそのツイート本文を取得
    contents = f"{tweet.user.name}{tweet.text}"
    #### 絵文字をseleniumは扱えないようなので、emojiをドロップする処理を入れる
    contents = emoji.replace_emoji(contents)
    #### 最初のツイートというフラグ firstLoop が Trueの場合というif文を入れる
    if firstLoop:
        #### 最初のツイートのIDをlike_log.txtに追記。これにより、処理済みのツイートを転記対象にしない
        with open(path_w, mode='a', encoding='UTF-8') as f:
            f.write(f"{tweet.id}\n")
        f.close()
        #### フラグをfalseにして、以降はlike_log.txtに転記しないようにする
        firstLoop = False
## seleniumでworkflowyに追記
        #### 一番上段のバレットを選択
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[1]/div[2]/span')
        #### クリック
        element.click()
        time.sleep(1)
        #### ここは注意。クリック処理をすると、文字列の中間にカーソルが行く
        #### 文字列の一番後ろにカーソルを移動してエンターして改行を入れたい。ボクの場合「memo」という文字列なので、中間にカーソルが行くと、右に2回カーソルを移動したい
        #### よって、Keys.RIGHTの処理を2回入れている。ここは文字列によってチューニング必要
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        #### エンターを実行し、空白行を挿入
        element.send_keys(Keys.ENTER)
        time.sleep(1)
        #### 先ほど挿入した空白行を指定
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[3]/div[1]/div[1]/div[2]')
        time.sleep(1)
        #### コンテンツを空白行に挿入
        element.send_keys(contents)
        time.sleep(1)
    else:
## IDをlike_log/txtに挿入する以外は、上と処理は同じ
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[1]/div[2]/span')
        element.click()
        time.sleep(1)
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        element.send_keys(Keys.ENTER)
        time.sleep(1)
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[3]/div[1]/div[1]/div[2]')
        time.sleep(1)
        element.send_keys(contents)
        time.sleep(1)

B:自分のツイートを、workflowyに自動転記したい

  1. pythonをインストール。参考→ http://netsu-n.mep.titech.ac.jp/~Kawaguchi/python/install-win/
  2. 「mine_main.py」「mine_log.txt」という2つの空ファイルを同じフォルダに作成
  3. 「mine_main.py」に下記コードを転記
  4. コードの「変数関連」に、自分のIDやパスなどを記入
  5. 必要なライブラリ(selenium、tweepy、emojiなど)をインストール。詳細はコード内に記載
  6. コマンドプロンプトで、 「python mine_main.py」で実行。
  7. 自動実行する場合はバッチ化する。 →参考 https://qiita.com/0yan/items/79a95bb9c9153851bd79

コード↓

# 変数関連
## workflowy関連
#### pythonWEBdriverがインストールされているパスを記載。windowsの場合、パスにバックスペースが入る。バックスペースは特殊な文字なので通常だと処理がうまくいかない。その場合r""で囲むようにする。
driverpath = r"C:\Users\wades\PycharmProjects\pythonProject\chromedriver_win32\chromedriver.exe"
#### workflowyのID/PWを記載
login_id = "YOURIDPW"
login_pw = "YOURIDPW"
#### workflowyで追記したページの固有URLを記載
page = "YOURIDPW"
## twitter関連
#### twitterAPIは英語での申請等が必要。参照→ https://www.itti.jp/web-direction/how-to-apply-for-twitter-api/#chapter-2
consumer_key = "YOURIDPW"
consumer_secret = "YOURIDPW"
access_token = "YOURIDPW"
access_token_secret = "YOURIDPW"
#### mine_log.txtのパスを記載
path_w = r"YOURPATH\mine_log.txt"

# ライブラリのインポート
#### WEBブラウザ自動化ツール「selenium」のインポート(未インストールなら「pip install selenium」と「https://sites.google.com/chromium.org/driver/」でのダウンロードとドライバインストール必須)
from selenium import webdriver
from selenium.webdriver.chrome import service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
#### pythonのtwitterクライアント「tweepy」のインポート(未インストールなら「pip install tweepy」必須)
import tweepy
#### seleniumが絵文字を扱えないため、絵文字削除のためのライブラリ「emoji」をインポート(未インストールなら「pip install emoji」必須)
import emoji
#### 待機処理をするためのライブラリ
import time

# tweepy前処理
## tweept認証
auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)
## 転記済ツイートの除外
#### 前回処理したツイートのIDをlike_log.txtの最終行から抽出する
with open(path_w) as f:
    last_line = f.readlines()[-1]
#### api.user_timeline() で自分のツイートを取得。オプションで「since_id」を指定することで、このIDより後のツイートのみフィルター出来る(つまり最新のいいねのみに抽出)
public_tweets = api.user_timeline(since_id = last_line)

# selenium前処理
#### ChromeDriverのパスを変数に設定
CHROMEDRIVER = driverpath
#### ChromeDriverのstartとstopを制御するServiceオブジェクトを介してパスを渡す
chrome_service = service.Service(executable_path=CHROMEDRIVER)
#### Chromeを起動
driver = webdriver.Chrome(service=chrome_service)

# seleniumを使用し、folkflowyに自動ログイン
#### 指定したURLに遷移する
driver.get("https://workflowy.com/login")
#### name要素からemailと書いてあるものを取得し、element変数に入れる
element = driver.find_element(By.NAME, "email")
#### ログインID項目にIDを入れる
element.send_keys(login_id)
#### Enterキーを押下
element.send_keys(Keys.ENTER)
#### PW項目表示待ち
time.sleep(3)
#### name要素からpasswordと書いてあるものを取得し、element変数に入れる
element = driver.find_element(By.NAME, "password")
#### パスワード項目にPWを入れる
element.send_keys(login_pw)
#### Enterキーを押下
element.send_keys(Keys.ENTER)
#### ログイン待ち(wokflowyは起動に少し時間がかかるので、少し待ち時間多め)
time.sleep(30)
#### 自動化する対象のページへ移動
driver.get(page)
#### 遷移待ち
time.sleep(30)

# workflowyにいいねしたツイートを自動記載
## ツイートのfor文処理
#### 最初のツイートだけIDを取得してlike_log.txtに書き混む必要あり 最初のツイートというフラグを firstLoop = True で付ける
firstLoop = True
#### ツイートをfor文で取り出す
for tweet in public_tweets:
    #### 文字列の中に変数を入れるときは、f"{変数}文字列" という構文にする
    #### tweet.user.nameでいいねしたツイート主のユーザ名を取得。tweet.textでそのツイート本文を取得
    contents = f"{tweet.user.name} : {tweet.text}"
    #### 絵文字をseleniumは扱えないようなので、emojiをドロップする処理を入れる
    contents = emoji.replace_emoji(contents)
    #### 最初のツイートというフラグ firstLoop が Trueの場合というif文を入れる
    if firstLoop:
        #### 最初のツイートのIDをlike_log.txtに追記。これにより、処理済みのツイートを転記対象にしない
        with open(path_w, mode='a', encoding='UTF-8') as f:
            f.write(f"{tweet.id}\n")
        f.close()
        #### フラグをfalseにして、以降はlike_log.txtに転記しないようにする
        firstLoop = False
## seleniumでworkflowyに追記
        #### 一番上段のバレットを選択
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[1]/div[2]/span')
        #### クリック
        element.click()
        time.sleep(1)
        #### ここは注意。クリック処理をすると、文字列の中間にカーソルが行く
        #### 文字列の一番後ろにカーソルを移動してエンターして改行を入れたい。ボクの場合「memo」という文字列なので、中間にカーソルが行くと、右に2回カーソルを移動したい
        #### よって、Keys.RIGHTの処理を2回入れている。ここは文字列によってチューニング必要
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        #### エンターを実行し、空白行を挿入
        element.send_keys(Keys.ENTER)
        time.sleep(1)
        #### 先ほど挿入した空白行を指定
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[3]/div[1]/div[1]/div[2]')
        time.sleep(1)
        #### コンテンツを空白行に挿入
        element.send_keys(contents)
        time.sleep(1)
    else:
## IDをlike_log/txtに挿入する以外は、上と処理は同じ
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[1]/div[2]/span')
        element.click()
        time.sleep(1)
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        element.send_keys(Keys.RIGHT)
        time.sleep(1)
        element.send_keys(Keys.ENTER)
        time.sleep(1)
        element = driver.find_element(By.XPATH, '//*[@id="app"]/div[3]/div/div[1]/div[3]/div[1]/div[1]/div[2]')
        time.sleep(1)
        element.send_keys(contents)
        time.sleep(1)

Discussion