※おまけが本編です
本記事の概要
こんな感じのイケてる絵文字がSlackに欲しいなあ~と思ったら、本記事を読もう!
→ Emoji Kitchen
本記事ではGitHub上に公開されているEmoji Kitchenのサンプルコードをローカルで実行し、これをSeleniumで操作して絵文字を組み合わせた画像ファイルをダウンロードし、Slackで検索できるようファイル名を絵文字の名前に変更します。その後、その画像ファイルを手作業でSlackワークスペースに登録します。
ソースコード
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
import requests
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
# レスポンシブするまで画面の横幅を狭める
driver.set_window_size(650, 768)
SAVE_DIRECTORY=r'./emoji_kitchen'
driver.get("http://localhost:5173")
driver.implicitly_wait(15)
actions = ActionChains(driver)
# フローティングボタンのせいでボタンが押せないよエラーが出るので、
# フローティングボタンを消す
floatingButton = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > button")
driver.execute_script("arguments[0].style.display='none';", floatingButton)
button1 = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.css-b27thi-MuiPaper-root > div > div.MuiGrid2-root.MuiGrid2-container.MuiGrid2-direction-xs-row.MuiGrid2-spacing-xs-2.css-s356kp-MuiGrid2-root > div:nth-child(1)")
button1img = button1.find_element(By.CSS_SELECTOR, "img")
button1.click()
cat_element = driver.find_element(By.CSS_SELECTOR, 'div.MuiBox-root img[alt="cat"]')
cat_element.click()
button2 = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.css-b27thi-MuiPaper-root > div > div.MuiGrid2-root.MuiGrid2-container.MuiGrid2-direction-xs-row.MuiGrid2-spacing-xs-2.css-s356kp-MuiGrid2-root > div:nth-child(3)")
button2img = button2.find_element(By.CSS_SELECTOR, "img")
button2.click()
button3 = driver.find_element(By.CSS_SELECTOR, "#root > div > div.MuiContainer-root.MuiContainer-maxWidthXl.css-15f64v6-MuiContainer-root > div > div.MuiPaper-root.MuiPaper-elevation.MuiPaper-rounded.MuiPaper-elevation1.css-b27thi-MuiPaper-root > div > div.MuiGrid2-root.MuiGrid2-container.MuiGrid2-direction-xs-row.MuiGrid2-spacing-xs-2.css-s356kp-MuiGrid2-root > div:nth-child(5)")
button3img = button3.find_element(By.CSS_SELECTOR, "img")
for element in driver.find_elements(By.CLASS_NAME, "css-xdcm7u > div"):
if element.get_attribute("class") == "css-1yyqevv-MuiImageListItem-root":
continue
actions.move_to_element(element)
actions.perform()
element.click()
time.sleep(0.1)
src = button3img.get_attribute('src')
filename = src.split('/')[-1].split('?')[0]
with open(f'{SAVE_DIRECTORY}/{filename}', 'wb') as file:
response = requests.get(src)
file.write(response.content)
with open(f'{SAVE_DIRECTORY}/filename_to_alt.txt', 'a') as file:
file.write(f'{filename}\t{button1img.get_attribute("alt")}\t{button2img.get_attribute("alt")}\t{button3img.get_attribute("alt")}\n')
print('finished. Press any key to exit.')
input()
driver.quit()
import os
SRC_DIRECTORY=r'./emoji_kitchen'
SAVE_DIRECTORY=r'./emoji_kitchen_output'
os.makedirs(SAVE_DIRECTORY, exist_ok=True)
new_file_name = {}
with open(f'{SRC_DIRECTORY}/filename_to_alt.txt', 'r') as f:
for line in f:
parts = line.strip().split('\t')
new_file_name[parts[0]] = f'{parts[1]}_{parts[2]}.png'
for root, dirs, files in os.walk(SRC_DIRECTORY):
for file in files:
if not file.endswith(".png"):
continue
src_path = os.path.join(root, file)
dst_path = os.path.join(SAVE_DIRECTORY, new_file_name[file])
with open(src_path, 'rb') as src_file:
with open(dst_path, 'wb') as dst_file:
dst_file.write(src_file.read())
実施手順
画像の取り込み
Emoji Kitchenのページに、GitHubへのリンクが張られている。
emoji-kitchen
これをgit cloneかDownload ZIPでダウンロードし、解凍先へchange directoryののち以下コマンドを実行
curl -L --compressed https://raw.githubusercontent.com/xsalazar/emoji-kitchen-backend/main/app/metadata.json -o src/Components/metadata.json
npm install && npm start
このコマンドにより、localhost:5173でEmoji KitchenのページのWebサーバが起動する。
これをSeleniumでスクレイピングする。
emoji_kitchen.py
のあるフォルダへchange directoryののち、以下コマンドを実行する。
pip install selenium
pip install webdriver_manager
python ./emoji_kitchen.py
Seleum制御下のChromeが起動し、ぽちぽち押しながら./emoji_kitchenディレクトリへ画像が保存される。
取り込んだ絵文字の名前
併せて./emoji_kitchen/filename_to_alt.txt
に、ファイル名と、imgタグのalt属性との対応表がタブ区切りで出力される。
u1fa84_u1f431.png cat magic_wand magic_wand-cat
u1f600_u1f431.png cat grinning grinning-cat
u1f603_u1f431.png cat smiley smiley-cat
u1f604_u1f431.png cat smile smile-cat
u1f601_u1f431.png cat grin grin-cat
:
:
1列目はファイル名、2列目は組み合わせの1つ目の絵文字の名称、3列目は組み合わせの2つ目の絵文字の名称。4列目は組み合わせ絵文字の名称である。
絵文字のファイル名
ファイル名はアンダースコア区切りで、
- 1つ目は組み合わせ1つ目の絵文字の文字コード
- 2つ目は組み合わせ2つ目の絵文字の文字コード
である。
Unicodeであることを示すために、それぞれの文字コードには「u」が付加されている。実際の文字コードはuを外した残りの桁なので、それでGoogle検索などをすると該当する絵文字について説明したWebページが出てきたりする。
一部、以下のように、文字コードがハイフン区切りで長くなっているものがある。
u1f62e-u200d-u1f4a8_u1f431.png
このうちu200d
の部分は「ゼロ幅接合子」と呼ばれるものであり、絵文字を組み合わせて合体するための制御文字(?)である。
なのでこの絵文字は「1f62e 200d 1f4a8
」で1つの絵文字である点に注意する。
Slackにアップロードする準備
u1fa84_u1f431.png
などの形式でSlackのカスタム絵文字へアップロードすると、以下のようにファイル名が絵文字の名前として自動入力され、実際に絵文字として使う際に名前で検索できなくて不便である。
ローカルで実行したEmoji Kitchenのページにおいて絵文字の名前がAlt属性に埋め込まれていることに着目し、これをファイル名として利用する。
以下コマンドを実行し、filename_to_alt.txt
の対応表に従ってファイル名を変更する。
python ./set_filename.py
すると、emoji_kitchen_output
フォルダ以下にファイル名が変更された状態で出力されているはず。
これをSlackカスタム絵文字追加ページへアップロードすると、そのファイル名で絵文字の名前が自動入力される。
その他
cat_magic_wand_猫_魔法の杖
のような名前にしたい場合は、
ファイル名から組み合わせもう一方の文字コードを抽出し、何らかの手段でこれに応じたja emoji nameを引き当ててファイル名に組み込むことができる。
Slackにアップロード
失敗1:カスタム絵文字一括登録
以下ソースコードで登録しようとしたけど、エラーが出てしまい
技能上の都合でどうにもならなかったので、
カスタム絵文字への登録は全部手作業で実施した。
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.action_chains import ActionChains
from webdriver_manager.chrome import ChromeDriverManager
import requests
import os
import pickle
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)
action = webdriver.common.action_chains.ActionChains(driver)
SRC_DIRECTORY=r'./emoji_kitchen_output'
def login(driver):
# ログインしてから、コンソールでEnterキーを押す
input()
cookies = driver.get_cookies() # クッキーを取得する
pickle.dump(cookies,open('./add_many_emoji_cookie.pickle','wb'))
exit(0)
driver.get(f"https://*******.slack.com/customize/emoji?utm_source=in-prod&utm_medium=inprod-customize_link-slack_menu-click")
if os.path.exists('./add_many_emoji_cookie.pickle'):
cookies = pickle.load(open('./add_many_emoji_cookie.pickle', 'rb'))
for cookie in cookies:
driver.add_cookie(cookie)
driver.refresh()
else:
login(driver)
driver.implicitly_wait(10)
def clickforcible(action, element):
action = webdriver.common.action_chains.ActionChains(driver)
action.move_to_element_with_offset(element, 5, 5)
action.click()
action.perform()
def upload_emoji(driver: webdriver.Chrome, filename):
button1 = driver.find_element(By.CLASS_NAME, 'p-customize_emoji_wrapper__custom_button_short')
button1.click()
driver.implicitly_wait(1)
# button1 = driver.find_element(By.CSS_SELECTOR, '[data-qa="customize_emoji_add_dialog_upload"]')
# button1.click()
# driver.implicitly_wait(1)
upload_input = driver.find_element(By.CSS_SELECTOR, '[data-qa="customize_emoji_add_dialog_file_input"]')
upload_input.send_keys(os.path.join(SRC_DIRECTORY, filename))
driver.implicitly_wait(1)
print('ok')
input()
quit()
upload_emoji(driver, 'cat_two_hearts.png')
結果
かわいい
不満
猫絵文字が多すぎて、絵文字を検索するときに:cat
ですべての猫絵文字が出なくなってしまった。(特に元から手書きで書いたやつ)
こんなにかわいいのだから、Slackはデフォルトの絵文字にEmoji Kitchenを追加するべき。
特筆すべき点
Emoji Kitchenでは100,000以上の組み合わせ絵文字をGoogleのデザインチームが愛情込めて手作業で作ったらしい。
マジ?
おまけ
Emoji Kitchenに出会うまでに筆者が手作業で作成した、ねこの絵文字をどうぞ;
black_cat_face
clap_cat
angle_cat
melting_cat
nyan_bowing
nyan_gesturing_ok
pray_cat
cat_hand
black_cat_hand
もっとつくるわよ~~(1個作るのに何時間もかかるので期待しないでお待ちください)