Beautiful Soupで株の情報を取得する

## <a ref=https://nextpublishing.jp/book/10319.html>「株とPython ─ 自作プログラムでお金儲けを目指す本」</a>
<a ref=https://nextpublishing.jp/book/10319.html>「株とPython ─ 自作プログラムでお金儲けを目指す本」</a>を読んでいます。
株の基礎知識、PyQueryやseleniumを使ったPythonによるスクレイピングの基礎、SQLiteの簡単な使用例も記載されていて、初心者の私には痒い所に手が届く至れり尽くせりの本です。
残念なのは、いくつかのサンプルコードが、スクレイピング対象のホームページ変更により動作しないものがあることです。
著者さんも<a ref=https://github.com/BOSUKE/stock_and_python_book#readme>サポートページ</a>で対応をなさっているようですが、すべての変更に対応していくのはなかなか大変なことと察します。

## get_brands.py
get_brands.pyは、この本のリスト2.1にある 「銘柄情報を取得しSQLiteに格納する」コードです。　最初に構築するデータベースの基本情報をHPから取得するコードなのですが、情報取得先にうまく接続できません。　PyQueryでの接続がうまくいかないようです。
このデータベースが作成できないと先に進めないので、この本にあるseleniumを使って大体のコードを作ろうかな、と思いました。　ですが、いちいちブラウザが立ち上がるのも少し重すぎる気がしましたので、そこで本書では述べられていないBeautiful Soupを使って元コードを軽く改変してみることにしました。　必要な変更手順は以下のようになります。
<ol>
<ol>
<li>インポートするライブラリの変更</li>
<li>htmlを取得するコードの変更</li>
<li>htmlから情報を抽出するコードの変更</li>
</ol>
</ol>

### 1.インポートするライブラリの変更
元のコードではPyQueryを使用するためにPyQueryを使っているので、以下のような記述になっています。
>from pyquery import PyQuery

変更後のコードではPyQueryは使用しないため、このコードは不要で、その代わりにwebページへのアクセスにrequest、htmlのパーシングにBeautiful Soupを使用するため、以下のコードで置き換えます。
>import requests
from bs4 import BeautifulSoup

### 2.htmlを取得するコードの変更
元のコードではPyQueryを用いて'url'からhtmlのリクエストと取得を行っています。　理由は知りませんが、ここでコケるようです。
htmlを取得できたら、'div.company_block'を探して、なければ求めているフォーマットではないので、何もせずに抜けます。
>  q = PyQuery(url)
>
>  if len(q.find('div.company_block')) == 0:
    return None

変更後のコードは以下の通りです。
>  html = requests.get(url).text
  q = BeautifulSoup(html, 'html.parser')
>
>  if q.find('div', class_='company_block') == None:
    return None

Beautiful SoupはPyQueryと違ってhttpサーバーへのリクエスト機能はないようなので、requestsを使って'url'へリクエストを行います。　requestsの文法から、rest APIを使っているのでしょうか？　'html'にwebページのtextが得られたら、Beautiful Soupを使って'q'経由で'html.parser'による情報抽出ができるようにしています。
webページのデータが取得できたら、元のコードと同様にdiv>company_blockの有無をチェックしています。

### 3.htmlからの情報抽出するコードの変更
元のコードで情報を抽出している個所は以下の通りです、
>    name = q.find('div.company_block > h3').text()
    code_short_name =  q.find('#stockinfo_i1 > div.si_i1_1 > h2').text()
    short_name = code_short_name[code_short_name.find(" ") + 1:]
    market = q.find('span.market').text()
    unit_str = q.find('#kobetsu_left > table:nth-child(4) > tbody > tr:nth-child(6) > td').text()
    unit = int(unit_str.split()[0].replace(',', ''))
    sector = q.find('#stockinfo_i2 > div > a').text()

Beautiful Soupを使用したコードは以下のようになります。
>    name = q.select_one('#kobetsu_right > div.company_block > h3').string
    short_name = q.select('#stockinfo_i1 > div.si_i1_1 > h2')[0].contents[1]
    market = q.select_one('#stockinfo_i1 > div.si_i1_1 > span').string
    unit = int(q.select_one('#stockinfo_i2 > dl:nth-child(3) > dd').string[:-2])
    sector = q.select_one('#stockinfo_i2 > div > a').string

元のコードにある'code_short_name'と'unit_str'は、文字列加工のために一時的に使用する変数なので、変更後のコードでは省かれています。
Beautiful Soupを使用する場合も考え方は変わりませんが、CSSセレクタを使ったほうがすっきり書けそうです。
CSSセレクタ取得するには、読み込んだホームページをブラウザで表示させて、デベロッパーツールを使うと、対象となる部分のCSSセレクタをコピーすることができ、そのままコード内で使用できます。
short_nameは対象の文字列を直接指し示すCSSセレクタがありませんでしたので、一階層上の文字列を取得し、2番目の要素をcontents[1]で取得しています。　この辺、とても簡単に実現できて、Pythonが良くできていると言ったらよいのか、ライブラリが良くできていると言ったらよいのか、とにかく便利なのものだなぁと思います。

## まとめ
「株とPython ─ 自作プログラムでお金儲けを目指す本」のサンプルコード'get_brands.py'をBeautiful Soupを使用して書き換えてみました。　
PyQueryを使用したコードは、容易にBeautiful Soupを使用したコードに置き換えることができそうです。
参考になれば幸いです。