プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、python3、ラズパイなど。

はてなブログの記事をPythonで自動投稿する

はてなブログの記事を自動生成して投稿するスクリプトを作りました。

はてなブログには、記事をスクリプトで投稿するAPIとMailによる投稿ができる仕組みがあります。
これを利用してTwitterの人気投稿を収集してブログ記事を生成します。
できあがったブログは↓。

s51517765.hatenablog.jp

この中で、Twitterの投稿をカード形式で埋め込みたかったのですが、APIではhtmlがエスケープされているようで目的が達成できません。
そこで、Mailによる投稿のスクリプトを作りました。

※人気Tweet選別の細かい仕様はノウハウとして、コードの中ではぼかしてあります。

全体の構成


Twiiterの投稿の取得は Tweepyを使用しています。
夜中の0時過ぎに自動的に起動して、以下のmain()が呼ばれると、
・Twiiter apiのauth認証
・人気Tweetの検索
・人気Tweetが不足していたら、自分のTweetから検索して追加
・Mailによるはてなブログ投稿
というながれです。

def main():
    now = datetime.now()
    if now.day % 2 != 0:
        acount = "s"
        print("s51517765 Make blog!")
        api = login(acount) #twitter api
        tweet_list = search_tweet(api, acount)
        tweet_list = my_tweet(api, tweet_list)
        post_main(acount, tweet_list)  

Twitter auth認証

def login(acount):
    if acount == "s":
        CONSUMER_KEY = "****************"
        CONSUMER_SECRET = "****************"
        ACCESS_TOKEN = "****************"
        ACCESS_SECRET ="****************"
    else:
        exit() #アカウント指定を確認して違っていれば終了

    auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
    auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
    api = tweepy.API(auth)
    return api

Twitterの検索

Twitter APIの検索に投げるキーワードはListで保持してRTやFav.で人気Tweetを検索します。
APIではRTされているものは、RTごとに拾われるので中身を確認して重複しているものは除外します。

def search_tweet(api, acount, count=200):
    tweet_list = []
    tweet_id = []
    now = dt.today()

    if acount != "s":
        print("Acount Error")

    random.shuffle(keywordlist) #Twiiter検索につかうキーワード

    replypattern = r"@[\w]+"  # 重複確認用

    keyword_count = 0
    for keyword in keywordlist:
        for tweet in api.search(q=keyword, count=count):  # 検索
            try:
                if tweet.retweet_count > ** or tweet.favorite_count > **): #人気記事の判定
                    if (now - tweet.created_at).days < 3:
                        txt = tweet.text
                        if txt.count("#") < 4: #ハッシュタグの多いTweetは除外
                            if is_japanese(txt):   #日本語のTweet以外は除外                                  
                                txt = re.sub(replypattern, '', txt) #@ や RT を除外する
                                txt = txt.replace("RT", "")
                                txt = txt.replace(":", "")
                                txt = txt.replace(" ", "")
                                if txt not in tweet_list:  # RTによる重複確認
                                   tweet_list.append(txt)
                                   tweet_id.append(tweet.id)
                                else:
                                   break
            except:
                pass

        if len(tweet_id) > 40:
            break
        time.sleep(30)

    return tweet_id

自分の投稿は以下のようにして取得できます。自分がRTしたものは除きます。
Twiiter APIではRTはTweet本文status.textRT:のようなテキストが付加されていますので、これを探します。

def my_tweet(api, tweet_list):
    for status in tweepy.Cursor(api.user_timeline).items():
        if "RT" not in status.text : #自分がしたRTは除外
            tweet_list.append(status.id)
    return tweet_list

日本語以外のTweetは除外します(日本語をキーワードに検索しているのになぜか全く関係ない海外のTweetが引っかかることがあるため)。
↓の記事を参考に、1文字でもひらがなorカタカナが含まれていれば日本語と判定します。
引用下では漢字を含めていますが、漢字はチェックしなくてもよいと考えました。
日本語で漢字のみで成立する文章というのは稀でかならず、ひらがなorカタカナが入ると思います。
minus9d.hatenablog.com

def is_japanese(string):
    for ch in string:
        name = unicodedata.name(ch)
        if "HIRAGANA" in name or "KATAKANA" in name:
            return True
    return False

投稿する本文

このスクリプトでMail投稿するときは「はてな記法」にする必要があります。
Twiiterの投稿をカード形式で表示するために、<a href=\"https://twitter.com/s51517765/status/************* "></a> のようにTwitterのLinkをaタグで挿入します。さらにこれをclassで囲んでいます。

def post_main(acount, tweet_list):
    # はてな記法
    now = datetime.today()
    yesterday = now - timedelta(days=1)
    title = "【" + str(yesterday.year) + "年" + str(yesterday.month) + "月" + str(yesterday.day) + "日" + "の人気ツイート】"
    body = ""
    for i in tweet_list:
        prefix = "<blockquote class=\"twitter-tweet\"><a href=\"https://twitter.com/s51517765/status/"
        sufix = "\"></a></blockquote><script async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>"
        body = body + prefix + str(i) + sufix + "\r\n"

    # メールによる投稿
    # g_mail.send_mail(title, body, "*****************.draft@blog.hatena.ne.jp")  # 下書き
    g_mail.send_mail(title, body, "*****************@blog.hatena.ne.jp")  # 投稿

Mailによるはてなブログ投稿

投稿用のアドレスははてなブログの管理画面から取得します。
f:id:s51517765:20200105142812j:plainf:id:s51517765:20200105142833j:plain

↓を参考にスクリプトを作成します。G-mailは外部から制御できるように権限を開放する必要があります。
news.mynavi.jp

# gmail-py
import smtplib, ssl
from email.mime.text import MIMEText

# https://news.mynavi.jp/article/zeropython-51/

# setting -----------------------------------------------------------
gmail_account = "********@gmail.com"
gmail_password = "*********"
# setting -----------------------------------------------------------

def send_mail(subject, body, mail_to="*******@gmail.com"):
    msg = MIMEText(body, "html")
    msg["Subject"] = subject
    msg["To"] = mail_to
    msg["From"] = gmail_account

    # Gmailに接続 --- (*4)
    server = smtplib.SMTP_SSL("smtp.gmail.com", 465, context=ssl.create_default_context())
    server.login(gmail_account, gmail_password)
    server.send_message(msg)  # メールの送信
    print("Gmail send ok.")

まとめ

これで、ブログ記事を量産できます。
ちなみにInstagramでも人気投稿を集めてブログ記事を作成というのを検討しているのですが、こちらはAPIで投稿を取得できなかったり、htmlが複雑だったりで考え中です。

参考

APIによる投稿
tadaken3.hatenablog.jp
Tweepy
s51517765.hatenadiary.jp
s51517765.hatenadiary.jp
s51517765.hatenadiary.jp
自動投稿
karaage.hatenadiary.jp