勢いで作りましたがまだ@slam_hubさんの許可をもらってないので場合によっては後で消します><
許諾をいただきました。どうもありがとうございます!
概要
- 特定のTwitterアカウントからTweetを抽出
- YoutubeやArxivのURLから画像やタイトルを抽出
- 整形してMarkdown(Marpスライド形式)にて吐き出す
- 作業環境:Python3.7 on Windows10,+ Docker環境
こうなる
工程
- Tweet情報を抽出
- リンクのURLからコンテンツを抽出
- データ選別など後処理
- 整形,文章作成
Twitter情報を抽出
TwitterからTweetをスクレイピングします。ざっと思いつく手法は以下の通り。
- Twitter APIを使う方法 PythonによるWeb API(4) 指定したツイッターアカウントのツイートを全抽出する|Dai|note
- TweetScraperを用いる方法
- TwitterScraperを用いる方法
基本的には最近面倒になったTwitter APIの申請が必要ですが,いくつか抜け道もあるようです。
ここでは以下の記事で取得したツイート群を使用します。
このTwitterScraperで抽出したjsonファイルは辞書型の要素を詰めたリストになります。(長いので詳細を御覧ください)
{'has_media': True, 'hashtags': [], 'img_urls': ['https://pbs.twimg.com/media/Ec2t_Y-UcAAYEyM.png'], 'is_replied': True, 'is_reply_to': False, 'likes': 26, 'links': ['https://arxiv.org/abs/2004.01793'], 'parent_tweet_id': '', 'replies': 1, 'reply_to_users': [], 'retweets': 9, 'screen_name': 'slam_hub', 'text': '画像からの物体方向推定を,self-supervisedに学習する枠組みを提案.画像から3次元方向とスタイル特徴量を抽出し,それらの潜在変数を元に幾何学的変換を行うGenerator(GAN)を用いて学習.損失には一貫性と水平対称性を利用し,教師あり学習に匹敵する性能を達成.\nhttps://arxiv.org/abs/2004.01793\xa0pic.twitter.com/Qr4LM3uTxA', 'text_html': '<p class="TweetTextSize TweetTextSize--normal js-tweet-text tweet-text" data-aria-label-part="0" lang="ja">画像からの物体方向推定を,self-supervisedに学習する枠組みを提案.画像から3次元方向とスタイル特徴量を抽出し,それらの潜在変数を元に幾何学的変換を行うGenerator(GAN)を用いて学習.損失には一貫性と水平対称性を利用し,教師あり学習に匹敵する性能を達成.\n<a class="twitter-timeline-link" data-expanded-url="https://arxiv.org/abs/2004.01793" dir="ltr" href="https://t.co/rEGIDLI41f" rel="nofollow noopener" target="_blank" title="https://arxiv.org/abs/2004.01793"><span class="tco-ellipsis"></span><span class="invisible">https://</span><span class="js-display-url">arxiv.org/abs/2004.01793</span><span class="invisible"></span><span class="tco-ellipsis"><span class="invisible">\xa0</span></span></a><a class="twitter-timeline-link u-hidden" data-pre-embedded="true" dir="ltr" href="https://t.co/Qr4LM3uTxA">pic.twitter.com/Qr4LM3uTxA</a></p>', 'timestamp': '2020-07-14T03:02:09', 'timestamp_epochs': 1594695729, 'tweet_id': '1282872986467397633', 'tweet_url': '/slam_hub/status/1282872986467397633', 'user_id': '1244129482132209664', 'username': 'SLAM-Hub', 'video_url': ''}
今回使うのは主に以下の3つになります。
- text:ツイートの本文
- link:ツイートに紐付けられたメディアのURL(Youtube, Aixivなど)
- img_url:ツイートに直接貼り付けられた画像のURL
ここから,スライド形式の資料のタイトル,本文,説明画像を1セットにして資料を集めていくというのが今回の流れです。
コンテンツ作成
先程抽出したデータからサーベイ資料に載せるコンテンツを作成していきます。
- タイトル≒論文題目
- テキスト≒ツイッターの説明テキスト
- サムネ画像≒代表的な画像
の3つが必要と考えます。
arxivの論文情報をスクレイピング
最も多いリンクはarxivのURLです。ここからタイトルを抽出します。
PythonでarXiv APIを使って論文情報取得、PDFダウンロード | note.nkmk.me
str形式で与えられるarxivid(例:"110.680”)を抽出すれば以下のようにタイトルを得られます。
import arxiv # get arxiv title arxiv.query(id_list=[arxivid])[0]["title"]
linkのURLからIDを抽出します。
- スラッシュでsplitして後ろを抽出
.pdf
が混じっている場合があるので非数字を除去- 番号の最後に英字が入る場合があるので先頭だけを見て判断
# .pdfが混じっている時がある。数字だけを抽出するようにする。 arxivid_ = 'https://arxiv.org/abs/1910.14139.pdf'.split('/')[-1] ids = [s for s in arxivid_.split('.') if s[0].isdigit()] arxivid__="" for i in range(len(ids)): arxivid__ += ids[i]+"." arxivid = arxivid__[:-1]
途中の数値部分のみを抜き出すロジックは以下のサイトを参考にしました。 regex - How to extract numbers from a string in Python? - Stack Overflow
Youtubeをスクレイピング
同様に多いlinkはyoutubeのリンクです。ここからはサムネイル画像とタイトルを抽出します。
Youtubeにアクセスして情報を抜き出すのは以前APIを取得してやっていましたが,APIの管理は正直面倒です。
今回は以下に示すyoutube-dlというAPIなしで使えるコマンドラインツールを用います。
導入はpipで問題なく入れられます。
pip install youtube-dl
コマンドラインツールなので,subprocess.check_output
を用いてコマンドライン出力を取得します。
import subprocess imgurl = subprocess.check_output("youtube-dl --get-thumbnail \""+URL +"\"", shell=True) title = subprocess.check_output("youtube-dl --get-title \""+ URL +"\"", shell=True)
帰ってくるURLの文字列には時々特殊文字が入っているのでエスケープが必要です。
まとめコード
- 辞書型の要素を格納するリストを作る。
- タイトル,本文,画像URLをそれぞれ加えて行く。
例外で画像だけがある場合もあったのでその時はタイトルは適当にして処理しました。
長いので詳細をクリックして見てください。
import arxiv import subprocess docdics = [] for dics in sdic: ddic = {} title = "" imgurl = "" if dics['links']:# if has link # linkの最後尾をタイトルにする title = dics['links'][0].split('/')[-1] # Youtubeだった場合 if 'youtu' in dics['links'][0]: # if it is youtube link imgurl = subprocess.check_output("youtube-dl --get-thumbnail \""+dics['links'][0] +"\"", shell=True) title = subprocess.check_output("youtube-dl --get-title \""+ dics['links'][0] +"\"", shell=True) # Arxivだった場合 if 'arxiv' in dics['links'][0]: arxivid_ = dics['links'][0].split('/')[-1] ids = [s for s in arxivid_.split('.') if s.isdigit()] arxivid__="" for i in range(len(ids)): arxivid__ += ids[i]+"." arxivid = arxivid__[:-1] title = arxiv.query(id_list=[arxivid])[0]['title'] if dics['img_urls']: imgurl = dics['img_urls'][0] # linkはないけど画像はある時 elif dics['img_urls']: imgurl = dics['img_urls'][0] title = "image only" ddic['id'] = dics['tweet_id'] ddic['title'] = title ddic['imgurl'] = imgurl ddic['text'] = dics['text'] docdics.append(ddic)
データ後処理
ここからはヒューリスティックな処理が続きます。
- 広報ツイートなどをフィルタリングする
- youtube-dlが返す文字がbyte文字列なので補正する(最初からやってればよかった)
広報ツイートをフィルタリングする
LinkのURLがあったとしても必ずしもサーベイ紹介Tweetとは限りません。(そらそうだ) 広報ツイートなどと区別するために,特定のキーワードを含んでいるかどうかでフィルタリングします。
# 広報ツイートが紛れ込んでいる。URLか本文のキーワードで落とすか。"可能","提案","実現"などのワードのどれかがはいっていないなら弾く。 filterwords = ["可能","提案","実現","推定","定式","最適化","生成","評価"] for docs in docdics: if not any(wd in docs["text"] for wd in filterwords): print(docs["text"])# テキスト閲覧
byte文字列をstrに変換
これは完全に私個人のミスですが,youtube-dlで得られる文字列がbyte文字列なのでこれをstrにデコードします。
# Encode to utf8 # string byteの混じった文字をstringに統一する # type(b'abcd') == bytes を用いる for docs in docdics: for dd in docs: if type(docs[dd]) == bytes: # .decode()でデコード
まとめコード
他にも, - タイトルの文字列に改行コードがあったのでスペースに変換
などの処理を行いました。
## filtering filterwords = ["可能","提案","実現","推定","定式","最適化","生成","評価"] filtereddics=[] for docs in docdics: if any(wd in docs["text"] for wd in filterwords): fdocs = docs # decode bytes for dd in fdocs: if type(fdocs[dd]) == bytes: fdocs[dd]=fdocs[dd].decode() # Remove \n from title fdocs["title"] = fdocs["title"].replace("\n"," ",10) filtereddics.append(fdocs)
.mdファイルに書き込み
markdown形式で最終的にまとめますがせっかくなのでMarp for vscodeで表示できるスライド形式に変換します。
- 改行してスライド区切り
---
を入力 - タイトル,本文を入力
- 画像を右端40%のfit形式で挿入
これを実行するコマンドは,
mkdocfile = open("survey.md","w+",encoding='utf-8') for mkdics in filtereddics: imgurl=mkdics["imgurl"] if imgurl: if imgurl[-1] == "\n": imgurl = imgurl[:-1] onepagestring = "\n---\n"+"## " + mkdics["title"]\ + "\n" + mkdics["text"]\ + "\n ![bg right:40% fit]("+imgurl+")\n" mkdocfile.write(onepagestring) mkdocfile.close()
以上!
Marpに落とし込む際は以下の記述を文頭に置きます。
--- marp: true header: 'from @slam_hub' size: 16:9 paginate: true ---
TODO・改善点
- 権限とかの問題(なぜ記事を先に書いた)
- Twitterのスクレイピング力不足でネストになっているツイートの追加情報を取得できていない
- タイトルに改行文字が含まれているので省く。
- サーベイツイート判定法がヒューリスティックに寄りすぎている
- キーワードごとに論文を分類したい。自然言語処理的なことをする?
ご意見・感想お待ちしております。
追記:Twintを用いた解析
次の記事でTwintを用いたTweet取得を試した所,結構上手く行ったのでDockerを用いた解析でも作成してみました。 Docker image形式なので環境を問わず実行できると思います。(所要時間10分くらい?)
参考記事: