Security Record

セキュリティ全般に関する情報を発信しています

Pythonでスクレイピングした結果をテキストマイニングしてLINEに送信する

アプリ説明

占いたい時期を西暦で入力して、上半期か下半期を選択。占いたい星座を入力すると、占いサイトより該当の星座占いをスクレイピングして、結果をテキストマイニングして画像を生成させます。 生成した画像はLINE Notifyを使用して自分のアカウントに送信されるようにしました。

生成される画像のイメージ

sample

使用したライブラリ

WordCloud
ワードクラウドの生成
https://pypi.org/project/wordcloud/

Janome
形態素解析エンジン
https://pypi.org/project/Janome/
https://github.com/mocobeta/janome

BeautifulSoup
スクレイピングツール
https://pypi.org/project/BeautifulSoup/
https://www.crummy.com/software/BeautifulSoup/bs4/doc/

python-dotenv
.envファイル読み込みに使用
https://github.com/theskumar/python-dotenv
https://pypi.org/project/python-dotenv/

スクレイピングと形態素解析についてはPHPで昔実験してた
形態素解析という言語処理ができるMecabを使ってみた
phpQueryを使ってWEBスクレイピングを試してみた

ライブラリのインストール

pip install janome
pip install wordcloud
pip install BeautifulSoup
pip install python-dotenv

作成したプログラム(全文)

import requests, os
from bs4 import BeautifulSoup
from janome.tokenizer import Tokenizer
from wordcloud import WordCloud
from collections import Counter
from dotenv import load_dotenv
#envファイルの読み込み
load_dotenv()
class Application:
    def __init__(self):
        self.__LINE_NOTIFY_API_KEY = os.environ['LINE_NOTIFY_API_KEY']
        self.__signDict = {
            '1'  : '牡羊座',
            '2'  : '牡牛座',
            '3'  : '双子座',
            '4'  : '蟹座',
            '5'  : '獅子座',
            '7'  : '乙女座',
            '8'  : '蠍座',
            '9'  : '射手座',
            '10' : '山羊座',
            '11' : '水瓶座',
            '12' : '魚座'
        }
        self.__periodDict = {
            '0'  : '一年',
            '1'  : '上半期',
            '2'  : '下半期'
        }
        #占い結果を出す西暦と星座を質問する
        self.__input_year   = input('西暦を指定してください\n\n')
        self.__input_Period = input('上半期・下半期を選択してください\n\n上半期 : 1\n下半期 : 2\n\n')
        self.__input_sign   = input('星座を選択してください\n\n牡羊座 : 1\n牡牛座 : 2\n双子座 : 3\n蟹 座 : 4\n獅子座 : 5\n乙女座 : 6\n天秤座 : 7\n蠍 座 : 8\n射手座 : 9\n山羊座 : 10\n水瓶座 : 11\n魚 座 : 12\n\n')
    #星座を選択
    def __selectSign(self):
        #URL生成    
        baseUrl = "https://voguegirl.jp/horoscope/shiitake{}-h{}/contents/".format(self.__input_year, self.__input_Period)
        print(self.__periodDict[self.__input_Period])
        if self.__input_sign == '1':
            #牡羊座
            load_url = baseUrl + "01aries"
        elif self.__input_sign == '2':
            #牡牛座
            load_url = baseUrl + "02taurus"
        elif self.__input_sign == '3':
            #双子座
            load_url = baseUrl + "03gemini"
        elif self.__input_sign == '4':
            #蟹 座
            load_url = baseUrl + "04cancer"
        elif self.__input_sign == '5':
            #獅子座
            load_url = baseUrl + "05leo"
        elif self.__input_sign == '6':
            #乙女座
            load_url = baseUrl + "06virgo"
        elif self.__input_sign == '7':
            #天秤座
            load_url = baseUrl + "07libra"
        elif self.__input_sign == '8':
            #蠍 座
            load_url = baseUrl + "08scorpio"
        elif self.__input_sign == '9':
            #射手座
            load_url = baseUrl + "09sagittarius"
        elif self.__input_sign == '10':
            #山羊座
            load_url = baseUrl + "10capricorn"
        elif self.__input_sign == '11':
            #水瓶座
            load_url = baseUrl + "11aquarius"
        elif self.__input_sign == '12':
            #魚 座
            load_url = baseUrl + "12pisces"
        else:
            print('1~12の番号で入力してください')
            exit()
        return load_url
    #スクレイピングして占いの文章のみ抽出
    def __scraping(self):
        html  = requests.get(self.__selectSign())
        soup  = BeautifulSoup(html.content, "html.parser")
        str_list  = [n.get_text() for n in soup.select('section.textbody p')]
        sentence = ''
        for i in str_list:
            sentence += i
        return sentence
    #LINEに結果を通知する関数
    def __sendLineBot(self):
        url   = "https://notify-api.line.me/api/notify"
        token = self.__LINE_NOTIFY_API_KEY
        headers = {"Authorization" : "Bearer "+ token}
        message = self.__input_year + "年" + self.__periodDict[self.__input_Period] + "。" + self.__signDict[self.__input_sign] + "の占い結果を圧縮しました!"
        payload = {"message"   :  message}
        files   = {"imageFile" : open('./sample.png','rb')}
        post    = requests.post(url, headers=headers, params=payload, files=files)
    #WordCloudでテキストマイニングを生成させる
    def main(self):
        token      = Tokenizer()
        token_text = token.tokenize(self.__scraping())
        words = ''
        word_list = []
        for i in token_text:
            pos = i.part_of_speech.split(',')[0]
            word = i.surface
            stopwords = [
            'こと','もの','それ','あれ','の','これ','ため','ん','何','みたい',
            '自分','あなた','今年','年','下半期','上半期','星座','星','座',
            '牡羊座','牡羊','羊',
            '牡牛座','牡牛','牡','牛',
            '双子座','双子',
            '蟹座','蟹',
            '獅子座','獅子',
            '獅子座','獅子',
            '乙女座','乙女',
            '天秤座','天秤',
            '蠍座','蠍',
            '射手座','射手',
            '山羊座','山羊',
            '水瓶座','水瓶',
            '魚座','魚'
            ]
            if pos == '名詞' and word not in stopwords:
                words = words + ' ' + word
                word_list.append(word)
        wc = WordCloud(background_color="white", font_path=r'msyhbd.ttc', width=800, height=800)
        wc.generate(words)
        wc.to_file('sample.png')
        counter = Counter(word_list)
        frequent_word = counter.most_common(30)
        print(frequent_word)
        self.__sendLineBot()
if __name__ == '__main__':
    app = Application()
    app.main()

gitHubにソースコードあります

https://github.com/ysk/python_Scraping_textMining

まとめ

テキストマイニングの結果がなんか微妙なので例外にする単語登録を増やしたほうが良いのだろうか? Pythonの練習のために作った使いみちが微妙なツールだけど、webスクレイピングやテキストマイニングについて、概要を把握することはできたぁなと。 普段実務でPHPを触ることが多いのだけど、Pythonの方がなんかワクワクする。