書籍「退屈なことはPythonにやらせよう」を教本にした「投資家のためのプログラミング勉強会(投プロ会)」の第7回目です。
これまでの勉強会では、プログラミングのベースとなる「変数」「条件分岐」「ループ」を学んできましたが、このあたりで復習を兼ねて、第1回からの演習を自力でやってみることをお勧めします。
プログラミングはコードをただ書き写しているだけではできた気になるだけで、いざやりたいことを書こうとしてもなかなか手が動かない、といったことが多いです。
二度、三度と自分の手でコードを書くことで基礎を固めていく方法がお勧めです。英語でもなんでも同じだと思いますが、基礎を固めることで、新しいことを勉強する際にいちいち後ろに戻って確認するストレスが無くなり、学習効率が上がるというわけです。
ということで、本日は文字列に対する操作を学びながら、前回作ったアメカブくんを改良してきましょう。
アメカブくんは指定したティッカーの企業名で米国のニュースサイトを検索し、日本語に翻訳してくれるアプリでした。こんな感じです。
今回はさらに、ニュースの題名にキーワード(例えば「配当」)という単語が含まれていたら、【】で囲んで目立つように表示形式を変えてみましょう。
例:
元の文:アトラシラン(TEAM)は本日の決算発表で配当を$10とする増配をアナウンスしました。
変更後:アトラシラン(TEAM)は本日の決算発表で【配当】を$10とする増配をアナウンスしました。
#ニュースは私の妄想です(^^)
元の文をこのように変更するためには、文字列操作について以下の事項を学ぶ必要があります。
- 文字列中でキーワード「配当」を検索して、この単語が含まれているか否かを調べる
- 含まれている場合は位置情報(インデックス)を取得する
- 文字列中の指定した位置に文字【と】を挿入する
それでは文字列中でキーワードを検索する方法から学んで行きましょう。
と、その前に、Pythonにおける文字列の約束事を一つだけ確認しておきましょう。
変数に文字列をセットする際はこれまでこのようにしてきました。
1 2 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" print(news) |
ここで重要なのは 、Pythonにおける文字列は “(ダブルコーテーション)で囲むということです。試しに ” で囲まないで変数を宣言した下記のプログラムを実行してみてください。
1 2 |
news = アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。 print(news) |
エラーになりましたか?
Pythonは文字列を ” で囲んではじめてそれが文字列であることを認識します。逆に ” で囲まないと文字列以外の数値型や他の特殊な型として認識しようとしておかしな動きになりますので「Pythonにおける文字列は ” で囲む」を常に意識してください。
ちなみに ” ではなく ‘ のシングルコーテーションで囲んでも同じ意味となります。最新のPythonの文法ではシングルコーテーションで囲むとなっていますが、他の言語ではダブルコーテーションの ” で囲む場合も多いということと、筆者の趣味もあり、本勉強会では ” で囲む記法を採用しています。
どちらを使ってもOK なのですが、他の人がコードを読んだ時に混乱しますので、混在させるのではなく、どちらかに統一するようにしてください。
それでは本題の文字列中でキーワードを検索する方法を学んでいきます。
Pythonで文字列を検索するにはfind()関数を使います。find()関数は指定した文字が存在する場合はその位置情報を返します。それではさっそく試してみましょう。
1 2 3 4 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" key_word = "配当" index = news.find(key_word) print(index) |
newsという変数にニュース文を、key_wordという変数に検索したい単語 “配当” をセットして、newsの中にkey_wordが含まれるかどうかをfind()関数で調べて、位置情報をindexという変数にセットしています。
上記の実行結果は 21 と表示されました。
それではkey_wordを “配当” から “TEAM” に変更して再度実行してみましょう。
今度は 7 と表示されました。
このindex変数にセットされた位置情報は何を表しているのでしょうか?
ピンとこない場合は、さらにkey_wordを “アトラシアン” にして実行してみましょう。(すでに分かっている方は出力結果を予想してみてください)
1 2 3 4 5 6 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" # key_word = "配当" key_word = "アトラシアン" index = news.find(key_word) print(index) |
今度は 0 と表示されました。
そうです、このfind()関数は指定したキーワードが何文字目に現れるかという位置情報を返してくれる関数なのです。
最初に指定した “配当” は22文字目に現れますし、”TEAM” は8文字目、”アトラシアン” は1文字目に現れます。
前回学習したリストと同じく、Pythonに限らずプログラミング言語の多くでは、順番を表現する際は0から開始しますので、それぞれ 21 / 7 / 0 と表示されたわけです。
それではもう一つ、存在しないキーワードをfind()関数にセットした場合 indexに何が返ってくるでしょうか。(実際に手を動かしてやってみてくださいね〜)
1 2 3 4 5 6 7 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" # key_word = "配当" # key_word = "アトラシアン" key_word = "マイクロソフト" index = news.find(key_word) print(index) |
はい、存在しないという意味で位置情報としてはありえない -1 という数字が返ってきました。
ではfind()関数のこれらの性質を利用して簡単なプログラムを作ってみましょう。
news変数の文字列の中に、キーボードから入力した文字が入っているかどうかを判断して、
文字が入っていた場合:「あり!」
文字が入っていなかった場合:「なし!」
それぞれ上記の表示をするプログラミングを書いてみましょう。もちろん前回学習した条件分岐のifとwhileループも使いますよ。(想定時間:5min)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" while True: print("キーワードをいれてください") key_word = input() if key_word == "END": print("終了します") break index = news.find(key_word) if index != -1: print("あり!") else: print("なし!") |
書き方はいろいろありますが私は上記のように作りました。まず最初に入力された文字が “END” であればプログラムを終了する処理を入れています(これがないと無限ループになってしまいます)。
そして、入力された文字をfind()関数に渡して、その結果が -1 かどうかでnews変数の文字列中にあるかないかを判断しています。( != は == の反対で、Falseの場合にTrueとして条件分岐する比較演算子です。詳しくは教本の2.2を参照してください)
いかがでしたか?このアプリを実行して、いろいろな単語や文字を入力しながら正しく動くかどうかを確認してみてください。
ちなみに、文字列の中に単語があるかないか「だけ」を調べたいときはin という演算子も使えます。(もちろんfind()関数をつかってもよいのですが、より直感的なコードにするために in を使うことをお勧めします)
1 2 3 4 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" key_word = "配当" print(key_word in news) |
newsという変数にニュース文を、key_wordという変数に検索したい単語「配当」をセットして、newsの中にkey_wordが含まれるかどうかをinという演算子で調べて、その結果をprint()関数で表示しています。in は含まれていればTrue、そうでなければFalseを返します。
さきほどの課題を in を使って書くとこのようになります。
1 2 3 4 5 6 7 8 9 10 11 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" while True: print("キーワードをいれてください") key_word = input() if key_word == "END": break if key_word in news: print("あり!") else: print("なし!") |
inはいろいろなところで出てくる演算子です。第5回のリストに要素が含まれるか、forループでリストから順番に要素を取得する場面などです。
1 2 3 4 |
stock_codes = ["1301","1332","1333","1352","1376","1377","1379"] code = "1352" flg = code in stock_codes print(flg) |
上記は東証の株価コードをリスト変数に入れて、指定したコードがそのリストに含まれているかを確認しています。考え方は文字列内の単語検索と同じですので便利なinを是非習得してください。
それでは、キーワードが現れる位置情報が取れるようになったところで、指定した箇所に【と】のカッコを挿入して、配当という文字が目立つように改造していきましょう。
Pythonにおける文字列の扱いで便利な点がスライスという考え方です。Pythonでは文字列をあたかもリストのごとく扱うことができます。
1 2 3 4 5 6 7 8 9 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" char1 = news[0] char2 = news[5] char3 = news[-1] print(char1) print(char2) print(char3) |
ア
ン
。
と表示されたかと思います。このように文字列変数に[]をつけてインデックスを入れるとその位置の文字を取り出すことができるのです。もちろんリストと同様に -1 をいれると後ろから一つ目の文字を取得できます。
さらに便利なのがスライスもリストと同じように扱えます。
1 2 3 4 5 6 7 8 9 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" str1 = news[0:6] str2 = news[13:] str3 = news[:12] print(str1) print(str2) print(str3) |
news[0:6]とすると0番目から6番目の文字列を抽出できます。news[13:]と終わりのインデックスを省略すると、その文字列の最後までを切り取ります。どうようにnews[:12]は最初のインデックスを省略しているのでnews[0:12]と同じ意味となります。(スライスとは切り取るということですね)
では上記の表示結果を確認してみてください。
アトラシアン
本日の決算発表で配当を$10とする増配決議をしました。
アトラシアン(TEAM)
str1〜str3の変数にそれぞれ上記の文字列がセットされたことがわかると思います。(スライスについては教本の6.1.2も参照ください)
ここまでくれば、”配当” の前後に【】をつけた文字列を作るのは簡単です。
考え方としては以下の順番でプログラミングするとよいでしょう。
- “配当” の文字が現れるインデックスを取得する
- 取得したインデックスの前後の文字列をスライスで変数にそれぞれセットする
- 【】をそれぞれの変数に加えて作りたい文字列を生成して表示する
ちなみに文字列同士をくっつけるのは +演算子を使います。使い方は第1回で学んでいますので確認してください。
ではアメカブ君の改造の前に、さきほど作ったfind()関数を使ったプログラムを改造して、指定した文字列の前後に【】をつけて表示するプログラムを作ってみましょう。
ちょっと頭をひねる必要がありますが、ポイントは文字列をどのようにスライスして、最後にどのようにくっつけるか、です。それでは Let’s try! (目安時間:10min)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
news = "アトラシアン(TEAM)は本日の決算発表で配当を$10とする増配決議をしました。" while True: print("キーワードをいれてください") key_word = input() if key_word == "END": break index = news.find(key_word) if index != -1: str1 = news[:index] str2 = news[index+len(key_word):] print(str1 + "【" + key_word + "】" + str2) else: print("なし!") |
いかがでしたか?うまくスライスできましたか?文字列を切り取る際のlen()関数の使い方も要チェックですのでしっかり理解していきましょう。
この技がマスターできれば様々な文字列操作が行えますので、例えば企業名から特定の文字を削除したり、URLの文字列を自在に作成したりなど、後々必要になってくる文字列操作の基礎ができたとも言えますよ。
それでは本日最後の課題です。先日作ったアメカブ君を改造して、配当という文字の前後に【】をつけて表示するアプリにバージョンアップしてみましょう。前回作成したアメカブ君はこのようなものでした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#------- ここからコピペする ----------# import urllib import urllib.parse from bs4 import BeautifulSoup import requests from time import sleep def get_stock_news(company): ret = [] googlenews = "https://news.google.com/search?q={}&hl=en-US&gl=US&ceid=US:en".format(urllib.parse.quote(company)) print(googlenews) r = urllib.request.urlopen(googlenews) html = r.read() sp = BeautifulSoup(html, "html.parser") for tag in sp.find_all("article"): url = tag.find("a").get("href") title = tag.find("span").getText() if url is None or title is "": continue if "article" in url: ret.append(title) if len(ret) == 10: break return ret def translate_en_jp(en): sleep(1) url = "https://translate.google.com/translate_a/single" headers = { "User-Agent": "GoogleTranslate/5.9.59004 (iPhone; iOS 10.2; ja; iPhone9,1)" } params = { "client": "it", "dt": ["t", "rmt", "bd", "rms", "qca", "ss", "md", "ld", "ex"], "dj": "1", "q": en, "tl": "ja" } res = requests.get(url=url, headers=headers, params=params) res = res.json() return f'{res["sentences"][0]["trans"]}' #------- ここまでコピペする ----------# while True: print("--- 検索したいニュースのティッカーコードを入れて下さい:") ticker = input() if ticker == "end": print("終了します") break flag = ticker in us_stocks.keys() if flag: print("ニュースを検索しています...") company = us_stocks.get(ticker) news = get_stock_news(company) for n in news: trans = translate_en_jp(n) print(trans) else: print("有効なティッカーをいれてください") |
翻訳結果の文字列がセットされている trans変数をそのままprint()関数で表示するのではなく、find()関数をつかって配当という文字が見つかったら【】をつけて表示するように改造してみましょう。(所要時間:5min)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
#------- ここからコピペする ----------# import urllib import urllib.parse from bs4 import BeautifulSoup import requests from time import sleep def get_stock_news(company): ret = [] googlenews = "https://news.google.com/search?q={}&hl=en-US&gl=US&ceid=US:en".format(urllib.parse.quote(company)) print(googlenews) r = urllib.request.urlopen(googlenews) html = r.read() sp = BeautifulSoup(html, "html.parser") for tag in sp.find_all("article"): url = tag.find("a").get("href") title = tag.find("span").getText() if url is None or title is "": continue if "article" in url: ret.append(title) if len(ret) == 10: break return ret def translate_en_jp(en): sleep(1) url = "https://translate.google.com/translate_a/single" headers = { "User-Agent": "GoogleTranslate/5.9.59004 (iPhone; iOS 10.2; ja; iPhone9,1)" } params = { "client": "it", "dt": ["t", "rmt", "bd", "rms", "qca", "ss", "md", "ld", "ex"], "dj": "1", "q": en, "tl": "ja" } res = requests.get(url=url, headers=headers, params=params) res = res.json() return f'{res["sentences"][0]["trans"]}' #------- ここまでコピペする ----------# us_stocks = {"PEP": "PepsiCo","JNJ": "Johnson & Johnson","PG": "Procter & Gamble","GIS": "General Mills", "KO": "The Coca-Cola Co", "MO": "Altria Group", "PM": "Philip Morris International", "BUD": "Anheuser Busch Inbev NV", "CL": "Colgate-Palmolive Company", "UL": "UNILEVER N.V. Common Stock", "XOM": "Exxon Mobil Corporation","GILD": "Gilead Sciences","T": "AT&T","HRL": "Hormel Foods", "V": "Visa","OKTA": "Okta","TEAM": "Atlassian"} while True: print("--- 検索したいニュースのティッカーコードを入れて下さい:") ticker = input() if ticker == "end": print("終了します") break flag = ticker in us_stocks.keys() if flag: print("ニュースを検索しています...") company = us_stocks.get(ticker) news = get_stock_news(company) for n in news: trans = translate_en_jp(n) index = trans.find("配当") if index != -1: str1 = trans[:index] str2 = trans[index+len("配当"):] print(str1 + "【" + "配当" + "】" + str2) else: print(trans) else: print("有効なティッカーをいれてください") |
いかがでしたか?上のプログラムでは配当という文字が入っていたら【】をつけて表示、なければそのまま表示という条件分岐を追加しています。実際に試してみてアメカブ君の使い心地がよくなったか実感してみてください。
今回もループと条件分岐がたくさん出てきましたが、前回同様、プログラミングはループと分岐でできているといっても過言ではありません、ぜひ慣れていきましょう。
本日はここまでです。お疲れ様でした!