#プロジェクトのディレクトリに移る
# アプリ実行
npx electron ./src
# パッケージング実行(M1 Mac)
npx electron-packager src cc_convertor --platform=darwin --arch=arm64 --overwrite
[macOS] 34 launchdによる定期実行 実施例
[macOS Big Sur 11.6.1]
launchdによるプログラムの定期実行を試してみました。
エディタやXcodeを使ってxml形式でplistファイルを作成するのですが、エディタでは非常に書きにくいです。Xcodeが多少マシかと思います。どちらにしても結局crontabの方が扱いやすいです。
plistファイルを/Library/LaunchAgentsに作成したら、chownコマンドで所有者変更、chmodコマンドで権限変更します。最後にplistファイルをロードすると定期実行が始まります。
下記コード例では60秒おきにtest.pyを実行します。
このスキルはcrontabが使えなくなった際の代替手段としてストックしておきます。htmlとxmlは極力書きたくないです。だからなのかJavaScriptとはまた疎遠になっています。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>test</string>
<key>ProgramArguments</key>
<array>
<string>python</string>
<string>test.py</string>
</array>
<key>StartInterval</key>
<integer>60</integer>
<key>StandardOutPath</key>
<string>test.out</string>
<key>StandardErrorPath</key>
<string>test.err</string>
</dict>
</plist>
# plistを有効化
launchctl load test.plist
# plistを無効化
launchctl unload test.plist
[macOS] 33 launchdによる定期実行検討
[macOS Big Sur 11.6.1]
前回記事でMicrosoft AutoUpdateの定期実行を強引にできなくしましたが、そもそもどこで管理しているのか調べてみました。
今回のケースではライブラリディレクトリにあるLaunchAgentsやLaunchDaemonsでコントロールしています。crontabだけではなくこのlaunchdというサービス管理フレームワークでも自由にプログラムの定期実行を設定できるようです。
Microsoftの他にCubaseのメーカーであるSteinbergやフリーソフトKarabinerのプロパティリストファイルがありました。知らぬ間に放り込まれているので監視が必要ですね。ここに名を連ねるアプリは曲者という印象です。
ちなみにplistファイルは基本的にはXcodeで編集可能なはずですが、RunAtLoadの設定をNOにすることはできませんでした。ファイルの権限を変えても不可でした。どうやらファイルごと消去するしかないようです。
次回以降でlaunchdを使った定期実行を試してみます。
2021/11/20追記:plistファイルを編集するには権限だけでなくchownコマンドでファイルの所有者を変更する必要があります。Macにrootでログインしてコマンド実行します。
[macOS] 32 Microsoft AutoUpdateの削除
[macOS Big Sur 11.6.1]
定期的な起動がじゃまな場合は、やや乱暴な方法ですが下図にあるMicrosoft AutoUpdateファイルを削除すればOKです。
[Python] 321 M1 Macへのlxml非公式インストール
[M1 Mac mini 2020 , macOS Big Sur 11.6.1]
lxmlのサイトを参考にM1 Macに非公式インストールしてみました。今のところpipコマンドではlxmlをインストールできません。miniforgeのcondaコマンドで可能です。
サイトにあったMac OS Xへのインストール方法をBig Surで試したところ、たまたま上手くいきました。
手順は以下の通りです。
1.lxml.tgzの最新版をダウンロードする。今回はver.4.6.3。
2. tgzファイルを解凍する。
3.lxml4.6.3ディレクトリをカレントディレクトリにする。
4.lxmlをsetup.pyでビルドする。ビルドが途中で終わっても先に進んで問題ありませんでした。あくまでもpandas.read_htmlの使用に限定しての話ですが。
python setup.py build --static-deps
5.lxmlをインストールする。
python setup.py install
lxml開発元はmacOSの開発環境を古いと断じており、サポートに消極的な様子が伺えました。とりあえずインストールはできたので良しとします。ビルド環境が用意されていて助かりました。
これでminiforgeに頼らずに私のpyenv環境をM1 Macにて再現できました。
[Python] 320 pandasの行名設定 index関連メソッド
データフレームの行名を設定するメソッドをまとめました。
データフレームに空白の行を挿入するコードは以下のようになります。set_index、reindex、reset_indexメソッドを駆使します。このコードによりどの分類が欠けていても処理後にAからEの全分類が明示されるようになります。
なおreindexメソッドでaxis=1にすると列名に対応します。
df2のNaNを0に置き換えないとうまくいかず、泥臭い実務的なコードになりました。この処理をしないとdf4で分類Cの行が消えてしまいます。df2からdf3は何も変わっていないように見えますが、分類Cの行に実体が入っているためreset_index処理で消えることはありません。
import pandas as pd
df = pd.read_excel('test.xlsx',sheet_name=0)
print(f"df:\n{df}\n")
df.set_index('分類',inplace=True)
print(f"dfインデックス設定後:\n{df}\n")
labels = ['A','B','C','D','E']
df2 = df.reindex(labels, axis=0)
print(f"df2:\n{df2}\n")
# 空データの分類Cを残すための処理
df3 = df2.replace({'NaN':0})
print(f"df3:\n{df3}\n")
df4 = df3.reset_index()
print(f"df4:\n{df4}\n")
--------------------------------------------------
出力
--------------------------------------------------
df:
分類 評価額 取得額 損益
0 A 110 100 10
1 B 150 100 50
2 D 200 100 100
3 E 120 100 20
dfインデックス設定後:
評価額 取得額 損益
分類
A 110 100 10
B 150 100 50
D 200 100 100
E 120 100 20
df2:
評価額 取得額 損益
分類
A 110.0 100.0 10.0
B 150.0 100.0 50.0
C NaN NaN NaN
D 200.0 100.0 100.0
E 120.0 100.0 20.0
df3:
評価額 取得額 損益
分類
A 110.0 100.0 10.0
B 150.0 100.0 50.0
C NaN NaN NaN
D 200.0 100.0 100.0
E 120.0 100.0 20.0
df4:
分類 評価額 取得額 損益
0 A 110.0 100.0 10.0
1 B 150.0 100.0 50.0
2 C NaN NaN NaN
3 D 200.0 100.0 100.0
4 E 120.0 100.0 20.0
[Python] 319 処理時間を測定
メモ書き。
import time,datetime
start = time.time()
<処理>
# 処理時間算出(秒)
process_time = time.time() - start
td = datetime.timedelta(seconds = process_time).total_seconds()
# 小数点第2位まで表示
td_2f = f'{td:.2f}'
[Python] 318 小数点以下桁数を指定
メモ書き。
num = 123.456
print(f'{num:.1f}')
print(f'{num:.06f}')
--------------------------------------------------
出力
--------------------------------------------------
123.5
123.456000
[Python] 317 lxmlがない場合のpandas.read_html代替スクリプト 改良版
前回の続きです。
htmlファイル内のtableを2次元リストを経て直接データフレームに変換する方法に書き直しました。CSVファイルを作成しない分、スマートかと思います。
# 代替スクリプト改良版
# 文字コードをUTF-8に変換してソース取り込み
html = driver.page_source.encode('utf-8')
# BeautifulSoupでデータ抽出
soup = BeautifulSoup(html, "html.parser")
# soupから3番目のtableを抽出
table = soup.find_all("table",attrs={"cellspacing" : "1"})[2]
rows = table.findAll("tr")
list_rows = []
for row in rows:
list_row = []
for cell in row.findAll(['td', 'th']):
text = cell.get_text()
text2 = text.replace('"','').replace("\n","").replace(" ","").replace(" ","")
list_row.append(text2)
list_rows.append(list_row)
# 2次元リストをヘッダとデータに分割
header = list_rows[0]
data = list_rows[1:]
# データフレームに変換
df = pd.DataFrame(data,columns = header)
[Python] 316 lxmlがない場合のpandas.read_html代替スクリプト
今のところM1 Macにおいてpipコマンドだけでライブラリを揃える場合、lxmlをインストールできないためpandas.read_htmlを使うケースでは代替スクリプトを考える必要があります。
私のスクリプトは以下のように書き換えました。tableを一旦CSVファイルにしてからデータフレームとして読み込んでいます。まどろっこしいですが仕方ないです。
# 代替スクリプト
# 文字コードをUTF-8に変換してソース取り込み
html = driver.page_source.encode('utf-8')
# BeautifulSoupでデータ抽出
soup = BeautifulSoup(html, "html.parser")
# soupから3番目のtableを抽出
table = soup.find_all("table",attrs={"cellspacing" : "1"})[2]
rows = table.findAll("tr")
filename = "table.csv"
with open(filename, "w", encoding='utf-8') as file:
writer = csv.writer(file)
for row in rows:
csvRow = []
for cell in row.findAll(['td', 'th']):
text = cell.get_text()
text2 = text.replace('"','').replace("\n","").replace(" ","").replace(" ","")
csvRow.append(text2)
writer.writerow(csvRow)
# CSVファイルをデータフレームに変換
df = pd.read_csv(filename)
# 旧スクリプト
# 文字コードをUTF-8に変換してソース取り込み
html = driver.page_source.encode('utf-8')
# BeautifulSoupでデータ抽出
soup = BeautifulSoup(html, "html.parser")
# soupから3番目のtableを抽出
table_data = soup.find_all("table",attrs={"cellspacing" : "1"})
df_stock_specific = pd.read_html(str(table_data), header=0)[2]
labels_specific = ['A','B','C','D','E']
df_stock_specific2 = df_stock_specific.reindex(labels_specific, axis=1)