cojimaru BLOG

エンジニア支援のために技術情報を発信するブログ

Webスクレイピングで急落銘柄通知ツールを作成してみた

f:id:cojimaru-chan:20210420010122p:plain

どうも、こじまるです。
以前Twitterの仲良しさんと投資ツールについてやり取りしていた時に、下記のようなツールを使ってみたいという話がありました。



f:id:cojimaru-chan:20210419234111p:plain:w500


そのため、当月・翌月が権利確定月の銘柄で、急落銘柄を通知するツールを作ってみようと思いました。

この記事をみてわかること

Webスクレイピングで急落銘柄通知ツールの作成

要件定義

システムの要件定義
  • 要件1. 当月・翌月が権利確定日の銘柄で、急落銘柄を通知できること
  • 要件2. スマートフォン/パソコンから簡単に情報取得できること

処理の流れ

今回使用するプログラムは、権利確定日の情報を取得する事前処理と定期的に急落銘柄を判定し通知するメイン処理に分離しました。

事前処理
権利確定日の情報は、頻繁に変更されるものではありません。そのため、スクレイピング先のサーバの負荷を考慮し、一ヶ月に一度あるいは一度だけ実行されればいいと思います。

f:id:cojimaru-chan:20210419235910p:plain

事前処理の流れ
  • ①権利確定月などの銘柄情報の取得
  • ②銘柄情報を保存


メイン処理(定期実行)
メイン処理に関しては、当月・翌月が権利確定日の銘柄で、急落銘柄を通知する必要があります。急落銘柄は、前日終値と現在の株価から割合を求めます。タイムリーにその情報を得るためにも定期的な実行が必要になります。

f:id:cojimaru-chan:20210420000004p:plain
メイン処理の流れ
  • スクリプトの定期実行
  • ②銘柄情報を取得
  • ③急落銘柄を通知

事前処理

①権利確定日の銘柄情報を取得

権利確定日の銘柄情報取得のために、https://kabuoji3.com/を使用しました。https://kabuoji3.com/robots.txtを確認しましたが、Allow:/と記載されていますので、問題なく使用できそうです。

User-Agent: *
Allow:/
Sitemap:http://kabuoji3.com/sitemap/sitemap-index.xml



今回使用するURLは、https://kabuoji3.com/stockholder/{銘柄コード}/になります。こちらのURLにブラウザでアクセスすると、権利確定月が表示されています。
f:id:cojimaru-chan:20210420000407p:plain


こちらの権利確定月の表示をXPATHで参照するようにし、取得しました。

url = "https://kabuoji3.com/stockholder/{}/".format(stock_code)
headers = {"User-Agent": "Chrome"}

html = requests.get(url, headers=headers)
html.raise_for_status()
dom_tree = lxml.html.fromstring(html.content)

# 権利確定月
vesting_date = dom_tree.xpath('//*[@id="base_box"]/div/div[3]/dl[2]/dd/text()')
if not len(vesting_date):
  continue
stock_vesting_date = vesting_date[0]


②銘柄情報の保存

権利確定月の情報は日々更新されるわけではないので、下記のようにExcelデータとして保存し、それをメイン処理で読み取む形で利用します。
f:id:cojimaru-chan:20210420000759p:plain

メイン処理

スクリプトの定期実行

タスクスケジューラやcronなどを使って、スクリプトの定期実行が可能です。こちらの設定方法などは下記記事を参考にしてください。

cojimaru-chan.hatenablog.com

cojimaru-chan.hatenablog.com

②銘柄情報を取得

事前処理で取得した権利確定月の情報を利用して、当月・翌月が権利確定月の銘柄のみに対してのみ、スクレイピング処理を行います。

当月・翌月が権利確定月の銘柄の判定
取得した権利確定月の情報には月を指す文字以外の余計な文字も含まれています。正規表現を使って、必要となる月を示す文字列のみを取得しています。

# 当月・翌月の文字列を取得
dt_now = datetime.now()
tmp = dt_now + relativedelta(months=1)
dt_month = str(dt_now.month)
dt_next_month = str(tmp.month)

...

stock_vesting_date = str(row[2]) # 権利確定月
if stock_vesting_date == "随時": # 随時は対象外に
  continue
else:
  month_list = re.findall("\d+月",stock_vesting_date)
  vesting_month_list = [s.replace("月","") for s in month_list]
  
  # 当月・翌月が権利確定月の銘柄のみ後段の処理を実行
  if dt_month in vesting_month_list or dt_next_month in vesting_month_list:
    pass
  else:
    continue



急落銘柄の判定
現在の株価、前日終値は以前の記事と同様に、みん株のサイトからウェブスクレイピングを利用して取得します。詳細に関しては、下記の記事を見てください。

cojimaru-chan.hatenablog.com

取得した株価と前日終値を利用し、急落率を計算します。ユーザが定義した急落率を超えた場合に、リストに情報を保持するように実装しました。

# 現在値
tmp = chart_dom_tree.xpath('//*[@id="layout"]/div[2]/div[3]/div[2]/div/div[1]/div/div/div[1]/div[2]/div/div[2]/div/text()')
current_price = float(re.sub(r'\D','',tmp[0]))

# 前日終値
stock_previous_closing_place = parse_dom_tree(chart_dom_tree,'//*[@id="contents"]/div[3]/div[1]/div/div/div[2]/div/div[1]//tr[1]/td[1]','円','')            
price_drop_rate = (1 - current_price / stock_previous_closing_place) * 100
if price_drop_rate >= TARGET_DROP_RATE:
  output_list.append([stock_code,stock_name,current_price,stock_previous_closing_place,price_drop_rate])


③急落銘柄を通知

Slackへの通知には、Incoming Webhooksを利用して実行しています。詳細はこちらでは割愛しますので、必要であれば、下記記事を参考にしてください。
参考
https://developers.wonderpla.net/entry/2020/06/18/110005

コード

今回使用したコードを下記にアップロードしました。興味がありましたら、事前処理はdataCollection.py、メイン処理はcreateStockMap.pyを確認してください。
github.com

実行結果

急落率 : 5%の条件で実行してみました。急落率が5%以上の銘柄が取得できています。
f:id:cojimaru-chan:20210420004649p:plain

他のサイトで急落率を確認しましたが、一致していますね。
f:id:cojimaru-chan:20210420005040p:plain

まとめ

Webスクレイピングを行って、当月・翌月が権利確定月の銘柄で、急落銘柄を通知するツールを作成しました。Webスクレイピング記事を他にも書いており、ナレッジがあったので、案外簡単に作成することができました。もし、興味がありましたら、利用してみてください。(※定期的なスクレイピングは高頻度に実行すると、サービス提供者への負荷になりますのでご注意ください。)

今後は有効性を検証するため、急落率 : 3%~5%、平日の9時~15時で一時間刻みの条件でスクリプトを実行し、有効性を検証してみようと思います。

【運営報告】ブログ開設2か月の初心者がPV数、収益の結果公開

f:id:cojimaru-chan:20210403102652p:plain

2か月ブログを運営するとどれくらいPV数が増えるのかな?

収益ってどれくらい出るの?

このような疑問にこたえます。

プログラミング・電子機器などの雑記ブログを始めて2か月経過しました。今回はブログ運営2か月後のPV数、収益を報告します。


この記事をみてわかること
  • ブログ2か月運営後のPV数と収益
  • 今後の運営方針

ブログ運営2か月後の運営結果

こちらが結論になります。

期間 1か月目 2か月目 全期間
ユーザ数 141人 208人 349人
PV数 682PV 519PV 1191PV
記事数 15記事 8記事 23記事
収益 0円 0円 0円


ブログ運営2か月後のPV数

今月のPV数 : 591PV(前月 : 682PV)


下記が今月のPV数の遷移になります。

f:id:cojimaru-chan:20210330235126p:plain
二か月目のPV数の遷移

前月と比較して、思ったよりも伸びなかったなというのが率直な印象。1000PVを目標にしていましたが未達ですね。

f:id:cojimaru-chan:20210401002004p:plain
PV数の比較


PV数の遷移をみると3月25日付近にPV数が跳ね上がったのは、Googleアドセンス不合格のTweetに対して、アクセスが多かったのが影響しています。PV数の向上には、他の方が興味を惹くTweetはかなり重要ですね。


ブログ運営2か月後の収益

今月の金額 : 0円(前月 : 0円)


前月同様に、今月も収益は0円という結果でした。現状加入しているものは、もしもアフィリエイトA8.netのみですが、A8.netはまだ使用できていません。


下記はもしもアフィリエイトの指標の比較になります。

1か月目 2か月目
インプレッション 1,093 1,970
クリック数 1 20
CTR 0.1% 1.0%


前月と比較して、インプレッション・クリック数などが結構増えました。下記のような商品紹介記事やレビュー記事を出したことによる効果なのかなと思います。
現役エンジニアがテレワークアイテムを公開してみた - cojimaru BLOG

ブログ運営2か月後の公開記事数

今月の記事数 : 8記事(前月 : 15記事)


前月は2日に1回ブログを更新できていましたが、今月は3日に1回のブログの更新もできませんでした。仕事が忙しいというのももちろんあったのですが、技術系の記事で結構時間がかかりました。

f:id:cojimaru-chan:20210401001731p:plain
公開記事数の比較


今月は新たにTwitterの管理を楽にするツールを作ってみました。Twitter APIの仕様調査、理解、実装、ブログ記事の作成まででそれぞれ1週間ずつかかってしまいました。
Twitter APIによりTwitter検索ツールを作成してみた - cojimaru BLOG
OAuthでTwitter連携!! 誰でもTwitterAPIが利用可能に - cojimaru BLOG


仕様調査から始めるとかなり時間を費やしてしまいます。技術系以外にも他にネタがあるのであれば、技術系は月1くらいで他の記事を優先して書いてもいいですね。

ブログ運営2か月目に取り組んだこと

前月の運営方針に対して

下記のように運営方針を定めていましたが、すべて目標としていた指標までには至りませんでした。

No. 運営方針 状況
1 ブログの更新頻度を1記事/2日を維持する 未達(1記事/3日)
2 ブログの内容に興味があるフォロワーを増やす
(フォロワー : 500人目標、記事執筆時 : 77人)
未達(252人)
3 SNSでブログ以外の情報を配信する(3件/1日) 未達(58件/月=1.9件/日)


目標が高すぎたのもあり、月半ばでブログのモチベーションが少し落ちてしまうときもありました。目標が厳しすぎるとブログを辞めたくなってしまうので、来月の方針は続けるのを第一優先に少し甘めにしたいと思います。

その他に対して

SEOを意識した記事の作成

検索流入を増やすために、SEOを意識して記事を作成することにしました。具体的には、下記の流れで記事作成を行いました。

記事作成の流れ

ラッコキーワードでサジェストの確認、選定

google広告でサジェストのアクセス数/月を確認. そのアクセス数でよければ、記事のメインワードを決定.

③記事のメインワードを意識した記事の作成.

記事のメインワードはそのワードで検索した場合に上位に表示されるサイトよりも多く含むように意識しています。SEOチェキでキーワード出現頻度を検索できるので、そちらを活用しています。


上記で紹介していますラッコキーワードはサジェストキーワードを無料で検索できるツールです。興味がありましたら、下記から利用してみてください。
related-keywords.com

Google Adsenseの申請

前月に1度目のGoogle Adsenseの申請をしましたが、不合格。
今月に2度目のGoogle Adsenseの申請をしましたが、不合格。
そのため、今は3度目のGoogle Adsenseの申請をしています。


不合格理由 : 価値の低い広告枠(コンテンツが存在しない)みたいなので、プライバシーポリシーを設置したり、Google Search Consoleの内容を見直したりしていました。

ブログ運営3か月目の目標・方針

目標としては、まず先月から目指している月1000PVを目指します。


前月の目標指標に対して、すべて未達だったこともあるので、今月の目標指標は少し甘め。

今後一か月の運営方針
  • ブログの更新頻度を1記事/3日を維持する
  • ブログの内容に興味があるフォロワーを増やす
    (フォロワー : 150人増やす. 記事執筆時 : 248人)
  • SNSでブログ以外の情報を配信する(2件/1日)

今後も読者の皆様に価値ある情報を共有できるように頑張りますので、応援よろしくお願いします。

【資産公開】2021年3月の資産と持ち株公開してみた

f:id:cojimaru-chan:20210330005332p:plain

どうも、現役エンジニアのこじまるです。
株式投資・つみたてNISAなど、資産運用を行っています。
今回はそれらの資産公開をします。

これから株式投資・つみたてNISAをしたいのだけど、経験者はどのような銘柄を選択しているの?

経験者はどれくらい損益が発生しているのかな?



このような悩みを解決します。

筆者の投資スタイル

筆者は信用取引で大穴を当てる』ような投資スタイルではなく、現物取引で地道に資産を増やしていく』投資スタイルです。

  • 投資をしたら基本的に放置
  • 個別株よりもETF(分散投資)
  • 資産 : 1,000万円を目指し、毎月積み立てています
  • ブログのために投資スタイルを変更するつもりはございませんので、その点ご理解ください。

    2021年3月末時点の資産公開

    結論

    先に結論から書きます。

    評価額 評価損益
    ネオモバイル証券(日本個別株) 313,966円 -8,953円
    楽天証券(つみたてNISA) 158,710円 18,710円
    SBI証券(米国ETF) 34,882円 -372円
    確定拠出年金 414,396円 -
    合計 921,954円 9,385円


    今月の買付合計 : 64,000円

    ネオモバイル証券(個別株)

    基本的に高配当株狙いで個別株投資をしています。高配当株ポートフォリオを作成し、分散投資をしようと思いましたが、購入したい銘柄が割高のため、少額での投資となっています。
    f:id:cojimaru-chan:20210328210217p:plain

    今月の楽天日本郵政との提携発表は熱かったですね。私は1200円台で買付をし、1400円台のプラスの時点で利確をしました。1400円台で値動きが落ち着いてたので再投資しましたが、今月末ではマイナスに。。



    ※国内の個別株は分散投資も大変だし、管理するのが割と面倒なので、米国ETFに資産を移していく予定です。


    高配当株はスクリーニング条件を満たす銘柄から選定しています。下記にスクリーニング条件を載せていますので、良ければこちらも見てください。
    cojimaru-chan.hatenablog.com

    楽天証券(つみたてNISA)

    投資金額 : 20,000円
    つみたてNISAはS&P500と先進国株式インデックスに投資しています。ファンド選択後、特にいじっていませんが、評価損益が13%くらいで推移しています。
    f:id:cojimaru-chan:20210328210724p:plain

    月々20,000円の投資しかできていなかったため、来月からはつみたてNISAの枠を使い切るように積み立てます。


    SBI証券(米国ETF)

    投資金額 : 約35,000円
    今月から始めた米国ETFでの投資です。高配当金狙いで投資しようか迷っていたのですが、せっかくならGAFAMなどのハイテク株の銘柄の割合が高い、QQQに投資することにしました。
    f:id:cojimaru-chan:20210328204947p:plain

    1株 35,000円は高いですが、米国ETFは1株単位で購入できます。
    外貨決済で行いましたが、慣れればスムーズにできそうです。


    確定拠出年金

    投資金額 : 9,000円
    勝手にリバランスしてくれるファンドに投資しています。株式比率を上げたいと思っているので、ファンドを見直す予定です。
    f:id:cojimaru-chan:20210329213441p:plain
    ※上記の資産配分には今月の投資金額は反映されていません。

    評価額 評価損益 利回り
    確定拠出年金 385,000円 29,396円 3.9%

    まとめ

    筆者が投資している、株式投資・つみたてNISAなどの資産を紹介しました。資産の振り返りのためにも、今後1か月毎に定期配信していきたいと思います。


    私が知っているお金を稼ぐ方法・お金を貯める方法などはこちらの書籍を参考に実践しています。興味がありましたら、是非見てみてください。

    OAuthでTwitter連携!! 誰でもTwitterAPIが利用可能に

    f:id:cojimaru-chan:20210328104302p:plain

    どうも、現役エンジニアのこじまるです。
    今回の記事はTweet検索ツールの続編になります。



    以前Twitter APIを使って、Tweet検索ツールを作成しました。このツールはユーザが指定したワードでTweet検索し、そのTweetにいいね、リツイートをすることができます。ただ、いいね・リツイートは筆者のアカウントでしかできません。そのため、他のユーザでもいいね・リツイートができるように変更してみました。


    Twitter APIのOAuth1.0の仕組みがいまいち分からない

    Twitter APIのOAuth1.0での実装ってどのようにするんだろう?



    この記事では、このような悩みを解決します。


    ※本記事ではFlaskを使ってOAuth1.0を実装しましたが、想像以上に苦労しました。なので、今後学習される方のために認証の流れを丁寧に説明します。

    OAuthでTwitter連携!! 誰でもTwitterAPIが利用可能に

    OAuth1.0の流れ

    今回の実装ではTwitter APIを実行するまでに、OAuth1.0の認可の仕組みを導入しています。

    OAuth1.0の流れ

    ①事前処理

    ②リクエストークンを取得する

    ③ユーザをリダイレクトする

    ④リクエストークンをアクセストークンに変換する

    ⑤SignIn後の処理

    具体的には、①~④の流れでOAuth1.0の処理を行います。アクセストークンを取得して、クライアントがTwitter APIを利用できる状態にします。そして、⑤では取得したアクセストークンを利用し、Twitter APIを実行します。

    f:id:cojimaru-chan:20210328100253p:plain

    ①事前処理

    事前処理として、Twitter Developer PortalよりCallback URLを登録します。このCallback URLは、Twitter ServerがリダイレクトさせるURLになります。

    f:id:cojimaru-chan:20210327172521p:plain

    ②リクエストークンを取得する

    UserがViewに表示される下記ボタンの押下により、ViewはServerにリクエストークンの取得を要求します。
    f:id:cojimaru-chan:20210327202332p:plain


    リクエストークンの取得のために、Serverでは下記の形式でTwitter Serverにリクエストを送出します。ここで、oauth_callbackは必須パラメータで、①でTwitter Developer Portalに登録したCallback URLと一致する必要があります。

    POST https://api.twitter.com/oauth/request_token
    {
         'oauth_callback':'http://192.168.10.107:5000/main'
    }


    @app.route('/signin',methods=["POST"])
    def postSignIn():
        consumer_key, consumer_secret = session_manager.getConsumerKey()
        twitter = OAuth1Session(consumer_key, consumer_secret)
        endpoint = "https://api.twitter.com/oauth/request_token"
        oauth_callback = "http://192.168.10.107:5000/main"
        response = twitter.post(endpoint, params={'oauth_callback':oauth_callback})
        token_dict = dict(parse_qsl(response.content.decode('utf-8')))
        authentication_endpoint = "https://api.twitter.com/oauth/authenticate?oauth_token={}".format(token_dict['oauth_token'])
    


    ③ユーザをリダイレクトする

    次に、②のPOSTリクエストで取得したoauth_tokenを使用して、Twitter Serverにユーザ認証を要求します。

    GET https://api.twitter.com/oauth/authenticate?oauth_token=xxxxxxxxxxxx



    上記のリクエストを送出するために、リダイレクトを使って実装しました。具体的には、ResponseのStatus codeを302, ヘッダのLocationフィールドを上記のURLに設定し、Viewにレスポンスを返しています。

    @app.route('/signin',methods=["POST"])
    def postSignIn():
        ...
        authentication_endpoint = "https://api.twitter.com/oauth/authenticate?oauth_token={}".format(token_dict['oauth_token'])
    
        response = jsonify()
        response.status_code = 302
        response.headers['location'] = authentication_endpoint
        return response
    



    ブラウザがURLにアクセスすると、下記のようにTwitterの認可ページが表示されます。筆者のツールの場合、いいね・リツイートを行うUserの情報を入力し、Twitter Serverに認証を依頼します。

    f:id:cojimaru-chan:20210327205702p:plain

    認証に成功すると、②で指定したCallback URLにリダイレクトします。

    ④リクエストークンをアクセストークンに変換する

    ③でリダイレクトしたURLのクエリストリングには、oauth_token, oauth_verifierが付与されています。

    http://192.168.10.107:5000/main?oauth_token=xxxxxxxxxxxx&oauth_verifier=xxxxxxxxxxxx



    リクエストークンをアクセストークンに変換する下記のTwitter APIでoauth_token, oauth_verifierが必須となっています。そのため、それらの情報を抽出し、下記のリクエストボディに与えます。

    POST https://api.twitter.com/oauth/access_token
    {
        'oauth_token':'xxxxxxxxxxxx',
        'oauth_verifier':'xxxxxxxxxxxx'
    }



    上記のリクエストが成功することで、oauth_token,oauth_token_secret,...が取得できます。

    def generateAccessToken(self,oauth_token, oauth_verifier):
        twitter = OAuth1Session(self.cosumer_key, self.cosumer_secret)
        access_endpoint = "https://api.twitter.com/oauth/access_token"
        response = twitter.post(access_endpoint, params={'oauth_token':oauth_token,'oauth_verifier':oauth_verifier})
        access_token_dict = dict(parse_qsl(response.content.decode('utf-8')))
        self.access_token = access_token_dict['oauth_token']
        self.access_secret = access_token_dict['oauth_token_secret']
    

    ⑤サインイン後の処理

    ④で取得した、oauth_token,oauth_token_secretを使って、Twitter APIを実行します。

    @app.route('/searchTweet',methods=["POST"])
    def getTweet():
        print('Enter getTweet')
        ...
        consumer_key, consumer_secret, access_token, access_secret = session_manager.getAllKey()
        global twitter
        twitter = OAuth1Session(consumer_key, consumer_secret, access_token, access_secret)
        endpoint = "https://api.twitter.com/1.1/search/tweets.json?{}&{}&{}&{}&{}".format(q,lang,result_type,count,entities)
        res = twitter.get(endpoint)
    

    結果


    まとめ

    以前作成したTweet検索ツールにOAuth1.0の認証を付与し、誰でもTweet検索を行って、いいね・Retweetを行うことをできるようにしました。OAuth1.0の仕様をあまり理解していなかったため、今回実装するのは結構苦労しました。ただ、実装したことで、それなりに理解はできたと思います。


    今回実装する上で必要になりました、HTTPの知識(REST I/F、リダイレクトなど)に関しては、下記の書籍を活用して勉強しました。Web APIなどの設計・開発を行う上で必要な知識が集約されているので、もし必要であればご覧ください。

    Twitter APIによりTwitter検索ツールを作成してみた

    f:id:cojimaru-chan:20210321103932p:plain

    どうも、現役エンジニアのこじまるです。
    今回はTwitter管理のために、Twitter APIを使用してTwitter検索ツールを自作します。



    私はTwitterの運用にSocialDogを使っています。キーワードモニター機能が割と便利で利用しているのですが、無料アカウントの場合だとフォロー、いいね、リツイートの回数に制限があります。


    SocialDogで有料アカウントになり余計な出費をするのは嫌なので、Twitter検索ツールを自作してみました。


    Twitter検索ツール作成

    要件定義

    ツール要件
    • キーワードからTweetの検索ができること
    • 検索結果よりいいね、リツイートができること
    • ユーザが操作できること

    環境構築

    今回の実装では、下記を使用しました。

    • フロントエンド : HTML, JavaScript
    • サーバーサイド : Python(Flask)

    HTML、JavaScriptではCDNJQueryなどを取得しています。そのため、必要なものは特にありません。Pythonでは、pipで下記のパッケージをインストールしています。

    pip install flask
    pip install requests
    pip install requests-oauthlib

    実装

    キーワードからTweetの検索

    f:id:cojimaru-chan:20210320194230p:plain
    キーワードからTweetの検索の流れ
    ①Flaskへの検索依頼

    HTMLからFlaskに対して、POST /searchTweetという形で検索依頼を投げます。


    Flaskでは、POST /searchTweetでルーティングできるように、app.routeで定義する必要があります。この関数ではリクエストの内容を解析し、リクエストから検索条件を取得しています。

    @app.route('/searchTweet',methods=["POST"])
    def getTweet():
        print('Enter getTweet')
        data = request.get_data().decode('utf-8')
        tmp,query = data.split('=')
        q = "q={}".format(query)
        lang = "lang=ja"
        result_type = "result_type=recent"
        count = "count=10"
        entities = "include_entities=true"
    
    ②Tweeter APIにキーワードの検索依頼

    ①で説明した、getTweetの続きになります。①で取得した検索条件に基づき、Twitter API : GET search/tweetsを使ってTwitterからTweetを取得します。

        twitter = OAuth1Session(settings.CONSUMER_KEY,settings.CONSUMER_SECRET,settings.ACCESS_TOKEN,settings.ACCESS_TOKEN_SECRET)
        endpoint = "https://api.twitter.com/1.1/search/tweets.json?{}&{}&{}&{}&{}".format(q,lang,result_type,count,entities)
        res = twitter.get(endpoint)
    


    Twitter API : GET search/tweetsとGET statuses/lookup*1で取得したデータから、下記の情報を抽出し、リストに格納します。

    【userオブジェクト】

    名前 内容
    name ネーム
    screen_name スクリーンネーム
    id 数値型のユーザーID
    profile_image_url_https プロフィール画像SSL用のURL

    Tweetオブジェクト】

    名前 内容
    id_str 文字列型のツイートID
    text ツイートの本文
    created_at Tweetが投稿された日時
    favorited いいねされているかどうかの状態
    favorite_count いいねされた数
    retweeted リツイートされているかどうかの状態
    retweet_count リツイートされた数
    retweeted_status 元のツイートの情報


    ここで、retweeted_statusは引用なし(公式)リツイートの場合のみ取得できます。


    userオブジェクト・Tweetオブジェクトの内容はこちらの記事を参考にしました。
    syncer.jp

    ③検索結果の表示依頼

    ②で取得したuserオブジェクト・Tweetオブジェクトの情報を集積したリスト(tweet_list)をrender_templateメソッドの引数に指定します。

    @app.route('/searchTweet',methods=["POST"])
    def getTweet():
        ...
        print("Exit getTweet")
        return render_template('searchTweet.html', tweet_list=tweet_list)
    


    今回は②で取得した検索結果を表示するために、tweet_listの配列数分ループさせるように実装しています。{{list.profile_image_url_https}}と記述することで、list内の要素にアクセスできます。

    {% extends "layout.html" %}
    {% block content %}
    {% for list in tweet_list %}
    <div class="twitter__block">
        <figure>
            <img src={{list.profile_image_url_https}} />
        </figure>
        ...
    </div>
    {% endfor %}
    {% endblock %}
    


    いいね、リツイートは、検索実行時のいいね、リツイートの状態に基づきON、あるいはOFFにして表示する必要があります。そのため、下記のようにlist.favoritedの値に応じて、spanタグのclass属性の値を切り替えるようにしました。

    {% if list.favorited %}
        <span id={{list.tweet_id}} class="LikesIcon on">
            <i class="fas fa-heart LikesIcon-fa-heart heart">{{list.favorite_count}}</i>
        </span>
    {% else %}
        <span id={{list.tweet_id}} class="LikesIcon">
            {% if list.favorite_count > 0 %}
                <i class="far fa-heart LikesIcon-fa-heart">{{list.favorite_count}}</i>    
            {% else %}
                <i class="far fa-heart LikesIcon-fa-heart"></i>
            {% endif %}
        </span>
    {% endif %}
    

    検索結果からいいね、リツイートを行う

    いいね・リツイートを行う

    いいね・リツイートを行うために、下記のようにクリックイベントを使用します。(いいねとリツイートの実装はあまり変わらないため、今回はいいねの場合のみ説明します。)検索結果のあるツイートのいいねの状態がONであれば、いいねを外します。いいねの状態がOFFであれば、いいねを行います。

    $('.LikesIcon').on('click', function() {
        let $btn = $(this);
        tweet_id = $btn.attr('id')
        // Likeボタンがonクラス持っていたら
        if ($btn.hasClass('on')) {
            $btn.removeClass('on');
            // 白抜きアイコンに戻す
            $btn.children("i").attr('class', 'far fa-heart LikesIcon-fa-heart');
            $.ajax({
                url: 'favorites',
                type:'DELETE',
                dataType: 'json',
                data : {'tweet_id' : tweet_id},
                timeout:3000,
            }).done(function(data) {
                if(data['favorite_count'] != 0)
                    $btn.children("i").text(data['favorite_count'])
                else
                    $btn.children("i").text("")  
            })
        } else {
            $btn.addClass('on');
            // ①アイコンを変更する
            // far fa-heart(白抜きアイコン)
            // ⇒ fas fa-heart(背景色つきアイコン)
            // ②アニメーション+アイコン色変更用のheartクラスを付与する
            $btn.children("i").attr('class', 'fas fa-heart LikesIcon-fa-heart heart');
            $.ajax({
                url: 'favorites',
                type:'POST',
                dataType: 'json',
                data : {'tweet_id' : tweet_id},
                timeout:3000,
            }).done(function(data) {
                $btn.children("i").text(data['favorite_count'])
            })
        }
    });
    


    JavaScript側で細々した操作をしたくないので、Ajaxでリクエストを送る先をFlaskにしています。Flaskでは、いいねを行う場合にはPOST favorites/createを実行し、いいねを外す場合にはPOST favorites/destroyを実行します。

    @app.route('/favorites',methods=["POST"])
    def setFavorites():
        twitter = OAuth1Session(settings.CONSUMER_KEY,settings.CONSUMER_SECRET,settings.ACCESS_TOKEN,settings.ACCESS_TOKEN_SECRET)
        tweet_id = request.form.get('tweet_id')
        endpoint = "https://api.twitter.com/1.1/favorites/create.json?id={}".format(tweet_id)
        res = twitter.post(endpoint)
        json_res = res.json()
        status = getStatus(tweet_id)
        return status
    
    @app.route('/favorites',methods=["DELETE"])
    def deleteFavorites():
        twitter = OAuth1Session(settings.CONSUMER_KEY,settings.CONSUMER_SECRET,settings.ACCESS_TOKEN,settings.ACCESS_TOKEN_SECRET)
        tweet_id = request.form.get('tweet_id')
        endpoint = "https://api.twitter.com/1.1/favorites/destroy.json?id={}".format(tweet_id)
        res = twitter.post(endpoint)
        json_res = res.json()
        status = getStatus(tweet_id)
        return status
    


    Twitterのハートボタンのアニメーションは下記記事を参考にさせていただきました。
    yuyauver98.me

    コード

    コード全文はGithubにアップロードしているので、こちらからご確認ください。
    github.com

    結果


    まとめ

    今回Twitter APIを使用して、Twitter検索ツールを作成してみました。自動実行だと楽だったのですが、UIを持たせる仕様にしたかったので、それに余計に時間がかかってしまったと思います。また、普段使用しないTwitter APIの仕様を理解し、実装するのは結構難しいと感じました。


    現状のツールでは、使用出来る機能が少なかったり、UIなども完全なものではないので今後改善を図りたいと思います。こちらの関連で何か出来上がりましたら、続編という形で掲載しますので、ご期待ください。


    pythonTwitter APIを使用した自動投稿の記事も書いていますので、ぜひ見てください。
    cojimaru-chan.hatenablog.com

    *1:GET search/tweetsで取得したRetweetのデータとTwitterの結果を見比べたところ、favoritedが一致しなかったため、GET statuses/lookup - 複数のツイートを取得する も利用しています。

    現役エンジニアがテレワークアイテムを公開してみた

    f:id:cojimaru-chan:20210313221751p:plain

    どうも、現役エンジニアのこじまるです。
    テレワークを開始して1年になりました。
    そんな筆者が1年間の中で揃えたテレワークアイテムを公開します。


    テレワークが始まったけど、どのようなアイテムが必要なの?

    テレワークを実施している人はどのようなものを使っているんだろう?



    この記事では、このような悩みを解決します。

    今後テレワークをされる方やテレワーク実施中の方の参考になればと思います。

    現役エンジニアがテレワークアイテムを公開してみた

    パソコン環境の充実

    PCの作業スペースの追加 | ディスプレイ

    会社ではディスプレイを支給されているが、自宅にディスプレイが無い人も多いと思います。ノートパソコンの小さい画面だけでは、複数のウィンドウで画面が表示できず、作業効率も落ちます。


    作業効率を上げるため、ノートパソコン以外のディスプレイはあった方がいいです。解像度が4Kのディスプレイだと値段がかなり高いので、筆者はフルHDのディスプレイを使用しています。長時間の使用になるため、目に負担がかからないフリッカーフリーのディスプレイがいいと思います。

    周辺機器の接続をこれ一つで | USB Type-Cハブ(ドック)

    最近のノートパソコンはUSB Type-Cの接続口を設けているものが多く、HDMI、有線LAN、USB Type-Aなどの接続口を設けていないものが多いです。そのため、ハブを活用して、それらを接続できるようにしています。


    ノートパソコン以外の大きいディスプレイで作業し効率を上げること、有線LANを使って安定したネットワーク環境で作業をすることはテレワークで重要です。なので、ノートパソコンに接続口が無い場合には、ハブの導入をおすすめします。

    ビデオ会議をサポート | ヘッドセット

    テレワークの会議は、ZOOMやMicrosoft Teamsを活用したビデオ会議を利用しています。ビデオ会議には、マイクとイヤホンが一体となったヘッドセットやマイク付きイヤホンが使用されています。


    筆者は長時間の通話でも耳が痛くならないように、耳かけ方のBlue Toothヘッドセットを使用しています。一日中会議でヘッドセットを使用することもあるので、連続通話時間が長いものを選択しましょう。

    騒音遮断で作業が快適 | ノイズキャンセリング付きイヤホン

    住まいによっては周囲の騒音がひどく、作業に集中できないことも。周囲の騒音を遮断できるノイズキャンセリング付き密閉型イヤホンが有効です。


    AirPods Proはマイク付きイヤホンなので、ビデオ会議にも活用できます。

    姿勢の悪さの改善

    腰をしっかりホールド | 骨盤サポートチェア

    筆者はデスクワークをしていると、姿勢が悪くなり、腰痛や肩こりが頻繁に起こります。そのような場合に、骨盤サポートチェアは有効だと思います。

    骨盤からしっかり支えてくれるので、姿勢の悪い人は綺麗な姿勢が保ちやすくなります。筆者は骨盤から支えてくれるためか、長時間のデスクワークでの腰に対する負担が軽減されたと感じています。

    目線が上がることで姿勢が良くなる | ノートパソコンスタンド

    ノートパソコンを長時間使用していると、目線が下を向き猫背になりがち。それにより、腰痛や肩こりなどを引き起こすことにもつながります。


    ノートパソコンスタンドを活用することで、目線が上がり自然と姿勢が良くなります。デスクワークで猫背になりがちな方におすすめです。

    テレワークスペース

    参考程度の紹介になりますが、筆者のテレワークスペースは下記のようになっています。
    f:id:cojimaru-chan:20210313224147j:plain

    まとめ

    今回筆者が使用しているテレワークアイテムについて、ご紹介させていただきました。テレワークアイテムは日常的に使うものだからこそ、作業効率化できるもの、健康のことを考慮したものを揃えるのがいいです。

    テレワークアイテムを探している方はぜひ参考にしてみてください。

    ノートパソコンスタンド BoYataを購入!! 使用感をレビューしてみた

    f:id:cojimaru-chan:20210312012244j:plain

    はじめに

    どうも、現役エンジニアのこじまるです。
    最近購入しましたテレワークで使えるおすすめグッズを紹介します。

    今回紹介する商品はAmazon ノートパソコンスタンド売上第一位のBoYata N19です。

    ノートパソコンスタンドを活用することで、視点が上がって自然と姿勢が良くなります。デスクワークで猫背になりがちな方の姿勢改善につながります。

    BoYataって聞いたことあるけど、どんな商品だろう?

    BoYataのノートパソコンスタンドを購入する前に使用感が知りたい



    この記事では、このような悩みを解決します。

    BoYataとは

    BoYataとは中国の深圳(しんせん)にある企業で、ノートパソコンスタンドやスマートフォンのスタンドなどの機器を販売しています。

    BoYataのノートパソコンスタンドとは

    BoYataのノートパソコンスタンドといえば、Amazon ノートパソコンスタンド 売れ筋ランキング第一位を取るほどの人気商品です。BoYataが販売しているノートパソコンスタンドは下記の二点です。


    筆者は下記のうち、N19のノートパソコンスタンドを購入しました。


    製品名 N19 N21
    画像
    スタンド寸法(幅x奥行き) 298x254mm 295x210mm
    重量 1.34 Kg 1.32 Kg
    対応インチ 10-17インチ 11-17.3インチ
    耐荷重 20kg
    カラー シルバー・ダークグレー・ブラック・シャンパンゴールド・ピンク
    Amazonランキング 第1位 第5位


    BoYata ノートパソコンスタンドを購入した理由

    筆者は以前から腰痛持ちで猫背なので、姿勢を矯正するためにノートパソコンスタンドが欲しいと思うようになりました。数ある商品の中でBoYataのノートパソコンスタンドを選択した理由は下記の通りです。

    BoYataのノートパソコンスタンドを購入した理由
    • スタンド上でノートパソコンをキータイプしてもぐらつかないほど頑丈
    • スタイリッシュなデザイン
    • Amazon ノートパソコンスタンド 売れ筋ランキング第一位

    キータイプしてもぐらつかないかは、動画配信サイトやAmazonレビュー、ブログなどを見て、問題ないと判断しました。

    開封してみた

    筆者が購入したのはN19のダークグレーです。箱に描かれている商品画像は購入した商品のカラーによらず、シルバーのカラーが使われているみたいです。
    f:id:cojimaru-chan:20210311012415j:plain


    Amazonの商品画像を見てダークグレーを選択しましたが、商品画像よりもかなり暗いです。
    f:id:cojimaru-chan:20210310221108j:plain


    ヒンジの角度を変更してみました。上からの荷重に対してぐらつかないために、角度を変更するのにかなり力が必要になっています。
    f:id:cojimaru-chan:20210310223136j:plain


    試しにノートパソコン(Surface book3)をスタンドに載せてみました。こちらのノートパソコンの重量は1642g、インチは13.5インチになります。
    f:id:cojimaru-chan:20210310221317j:plain


    ノートパソコンを載せてもぐらつきもしないです。
    f:id:cojimaru-chan:20210310221336j:plain


    普段テレワークで使用するデスクに配置してみました。ノートパソコン上のキーボードでタイプしても、ほとんどぐらつきませんね。
    f:id:cojimaru-chan:20210310222803j:plain

    良かった点

    ・キータイプしてもぐらつかないほど頑丈
    ・無段階で高さや角度の調整が可能

    キータイプしてもぐらつかないほど頑丈

    他のノートパソコンスタンドでは、キータイプする時にぐらつくものなどもあったりします。しかし、BoYataのノートパソコンスタンドはかなり力を加えないとヒンジの角度を変更できないほど頑丈です。なので、ノートパソコンのキータイプ程度ではぐらつきません。

    無段階で高さや角度の調整が可能

    ノートパソコンスタンドによっては、段階的にしか高さや角度を変更できない商品などもあったりします。BoYataのノートパソコンスタンドは段階なしで高さや角度を変更できるため、自分が見やすい高さでノートパソコンを使用することが出来ます。

    悪かった点

    ・重量があり、ノートパソコンスタンドを持ち運ぶ場合に適していない

    重量があり、ノートパソコンスタンドを持ち運ぶ場合に適していない

    個人的に、想像していた以上に重量があります。出先でノートパソコンスタンドを使用したいという方にはあまり向いていない商品だと思います。

    結論

    結論として、BoYataのノートパソコンスタンド N19はかなり良い商品です。

    少し値段は張りますが、ノートパソコンスタンドを探している方にとって、納得する一品だと思います。ぜひ検討してみてください。