🈳

AtProtocolを使ってBlueskyにPythonからLinkCardをPostする

2023/08/28に公開

https://zenn.dev/sion_pn/articles/afe598bdbfebf9
前回、AtProtocolを使ってBlueskyにPythonからPostする方法を書きましたが、この方法でURLを含んだテキストを投稿しても、リンクが自動では有効にならないため任意のWebページを開くことが出来ません。
そのため、Blueskyへリンク付きのPostを投稿したい場合、LinkCardを作成して投稿を行うという手順を踏む必要があります。

使用ライブラリ

前回の記事で紹介した、The At Protocol SDK for Pythonを使用して実装を行います。
本記事投稿時点では、このライブラリの開発状況はまだStableではないため、今後破壊的な変更が入る可能性があります。
本記事ではv0.0.14時点の情報を元に記載していますので、最新のドキュメントと照らし合わせながらお読みください。

LinkCard作成とPost

models.AppBskyEmbedExternal.Mainメソッドで、LinkCardを作成します。
通常の投稿で使用するsend_postメソッドを用い、Optionalな引数であるembedに上記で作成したLinkCardを指定することで、LinkCardつきのPostを投稿することができます。

main.py
from atproto import Client, models

# 初期化

# Clientのコンストラクタで引数を省略すると、bsky.appへ接続します
api = Client()
# 任意の接続先を指定したい場合は、以下のように記述します
# api = Client('BlueskyインスタンスXRPCのURL(例:https://bsky.social/xrpc)')

# ログイン
api.login('ユーザーネーム or メールアドレス', 'パスワード')

# LinkCardの作成
embed_external = models.AppBskyEmbedExternal.Main(
    external = models.AppBskyEmbedExternal.External(
        title = "LinkCardのタイトル",
        description = "リンク先の説明",
        uri = "https://example.com"
    )
)

# Post
api.send_post('投稿テキスト本文', embed = embed_external)

titleとdescriptionをリンク先から取得する

上記までのコードでLinkCard付きのPostを投稿することができますが、titleやdescriptionを手動で入力するのは少し面倒です。
そこで、titleとdescriptionをリンク先から取得して利用してみます。

まずは、pipで今回使用するライブラリをインストールします。

zsh
pip install requests
pip install bs4

requestsはHTMLファイルの取得、bs4(BeautifulSoup4)はHTMLやXMLファイルなどからデータ抽出を行うためのライブラリです。

以下、リンク先URLからtitleとdescriptionを取得し、タプルで返却するメソッドです。

main.py
from bs4 import BeautifulSoup
import requests
def get_title_and_description(url:str):
    title : str = ''
    description : str = ''

    response = None
    try:
        # requestsで対象のURLに対してGET
        response = requests.get(url, timeout=60)
    except:
        return '', ''
    if response.status_code != 200:
        return '', ''

    # responseに含まれるテキストデータを、HTMLパーサで処理
    soup = BeautifulSoup(response.text, 'html.parser')
    # titleタグ内のテキストを取得
    result = soup.find('title')
    if result != None:
        title = result.text
    # metaタグdescription内のテキストを取得
    result = soup.find('meta', attrs={'name': 'description'})
    if result != None:
        description = result.get('content')

    # タプルでまとめて返す
    return title, description

上記のメソッドを使ってembed_externalを取得すると、以下のようなコードになります。

main.py
url = "https://example.com"
title, description = get_title_and_description(url)
embed_external = models.AppBskyEmbedExternal.Main(
    external = models.AppBskyEmbedExternal.External(
        title = title,
        description = description,
        uri = url
    )
)

以上です。

今回のサンプルコードでは最低限のエラーハンドリングしかしていませんので、用途に応じて適切にエラーハンドリングして実装すると良いでしょう。

Discussion