百済茄子

アマチュアプログラマによる主にPythonとDjangoのブログ。

自習室データ取得と継続日数ランキングの作成 ~PythonでWebスクレイピング1

   

           

<シチュエーション・やりたいこと>

FXの勉強会サイトを運営している。(掲示板CGI)
簡易的な「自習室」を作った。(スレッドを立てた)
参加者は次のように宣言と報告をしている。

例)
 宣言:22:00~23:00 チャートの分析をします。
 報告:★達成



宣言:書き方は自由。
報告:勉強が終わったら自己評価を書き込む。(できた★、まあまあ▲、できなかった△)

毎朝、前日分までのデータで「継続日数ランキング」を発表している。



現状、自作のPythonプログラム(make_ranking.py)を使いランキングを毎日集計している。流れは以下の通り。
1.サーバーから自習室の全データ(○○.cgi)をダウンロードする。(download.py)
2.そのままでは使えないようなので、○○.cgiを開き手動でコピーし、○○.txtへペースト。
3.make_ranking.pyでランキングを集計する。
4.結果を手動でサイトへコピペする。

4は使用している掲示板CGIの仕様で難しいっぽいので、1~3をワンクリックで行いたい。
新しいサイトが完成するまでの間に合わせ。)


○○.cgiを○○.txtと変換するコードは書けた
import os
import shutil

current_dir = '.'
filename = '○○.cgi'
new_filename = '○○.txt'
shutil.copy(os.path.join(current_dir, filename), new_filename)

しかし、エンコード云々でどうしても上手くいかない。
スクレピングでHTMLのデータをそのまま持ってきて使えば楽だと思う。

テストサイトを作った。

勉強会サイト
http://practice1123.stars.ne.jp/test/patio.cgi?mode=logoff
ログインID: mmm123
パスワード: mmm123

管理画面
http://practice1123.stars.ne.jp/test/admin.cgi?
パスワード: mmm123


現状で使っているもの
download.py
from ftplib import FTP

ftp = FTP('112.ooo.ooo.ooo')
ftp.login('XXXXXXX.jp', 'xxxxxxx')
ftp.cwd('/xxxx/xxxx/xxxx/xxx/')
files = ftp.nlst('○○.cgi')
for file in files:
    print(file)
    with open('' + file, 'wb') as f:
        ftp.retrbinary('RETR %s' % file, f.write)

ftp.quit()

参考:PythonでFTPからダウンロード


data.txt(使用しているデータ)
1<>無題<>ハンドルネーム<><>★達成<>2018/06/13(Wed) 09:48<>202.217.199.13<>aaaaaaaaaaa<><><>bbbbbbb<>123456789<>,,<>,,<>,,<>2<><>
2<>無題<>ハンドルネーム<><>★達成<>2018/06/13(Wed) 09:48<>202.217.199.13<>aaaaaaaaaaa<><><>bbbbbbb<>123456789<>,,<>,,<>,,<>2<><>
3<>無題<>ハンドルネーム<><>★達成<>2018/06/13(Wed) 09:48<>202.217.199.13<>aaaaaaaaaaa<><><>bbbbbbb<>123456789<>,,<>,,<>,,<>2<><>
4<>無題<>ハンドルネーム<><>★達成<>2018/06/13(Wed) 09:48<>202.217.199.13<>aaaaaaaaaaa<><><>bbbbbbb<>123456789<>,,<>,,<>,,<>2<><>
......
......


make_ranking.py(思いつくままに書いたので汚い)
import datetime
import re
import linecache


# 関数の良い名前が思いつかない
def make_dict(today=datetime.datetime.now().strftime('%Y/%m/%d')):
    """
    :param today: 指定日(文字列YYYY/MM/DDで指定)
    :return: {'ハンドルネーム': 日数}の辞書

    指定日の前日までの継続日数を集計し、辞書型で返す。
    """

    until_yesterday_dict = dict()

    # today(文字列)をdatetimeに変換
    today = datetime.datetime.strptime(today, '%Y/%m/%d')

    # 1行目の「最初の日付」を取得
    first_date_regex = re.compile(r'\d\d\d\d/\d\d/\d\d')
    mo = first_date_regex.search(linecache.getline('○○.txt', 2))
    now_date = datetime.datetime.strptime(mo.group(), '%Y/%m/%d')

    # ループ
    while True:
        if now_date > today:
            break

        # ○○.txtから1行ずつ読み込む
        with open('./○○.txt', encoding='utf-8') as fd:
            now_date_set = set()
            for row in fd:
                yesterday = now_date - datetime.timedelta(1)    # now_dateの1日前
                yesterday = yesterday.strftime('%Y/%m/%d')      # 文字列に変換

                # 昨日の日付が含まれている、かつ、★か▲か■が含まれている行を探す
                if yesterday in row and ('★' in row or '▲' in row or '△' in row):
                    handle_name_regex = re.compile(r'無題<>.*?<><>')
                    mo = handle_name_regex.search(row)
                    mo = mo.group()
                    # 不要な部分を削除
                    mo = mo.replace('無題<>', '')
                    mo = mo.replace('<><>', '')
                    now_date_set.add(mo)

            # 辞書のkeyのみの集合を作る
            until_yesterday_set = set(until_yesterday_dict)

            # 3つの分岐
            # ①today_setにもuntil_yesterday_setにもない(今日も書き込んだ)→加算
            add = now_date_set & until_yesterday_set
            # ②today_setにある、until_yesterday_setにない(今日から書き込んでいる)→新規(加算)
            new = now_date_set - until_yesterday_set
            # ③today_setにない、until_yesterday_setにある(やめちゃった)→削除
            delete = until_yesterday_set - now_date_set

            # add, newの内容をuntil_yesterday_dictに加算する
            add_new = add | new
            add_new_list = list(add_new)
            for a_n in add_new_list:
                if a_n in until_yesterday_dict:
                    until_yesterday_dict[a_n] += 1
                else:
                    until_yesterday_dict[a_n] = 1

            # deleteの内容をuntil_yesterday_dictから削除する
            delete_list = list(delete)
            for d in delete_list:
                del until_yesterday_dict[d]

            # 次の日の処理へ
            now_date += datetime.timedelta(1)

    return until_yesterday_dict


###############処理開始###############
if __name__ == '__main__':
    # today = '2018/6/25'     # 指定日

    # 辞書をvalue降順ソートする
    s = sorted(make_dict().items(), key=lambda x:x[1], reverse=True)
    until_yesterday_dict = dict(s)
    print(until_yesterday_dict)

    # ランキングに表示する日付(前日)
    today = datetime.datetime.now() - datetime.timedelta(1)   # 指定日の前日
    today = today.strftime('%Y/%m/%d')    # 文字列に変換

    # ranking.txtにランキングを出力する
    with open('./ranking.txt', 'w', encoding='utf-8') as fw:
        fw.write('<継続日数ランキング({}時点)>\n'.format(today))
        for k, v in until_yesterday_dict.items():
            if v < 3:
                continue
            fw.write('【{}日目】{}\n'. format(v, k))

        fw.write('(継続日数3日目以上のみ掲載。)')



次回、改善策を考える。


 - Python

コメント投稿はこちら

メールアドレスが公開されることはありません。

CAPTCHA


  関連記事

自習室データ取得と継続日数ランキングの作成 ~PythonでWebスクレイピング2

前回の続き。 勉強会サイト(テスト用) http://practice1123. …

関数、ドキュメンテーション文字列、help関数、再帰呼出し、位置・キーワード引数とデフォルト値、ラムダ式 ~Python問題集4

Q1.三角形の面積を求める関数を定義しなさい。 また、引数についての説明をドキュ …

自習室データ取得と継続日数ランキングの作成 ~PythonでWebスクレイピング5

前回の続き。 <今回やること> ・過去のランキングデータ(辞書型)を元に、その日 …

基本の基本、print、変数、演算子 ~Python問題集1

Q1.Pythonインタプリタを起動し’hello world!&# …

Python(3以上)問題集

基礎の基礎 基本の基本、print、変数、演算子 基本文法 インデックス、スライ …

自習室データ取得と継続日数ランキングの作成 ~PythonでWebスクレイピング4

前回の続き。 <今回やること> 必要なデータを、とりあえず全部取ってくる。 (そ …

for文、整数型への変換(キャスト)、メンバーシップ・テスト演算子in、理論演算子、ネストとbreak/continue文、range関数とreversed関数 ~Python問題集3

Q1.テストの成績exam_results = [20, 10, 46, 13, …

作ってみた

自習室データ取得と継続日数ランキングの作成 ~PythonでWebスクレイピング …

インデックス、スライス、ステップ、format、置換replace、リストの初期化、リストの連結、要素の追加・削除 ~Python問題集2

Q1.変数fibonacci = ‘11235813213455&# …

Pythonのexeファイルを作成するPyInstaller、Chromedriverのコンソールウィンドウが閉じない問題 ~自習室データ取得と継続日数ランキングの作成 ~PythonでWebスクレイピング6

前回の続き。今回でとりあえず終了。 完成したので、完成品とメモ書きを残す。 <プ …


PAGE TOP