新年度になり、やる気が出ないので調査が進まず、日記の更新が辛くなってきた……。
最近では記事を投稿した瞬間から次の日記内容に頭を悩ましている。
RSS登録者数13というゴミサイトなのに、胃を痛めて日記更新って……何してるんだろ……俺。
収益なんて月100円程度の完全なボランティア活動なのに、〆切の呪縛から逃れられないような捉えられない感覚に陥っている。
投稿内容も「専門的じゃなきゃ駄目だ」と、自分で投稿内容のハードルを上げてしまっていた……。
そもそも釣りに行けば、それだけで1つの日記が書ける。アクセス数も多いし。
でも、12月、1月、2月、3月、4月は、検見川浜突堤(千葉県内湾)では何も釣れない。寒いし。
夏前になればコノシロが釣れるようになり、夏以降はカタクチイワシ、秋にはサビキで五目釣りで爆釣になる。
しかしオフシーズンは、ヒイカやカレイ、ボラぐらいしかターゲットがいなくなるため、少なくともファミリーは来なくなる。
……と書いたものの、経験と各種ブログを見た私自身の感覚でしか無い。
もっと事実データに基づく結果が欲しい。
そもそも、感覚でモノを言う人は嫌いなんだよね。
主観ではない千葉県内湾(東京湾)の年間の釣果データ
みたいなモノって無いのかなぁ?
多くのサイトは広告収入目的だから、内容が浅いんだよなぁ。
なければ自分自身で作るしか無いけど、どうやって?
……。
ん、まてよ?
市原海釣り公園の釣果情報
「何も釣れない」
と揶揄されれつつも大人気の釣り公園
市原海釣り公園
金払っておかっぱ釣りをするのは、負けた気分になるので、施設を使ったことは無い。
でも、情報収集で活用している人は多いはずだ。
なぜなら、ご丁寧に每日、客数、釣果が記載されている。
200人以上の顧客の釣果を、どのように算出しているのだろう?
首から下げる入場券を返す場所におじちゃんが座ってて、そこで聞かれた!
桟橋にもおじちゃん達がいて、釣れてるのを見てカウントしてました!
と言う人もいるけれど、
釣りに行っても釣果を訊かれたことが無い!
と言う人もいる。
サンプル調査して平均値に来場者数をかける
会員の釣果を来場者数に拡大推計してる
など、適当なのかもしれない。
信憑性は低いものの、同じ機関が同じ感覚で集計している点は素晴らしい。
この釣魚結果を収集すれば、少なくとも事実に基づいた分析ができる気がしてる。
Python使ってスクレイピング
やる事はPython使ってスクレイピングするだけだ。
「robots.txt」でもスクレイピングはNGになっていない。
1 2 3 |
User-Agent:* Disallow:/admin/ Sitemap:https://ichihara-umizuri.com/sitemap.xml |
ただ厄介なのは、
人間が每日更新していること
記述方法がバラバラで誤記もあり、その辺りは目視で結果を確認しながら適時パース方法の更新が必要になる。
まあデータ分析の前処理(データクレンジング)の範疇だけどね。
数日間だけ取得してみた。
1 2 3 4 5 6 7 8 9 10 11 |
# # python parser.py 人数 潮 水温 天気 キス フッコ セイゴ タコ ギマ コウイカ クロダイ コノシロ アジ カイズ ホウボウ イシモチ メバル サバ ハゼ メジナ シロギス カサゴ 20200601 0 0 0 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 20200602 180 0 0 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 20200603 0 0 0 0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN 20200604 137 中潮 23.5 曇 10 1 3 3 1 9 3 2 2 1 NaN NaN NaN NaN NaN NaN NaN NaN 20200605 166 大潮 22.5 晴 45 0 0 2 0 14 3 2 0 0 1 NaN NaN NaN NaN NaN NaN NaN 20200606 226 大潮 24.0 晴 47 0 2 12 1 54 1 3 6 0 1 20 1 5 1 1 NaN NaN 20200607 208 大潮 22.0 曇 0 0 1 7 0 37 1 2 0 0 0 0 0 0 1 0 3 NaN 20200608 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 NaN 20200609 163 中潮 22.0 晴 26 7 1 21 0 36 8 1 3 0 4 1 0 0 0 0 0 1 |
正しく動いているが前処理は色々と必要そうだ。
セイゴやフッコはスズキ、カイズはクロダイに変換する。
「NaN」は「df.fillna(0)」でゼロに変換する。
入場者数が「200名様」と書いている場合と「200様」もあるので、両方取得可能にする。などなど。
月別の千葉県内湾の年間釣果グラフ
CSVをGoogle Chartを使ってグラフ化する。
Google Chartは初めて使ってみた。魚種の名前が多いので「Chart.js」だと凡例が邪魔だしね。
ただUI検討は大変なのでできる限りデフォルト利用だ。
それでも、凡例押したら非表示にする仕組みは作った。最初は非表示にすると人数が棒グラフ化されてしまったが、そこも修正できた。
1 2 3 4 |
options.series = series; // 人数はラインのまま options.series[0].color = '#666'; options.series[0].type = 'line'; |
なお、スマホ向けに日毎の結果を月毎の平均データに変換してグラフ化した。
これもpandasを使えば簡単だ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import pandas as pd from datetime import datetime from datetime import timedelta def main(): df = pd.read_csv('fishing.csv') df = df.rename(columns={df.columns[0]: 'date'}) # 日付・時刻型に変換 df['date'] = pd.to_datetime(df['date'], format='%Y%m%d') # 日時カラムにインデックスを貼る mm = df.set_index(["date"]) df = mm.resample('M').mean() df = df.fillna(0).astype('int') df.to_csv("fishing3.csv") if __name__ == "__main__": main() |
で、2020年以降の月別の千葉県内湾の釣果結果は次の通り。
スマホだとレスポンシブル対応したので潰れて見れないかもしれない。
パソコンで見ると次のように表示される。
いろいろな事が分かってきた。
千葉県内湾で、よく釣れているのは、
アジ、イワシ、サバ、コノシロ、サッパ
サビキ釣りでお馴染みの回遊魚だ。
で、よく釣れる時期は
9月、10月
年間を通してファミリーで釣りを楽しむなら
7月~12月ぐらいまで
なるほど、なるほど。
って、分析前の仮説と同じじゃん!
まあ、そうだけど
仮説は実証して初めて真実になる。
[引用] ドラマ「ガリレオ シーズン2」第8話湯川学のセリフ
それに、「アジ、イワシ、サバ、コノシロ、サッパ」を非表示にする事も可能なので、そこから次のことが分かる。
青物回遊魚以外だと、キスやサヨリやコウイカ、クロダイ、カサゴ、タコ、スズキがよく釣れるようだ。コウイカじゃなくてシリヤケイカだろうけどさ。
イナダやサワラも釣れているようだけど、玄人が毎日通って釣れるレベルじゃないかな。
そして、キスって夏場のイメージだけど5月が一番釣れて徐々に釣果が減ってる……。
このデータは、昨年はコロナの緊急事態宣言で4月、5月、6月頃のデータは十分ではない。
6月、7月頃になれば一年を通した釣果から、どの時期に何を釣るのが適切なのか判断できるようになりそうだ。
まとめ
結構、サクサクっと作ることができた。
自分のスキルが上がったのか、素性の良いデータだったのか、あまり困る事は無かった。
実装よりアイデアを出すのがシンドいんだけどね。
これ、定期的に稼働させてデータ集計したいけど、これだけのコンテンツじゃアクセス数も低いだろうし、どこで運用するかな。
貧乏釣り人なので、釣りの解説サイトを作っても時代遅れな記事になりそうだしなーー。
と言いつつ、作ってみた。
ソースコード
丁度100行のソースコード。Pythonは凄いな。
なお、実行すると相手のサーバーに負荷がかかるので、このままでは動かないようにしている。
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
import re import io, sys import pandas as pd import requests from bs4 import BeautifulSoup from datetime import datetime from datetime import timedelta import functools print = functools.partial(print, flush=True) sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') def try_except(name, text): a = "0" try: a = re.findall(name, text)[0].replace('名', '').replace('℃', '') except: pass return a def check(df, date, text): try: df.loc[date, '人数'] = re.findall(r'\d+名', text)[0].replace('名', '') except: try: # 名がついてない誤記対応 df.loc[date, '人数'] = re.findall(r'\d+様', text)[0].replace('様', '') except: pass df.loc[date, '潮'] = try_except(r'\w潮', text) df.loc[date, '水温'] = try_except(r'\d+.\d℃', text) df.loc[date, '天気'] = try_except(r'晴|雨|曇|雪', text) return df # 魚の名前変更 def convert_name(text): text = text.replace('カイズ', 'クロダイ') text = text.replace('フッコ', 'スズキ') text = text.replace('セイゴ', 'スズキ') text = text.replace(' ', '') text = text.replace('.', '') return text def parse(df, date, urlName): url = requests.get(urlName) soup = BeautifulSoup(url.content, "html.parser") try: m = 0 for i in range(len(soup.select(".news-detail-txt"))): if not (soup.select(".news-detail-txt")[i].text == ""): m = i break df = check(df, date, soup.select(".news-detail-txt")[m].text) except: # Not found return df for elem in soup.find_all("th"): try: if (elem.string == None or elem.string == "日"): break name = elem.string.replace('\xa0', '') name = convert_name(name) num = elem.find_next_sibling("td") title = num.find_next_sibling("td") cout = title.string.replace('\xa0', '').replace('匹', '').replace('合計', '').replace(' ', '').replace(' ', '') df.loc[date, name] = cout except UnicodeEncodeError as e: print(e) except: pass return df def sub(df, date): url = "サイトURL" df.loc[date] = 0 df = parse(df, date, url + date + ".html") return df def daterange(_start, _end): for n in range((_end - _start).days): yield _start + timedelta(n) def main(): cols = ['人数', '潮', '水温', '天気'] df = pd.DataFrame(index=[], columns=cols) df = sub(df, "20210408") df = df.fillna(0) df.to_csv("fishing.csv", encoding="shift_jis") if __name__ == "__main__": main() |