📸

Pythonでスクリーンショットを撮ってくれるDiscordbotを作る!

2021/07/22に公開
2

この記事はもともとQiitaに投稿していたものを、Zennバージョンに書き直し、さらに解説を加えたものです。

今回完成させるもの

Discordでサイトのスクリーンショットを出してくれるボットを作ろう!
ご注意: Discord.py入門編ではありません。 アカウント作成については、以下の素晴らしい記事をご覧ください:こちら

環境

  • Windows 10(実際動かす環境では、AWS lightsail上のUbuntu)
  • Python 3.8 注意:discord.py v2.0がリリース後、Python 3.8以前のバージョンでdiscord.pyは動作しなくなります。
  • discord.py 1.7.3
  • Chromedriver 自動インストールにて利用できるもの
  • Google Chrome 91.0.4472.114

pipのインストール

pip install discord.py chromedriver chromedriver-binary selenium webdriver-manager

実装

さっそく実装していきましょう!まずはスクリーンショット部分です。


"""
モジュールのインポート
"""
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from webdriver_manager.chrome import ChromeDriverManager

Web='https://yahoo.co.jp'
FILENAME = 'screen.png'
"""
------------------------------
Chromeのオプション設定部分:
"""
options=Options()
options.set_headless(True)
options.add_argument('--disable-dev-shm-usage')
options.add_argument('start-maximized')
options.add_argument('disable-infobarse')
options.add_argument('--disable-extensions')
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')
options.binary_location='Chromeがあるとこ'
driver = webdriver.Chrome(
            ChromeDriverManager().install(), options=options)

"""
------------------------------
撮影:
"""
driver.get(web)
driver.set_window_size(1280, 720)
driver.save_screenshot(FILENAME)
driver.quit()

driverはChromeDriverManagerが自動で更新、インストールをしてくれます。
ただし、速度が少々遅くなることがあります。(今のところ実際に不便は感じていません)

このままの状態でも使用することができます。このような画像が撮れます:
Test run

Discordボットに移植する

最低限動くようにはなっています。

import discord
from discord.ext import commands
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
import aiohttp
import asyncio
from webdriver_manager.chrome import ChromeDriverManager

bot = commands.Bot(command_prefix='!',activity=discord.Activity(name='起動中!',type=discord.ActivityType.watching))

bot.session = aiohttp.ClientSession(
    headers={
        'User-Agent': 'Discordbot/T-taku; aiohttp on Python 3.8;'
    },
    skip_auto_headers=['User-Agent'],
    loop=bot.loop
)

@bot.event
async def on_ready():
    #ログイン
    print('Login infomation>>>')
    print(bot.user.name)
    print(bot.user.id)
    print('------')

@bot.event
async def on_message(message):
    ctx = await bot.get_context(message)#ctxをとる
    if message.author.bot:
        return #Botには反応しない
    await bot.invoke(ctx)

async def get_ss(ctx,web):
    try:
        options=Options()
        options.set_headless(True)
        options.add_argument('--disable-dev-shm-usage')
        options.add_argument('start-maximized')
        options.add_argument('disable-infobarse')
        options.add_argument('--disable-extensions')
        options.add_argument('--disable-gpu')
        options.add_argument('--no-sandbox')
        options.binary_location='Chromeがある場所'
	driver = webdriver.Chrome(
            ChromeDriverManager().install(), options=options)
	if not 'http' in str(url):
            try:
                await bot.loop.run_in_executor(None, functools.partial(driver.get, 'https://'+str(url)))
            except:
                await bot.loop.run_in_executor(None, functools.partial(driver.get, 'https://'+str(url)))
        else:
            await bot.loop.run_in_executor(None, functools.partial(driver.get, str(url)))
        if 'IP' in driver.page_source:
            await ctx.send('このWebページにはアクセスできません。')
        else:
            await bot.loop.run_in_executor(None, functools.partial(driver.set_window_size, 1280, 720))
            await bot.loop.run_in_executor(None, functools.partial(driver.save_screenshot, 'screenshot.png'))
            await bot.loop.run_in_executor(None, driver.quit)
            file = discord.File('screenshot.png', filename='image.png')
            embed = discord.Embed(title='スクリーンショット', description=f'{web}')
            embed.set_image(url='attachment://image.png')
            await ctx.send(file=file,embed=embed)
            driver.quit()
    except:
        await ctx.send('エラーが発生していたため、アクセスができませんでした。')

@bot.command()
@commands.cooldown(1, 15, type=commands.BucketType.user)
async def ss(ctx, web):
    await get_ss(web, ctx)

bot.run('TOKEN HERE')

IPと書いてあるところにサーバーのIPなどを入れておくと、サーバーのIPが公開されているサイトにアクセスできないようにする仕組みです。

ちなみに、非同期処理をしてないと、同時にコマンドが実行された場合ほぼほぼ100%の確率で、ボットが落ちます(オフラインになる)。そのため、しっかり非同期処理をしておきましょう。

稼働中の様子

感謝

  • プログラムの確認をしてくれたjun50さん。(@jun50_python)
  • 記事を読んでくれた全ての方々。

Discussion

tuna2134tuna2134

えーと
options.set_headless(True)が
selenium4ではoptions.headless = Trueになります