Slackbot備忘録(3) ～スクレイピング&OCR編～

# はじめに
pythonを学ぶからにはスクレイピングをしてみたい…
そこで、今日のニュースとSteamの人気ゲームtop5をスクレイピングして表示するbotを作成。
ついでに、botでOCRして欲しいとの依頼があったりなかったりしたので、そちらも作成。

# 環境
- Windows10 64bit
- python3

# スクレイピング
## 作ったbot

- Steam人気ゲーム教えてbot

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201826/38b0b206-3af5-c3d1-ba03-ac472397faef.png)

- 主要のニュース教えてbot

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201826/7c84df47-4d81-a75d-32f5-372ca40c62d2.png)

## Slackbotの作成方法
は以前に書いた[こちら]()の記事をご覧ください。

## Beautiful Soupのインストール

下記のコマンドを開発環境内でインストール
```pip install beautifulsoup4```

## 要素の取得方法
基本的には、開発者モード(F12)を使って要素を抽出します。

- steamの場合

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201826/2aec842d-a170-0f77-5c11-61fd76eac97f.png)

赤い四角で囲った部分のElementsが

```html
<span class="title"></span>
```

とわかります。

- yahooニュースの場合

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201826/39cc939b-cd62-99cd-5e04-f20b7bc26e96.png)


![image.png](https://qiita-image-store.s3.amazonaws.com/0/201826/273c6365-77b3-7ea7-8781-0047da01a220.png)

赤い四角で囲った部分は基本的に以下のようなElementsになっていることがわかりました。

```html
<ur class="topics">
 <li class="topTpi">
  <div>
   <h1 class="ttl">
    <a href=…></a>
   </h1>
  </div>
 </li>
 <li class="topTpi">
  <div>
   <p class="ttl">
    <a href=…></a>
   </p>
  </div>
 </li>
 …以下省略
</ur>
```


## コード
- Steam人気ゲーム教えてbot 

```python:my_mention.py
@respond_to('Steamで人気のゲーム教えて')
def steam_reply(message):
    import urllib.request, urllib.error
    from bs4 import BeautifulSoup

    #Steamから人気のゲーム情報をスクレイピング
    url = 'http://store.steampowered.com/search/?filter=topsellers&os=win'
    html = urllib.request.urlopen(url)
    soup = BeautifulSoup(html, "html.parser")

    #span要素を取得
    span_tag = soup.select('.title')

    #ループをまわして配列にいれる
    for i in range(5):
        span_tag[i]
        #要素の文字列を取得
        span = span_tag[i].string
        messa = str(i+1) + '位' + span
        message.reply(messa)
```

- 主要のニュース教えてbot

```python:my_mention.py
@respond_to('主要のニュースを教えて')
def news_reply(message):
    import urllib.request, urllib.error
    from bs4 import BeautifulSoup
    #yahooニュースをスクレイピング
    url = 'https://news.yahoo.co.jp/'
    html = urllib.request.urlopen(url)
    soup = BeautifulSoup(html, "html.parser")

    #a要素を取得
    a_tag = soup.find('ul', attrs={'class' : 'topics'})
    tags = a_tag.find_all(attrs ={'class' : 'ttl'})
    for title in tags:
        message.reply(title.find('a').contents[0])
```

## コード解説

Steambotの方は、span要素からclassが"title"のものを抽出しました。
詳しい要素の取り方は[こちら](https://qiita.com/itkr/items/513318a9b5b92bd56185)を参考にしました。
この取り方一覧を参考にすれば無敵な気がします。

# OCR

## 作ったbot

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201826/3c4a9aaf-c276-4b43-c82d-7e75f3f47688.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/201826/d7f09edf-794c-1685-47c1-a8b551e9e307.png)


おやおや
精度はあまりよくないかもしれないです。

## ツールをインストール
[tesseract-ocr-setup-3.02.02.exe](https://sourceforge.net/projects/tesseract-ocr-alt/files/)をインストール


pythonにインポートさせるため、以下2つのコマンドを実行。
```pip install pyocr```
```pip install Pillow```

## プログラムの流れ
OCRを行う際に、どうしても自分のローカルフォルダに画像を保存する必要があったため、

Slackに画像をUP → ローカルフォルダ内に画像を保存 → OCR → 結果をSlackに投稿

という順序を取っています。 

## コード

"トークン"の部分ではSlackbotで取得したトークンを入力します。
Slackbot_Settings.pyで記載したものと同じです。
詳しくは[ここ](https://qiita.com/usomaru/items/529b6f40902ee1eda125#slack-bot%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E4%BD%9C%E6%88%90)のAPItoken部分を参照。

```python:my_mention.py
@listen_to('(.*)')
def testo_func(message, params):
    from PIL import Image
    import sys
    import pyocr
    import pyocr.builders
    import requests
    import shutil

    tools = pyocr.get_available_tools()

    if len(tools) == 0:
        message.reply('OCRツールないよ')
    else:
        if 'file' in message.body:
            
            url = message.body['file']['url_private']
            flag = message.body['file']['filetype']
            tmpfile = './tmp.' + flag

            token = 'トークン'
            res = requests.get(url, headers={'Authorization': 'Bearer %s' % token}, stream=True)

            #ファイル書き込み
            with open(tmpfile, 'wb') as fp:
                res.raw.decode_content = True
                shutil.copyfileobj(res.raw,fp)

            #ここからOCRの処理
            tool = tools[0]
            langs = tool.get_available_languages()
            lang = langs[0]

            im1 = Image.open('tmp.png')

            txt = tool.image_to_string(
                im1,
                lang=lang,
                builder=pyocr.builders.TextBuilder(tesseract_layout=6)
            )
            message.reply(txt)
```

## コード解説
まず、画像ダウンロード部分。画像の保存にはrequestsを使うらしいです。
url,flgで設定している値(message.body[~][~]部分)はSlackの[filetype](https://api.slack.com/types/file#authentication)を見て頂ければと。
ファイル書き込み部分に関しては[こちら](http://programming-study.com/technology/python-file/)のサイトを見て作成しました。
続いてOCR部分。
txt部分でおもにOCRの対象や言語、オプションの設定をしてます。
引数は画像(実行するディレクトリからの相対パスor絶対パス)、翻訳したい言語、OCRをする際のオプションです。


# さいごに
このbotを作ってから時間がたってしまいました。
うろ覚えな部分があるので、もう一回復習して解説を充実させたいと思います。


# 参考
- [PythonとBeautiful Soupでスクレイピング](https://qiita.com/itkr/items/513318a9b5b92bd56185)
- [Python3でPyOCRを用いてPacerのシェア画像から歩数抽出](http://nfnoface.hatenablog.com/entry/2016/11/06/115000)
- [slackbotでbotに投げられた画像をダウンロードする](http://swdrsker.hatenablog.com/entry/2017/09/08/113000)