OmniAICreator/Qiita-1.07M huggingface dataset:
猫でも作れる高機能プログラミング言語 FR2 WonderとFR2 Cyclonの紹介

どーも、どーも、こんばんわー、ハルキーです。
普段はこちらでブログを書いています。https://note.com/halcky_2025/
今日は世界へ向けて開発している猫ツク言語のFR2 WonderとFR2 Cyclonの紹介をしたいと思います。（ガチです。
GitHubへの公開は1月ぐらいになりそうですので、サンプルコードを載せておきます。

```sample.wonder
class Hello <- Object{int x, int y|
    int X: x
    int Y: y
    var LX:
        return X;#set X: val
    var aisatu{var hour|
        if 4 < hour and hour < 10:
            return["good morning"]
        "Hello World"
    }
}
var h: Hello.new[3, 2]
print[h.aisatu[7]]
var i : 0
while i < 9:
    #head
    i : i + 1
    if i <= 5: goto["head"]
    print[i]
    i : i + 1
switch h.LX:
    #1 print[1]
    #2 print[2]
    #3 print[3]
    #4 print[4]
    #default print[5]
```

```sample.cyclon
class Hello <- Object{int x, int y|
    int X: x
    int Y: y
    prop var LX{
        return X; #set X: val
    }
    var aisatu{var hour|
        if {4 < hour and hour < 10|
            return "good morning"
        }
        "Hello World"
    }
}
var h: Hello.new(3, 2)
print(h.aisatu(7))
while{var i: 0, i < 9|
    #head
    i : i + 1
    if {i <= 5| goto "head"
    print i
    i : i + 1
}
switch{h.LX|
    #1 print 1
    #2 print 2
    #3 print 3
    #4 print 4
    #default print 5
}
```

FR2とは何なのかですが、youtubeでLispはシンプルな法則で作られていて、すごいみたいな話を聞いたので、そんなん簡単に作れるじゃん、仕様書を読めばわかる機械に対してではなく、複雑だと思われている人間の脳に対して。

そうやって作ったのが、FR2 Wonderです。
人間の脳は、Feelつかみ Result要点 Reason 説明　の構造にした時に極めて理解が効率的になります。この３点をどうするかで、コミュニケーションの方法から考え方まで人間は強く影響を受けています。
I think he is good person in Japan.
_____ ________________ ______.
Feel　　　Result　　　　Reason
　　　he is good person
　　　____ ____ ______.
　　　Feel　Result　Reason
つかみで脳を刺激され、要点で内容を把握し、詳細で正確な判断をするように脳は検索します。この方法を使うと英語のToeicのListeningで文章の重要度が予測できるようになり、点数が100点ぐらい向上します。


これが、FR2です。

シンプルな法則性のもとで作られているので、Wonderは構文解析が800行、Cyclonは構文解析が170行となっています。
意味解析の方も驚くほどシンプルになっています。
Wonderの方はもうちょっと短くできるかもしれません。

そして、
Python風の記風になったものがFR2 Wonderで、Wonderは驚くほど素晴らしいという意味で、びっくり箱に入った蛇を暗示しています。
FR2 CyclonはC言語記風になっていて、CyclonはC言語の代替として、世界の台風の目になるという意味でつけられました。

来年の未踏に応募するので、ぜひ応援してください。
手伝ってくれる人を募集しています。
AWS CloudWatch Logsの話 : ログのインサイトを活用しよう

この記事は、[ソニックガーデン プログラマ Advent Calendar 2024](https://qiita.com/advent-calendar/2024/sonicgarden)の14日目の記事です。

# TL;DR

AWS CloudWatch Logsのログのインサイトを使うことで、Railsアプリケーションのログを高速に検索し、解析できます。特定の条件でログを絞り込んだり、JSON形式のデータを解析して必要な情報だけを抽出したりするなど色々な活用が可能です。ただし、スキャンするデータ量に応じて料金が発生するため注意が必要です。

# 背景

RailsアプリケーションのほぼJSON形式のログをAWSのCloudWatch Logsに保存しています。アプリケーションログは必要に応じて絞り込んで閲覧したいケースがよくありますが、単純な検索ではログの分量が多い場合に時間がかかりすぎることがあります。

## 「ログのインサイト」とは？

「ログのインサイト」はCloudWatch Logsの機能の一つで、以下のようなメリットがあります：

- ログの高速検索
- ログ内容の解析や計算による絞り込み・出力
- 必要な項目だけをSQL風のクエリで選択して出力
- 出力結果をCSV形式などでダウンロード

「ログのインサイト」はCloudWatchのサイドメニューからアクセスできます。

![image1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19098/40390020-ad52-f9f8-1e9b-dc9c79e6391a.png)

## 基本操作

ログのインサイトを開くとこのような初期画面が現れます。

![image2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19098/6c98feef-e1e4-ea42-8106-88538121d069.png)

1.	検索期間を指定する
2.	タイムゾーンを必要に応じて変更する
3.	ロググループを選択する
4.	クエリを入力する
5.	実行する
6.	結果が表示される
7.	必要なら結果をダウンロードやコピーする

またクエリには次のようなものがプリセットされています。

```
fields @timestamp, @message, @logStream, @log
| sort @timestamp desc
| limit 10000
```

## ログ解析の実例

いくつかの例を紹介する前に、今回解析の対象とするログ形式を明確にておきます。
Railsでlograge gemを使用し、以下のようなJSON形式をログを解析することとします。
（横に長いので表示ために改行をいれています）

```
I, [2024-12-06T09:56:24.761724 #42]
INFO -- : [ec002d16-7b40-4fac-8bf7-f873e56b1234]
{"method":"GET","path":"/","status":200,"duration":156.93,
"remote_ip":"127.0.0.1","user_agent":"Mozilla/5.0","params":{}}
```

■特定の文字列にマッチするログを抽出

例）UsersControllerが処理したリクエスト

```
fields @timestamp, @message, @logStream, @log
| filter @message like /UsersController/
| sort @timestamp desc
| limit 10000
```

■ 特定のフィールドを切り出して絞り込み

例）duration（レスポンスにかかった時間）が10秒以上のリクエスト

```
fields @timestamp, @message, @logStream, @log
| parse @message '"duration":*,' as @duration
| filter @duration > 10000
| sort @timestamp desc
| limit 10000
```

■ 特定のフィールドを切り出して、さらに式を適用して条件にしたい

例）user_nameにマッチした文字列が10文字より大きいリクエスト

```
fields @timestamp, @message, @logStream, @log
| parse @message '"user_name":*,' as @user_name
| filter strlen(@user_name) > 10
| sort @timestamp desc
| limit 10000
```

■ 特定の行を除外したい

例）ActiveJobの実行ログや手動で書き出しているログを除外する

```
fields @timestamp, @message, @logStream, @log
| filter @message not like /Job|\[INFO\]/
| sort @timestamp desc
| limit 10000
```

■ JSONとしてパースして検索する

```
fields @message
| parse @message 'I, [*]  INFO -- : [*] *' as _, @request_id, jsonPart
| fields jsonParse(jsonPart) as json
| filter json.method like /GET/
| sort @timestamp desc
| display @timestamp, @request_id, json.action, json.method, json.duration
```

jsonParseにJSONの文字列を渡すことでjsonとして解釈できるようにし、`json.method`や`json.duration`のようにアクセス可能になります。これが非常に便利。今回の本題といっても過言ではありません。

## 注意点: 料金の発生

「ログのインサイト」は、スキャンするデータ量に応じて料金が発生します。公式ページによると、ログを1GBスキャンするごとに **$0.0076** かかります。

[Amazon CloudWatch Pricing – Amazon Web Services (AWS)](https://aws.amazon.com/jp/cloudwatch/pricing/)

> Query (Logs Insights) $0.0076 per GB of data scanned

大量のログを検索すると予想以上の料金が発生する可能性があるため、注意してください。
インテントデータ


+ [インテントデータの作成、保存、管理](https://help.salesforce.com/s/articleView?id=service.bots_service_intent_data.htm&type=5)

ボットには、イベントに関するデータとインテントに関するデータの 2 つのデータ セットがあります。イベント データは、ボットの会話内で発生する事柄を指します。インテント データは、ボットが顧客を理解できるようにモデルをトレーニングするために使用されるデータを指します。堅牢なボットを作成するには、両方のデータ セットを処理するための戦略が必要です。

インテント データには、顧客の要望をボットに伝える発話またはフレーズが含まれています。たとえば、エージェントと話すように依頼する方法は多数あります。「エージェントに転送してください」、「人間と話したい」、「エージェント」などです。インテントに追加する発話が多ければ多いほど、ボットが正しい判断を下せる可能性が高くなります。

バランスの取れたインテント モデルを持つことが重要です。モデルを構築するには少なくとも 20 個の発話が必要ですが、発話の数が多いほどモデルの精度が高まります。また、モデルに含まれるインテントには、他のインテントとほぼ同じ数の発話を含めることをお勧めします。

ボットの成長に合わせて、インテント データ管理の戦略を作成します。優れた戦略には、サンドボックス内で新しいインテントと発話をテストおよび追加して変更を試すためのパスが含まれます。これは、ライブ ボットに発話とインテントを追加すると、顧客エクスペリエンスに影響するためです。

インテントは同じボットのバージョン間で共有できます。

# The related Ml Intent is already referenced by another Ml Intent

+ [The related Ml Intent is already referenced by another Ml Intent](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000Bd3urSAB)

>最近、CI プロセスを新しい Azure DevOps ベースのパイプラインに移行しました。パイプラインに、検証とデプロイのために SFDX コマンドを発行するタスクを作成しました。この切り替えの前は Flosum を使用していました。最近 Einstein ボットの変更をデプロイする必要が生じるまで、すべて順調でした。これが、未解決のまま残っている唯一の問題です。  

>サンドボックス組織からローカル機能ブランチに問題なくプルできます。ただし、ボットがすでに存在する組織にデプロイしようとすると、     「関連する Ml Intent は、別の Ml Intent によって既に参照されています」というエラーが発生します。 

>ターゲット組織にはボットの V1 と V2 があります。メイン ボットと V1 をデプロイしようとしています。ターゲット組織では、V1 は非アクティブです。V2 はアクティブです。参考までに、ボット コードはソース形式です。デプロイするには、mdapi 形式に変換してから、force:mdapi:deploy を使用してデプロイしました。何か見落としているのでしょうか? ご協力いただければ幸いです。

+ [intent error when importing a bot block](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000J80yF0AR)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/9a640d34-27fa-93be-4a76-c77aee86ff04.png)

>ブロックには、Einstein インテント セットにリンクされたダイアログ インテントを含めることはできないことはわかっています。ただし、元のブロックを作成する前に、すべてのインテント参照をすでに削除しました。Salesforce は、削除された後でも元のインテント参照を「記憶」しているのでしょうか? これまでにこのような現象を経験した人はいますか? アドバイスをいただければ幸いです。 

+ [インテントダイアログを無効にする - アインシュタインボット](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000A8ULFSA3)

:::note
ダイアログの「アインシュタイン」ボタンをオフに切り替えて、インテント/インテントセットを削除すると、無効とみなされます。
:::
■ 継続的改善（PDCAサイクル）の具体例について

# 1. Plan（計画）

■ <INS><font color="#00bfff">QAの役割: 品質基準とプロセスの計画を立てる</font></INS>
<br>

<font color="#66cdaa">品質目標の設定</font>：
　　・プロジェクトの成功に必要な品質目標（バグ検出率、顧客満足度など）を定める
　　[例] テスト完了後のバグ報告数を20%削減する 等
<br>

<font color="#66cdaa">テスト戦略の策定</font>：
　　・テストの範囲や手法（手動/自動）を決定
　　・テストツールの選定と導入計画
<br>

<font color="#66cdaa">リスク分析</font>：
　　・製品やプロセスにおける潜在的な品質リスクを特定し、優先順位を設定
<br>

<font color="#66cdaa">品質保証プロセスの計画</font>：
　　・プロジェクト全体の品質を保証するための具体的なタスク
  　　（レビューのタイミング、品質ゲートの設置など）を定義
<br>

# 2. Do（実行）
<br>

■ <INS><font color="#00bfff">QAの役割: 計画に基づいてテストと品質保証活動を実施する</font></INS>
<br>

<font color="#66cdaa">テスト設計</font>：
　　・仕様に基づき、テスト観点を洗い出し、テストケースを作成
<br>

<font color="#66cdaa">テストケースの実行</font>：
　　・計画したテストケースに基づいて、システムや機能の動作確認を行う
<br>

<font color="#66cdaa">バグの報告とトラッキング</font>：
　　・不具合を発見し、開発チームにフィードバック
　　・バグ管理ツール（例: Backlog）を使用して進捗を追跡
<br>

<font color="#66cdaa">プロセス遵守の確認</font>：
　　・チームが規定の品質プロセスに従っているかを確認
　　・必要に応じて改善提案を行う
<br>

<font color="#66cdaa">改善アクションの試行</font>：
　　・新しいテストツールやプロセス改善案を実際に試行し、その効果を確認
<br>

# 3. Check（評価）
<br>

■ <INS><font color="#00bfff">QAの役割: 実行結果を評価し、目標とのギャップを分析する</font></INS>
<br>

<font color="#66cdaa">データ収集と分析</font>：
　　・テスト結果、バグの検出率、解決までの平均時間などのデータを収集
　　・テスト戦略が有効であったかを確認する
<br>

<font color="#66cdaa">品質評価</font>：
　　・プロジェクト目標
  　　（例: バグ削減率、製品のパフォーマンス指標など）に対する達成度を評価
<br>

<font color="#66cdaa">失敗の特定</font>：
　　・プロセスやテストケースの不足による問題点を特定する
　　・発見できなかったバグの原因を分析（テスト漏れ、仕様の理解不足など）
<br>

# 4. Act（改善）
<br>

■ <INS><font color="#00bfff">QAの役割: 評価結果に基づいてプロセスを改善し、次のサイクルに活かす</font></INS>
<br>

<font color="#66cdaa">プロセスの最適化</font>：
　　・不十分だったプロセスを改善し、新たな品質保証ポリシーを作成する
　　・効果が確認された手法やツールを標準化する
<br>

<font color="#66cdaa"> 教育と啓発</font>：
　　・チームに対して、失敗から得られた教訓を共有し、品質意識を高める
<br>

<font color="#66cdaa">次サイクルの準備</font>：
　　・改善案を次の計画段階（Plan）に反映し、より高い品質を目指す
　　・QAにおけるPDCAサイクルの具体的な適用例を作成する
<br>

# ■ まとめ：
<br>

<INS>PDCA（Plan／Do／Check／Act）サイクルを活用することで、
QAは品質保証プロセスの「継続的な改善」を担い、
プロジェクト全体の成功に貢献できます。</INS>
Fusion360の回路設計でビアをレジストで覆う方法

Fusion360でビアをレジストで覆う方法がわからなかったのでここにメモします。
このやり方が正しいか正直わからないですがとりあえず上手く行きました。笑
もっと簡単な方法をご存知の方は教えていただけると嬉しいです。

## レジストで覆う理由
ビアにレジストがない場合最悪短絡の危険があります。
以前にビアと部品の足が近くにあり短絡を起こしたことがありました。
原因追及に゙時間がかかるので未然に防ぎたいですよね。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/a43b5234-ea6f-4615-e3f9-cf0033ba1c27.png" width=30%>


## ビアのプロパティを確認
対象のビアの上で[右クリック>プロパティ]を開きます。
プロパティから[ドリル(D)]のサイズ、[はんだマスク]がOFFになってることを確認します。
ドリルの値は先で使用するのでコピーしておくと便利です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/58b57742-19cf-5948-c40e-f1babc664645.png" width=80%>


## デザインルールの設定
[ルール]タブから[デザイン基本設定]を開きます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/8ee05ba0-08d1-4c1c-4514-95062799cf3b.png" width=50%>
[マスク]のタブに移動し[制限]の入力欄に先ほどコピーしたビアのドリルサイズを入力します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/628c7b93-ef9f-6f8c-9ec0-c169defc8f15.png" width=80%>
[OK]で設定を適用します。その際にこのような警告が出る場合がありますが無視して[はい]をクリックします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/7624a1aa-0659-48d4-2bb3-e0a11b72b0fa.png" width=50%>

以上で設定が完了しました。

## ビアの確認
ビアにレジストが被るか確認の行います。
まず保存してから[製造]タブの[CAMプロセッサ]を開きます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/c3731aa1-761b-3d8b-9a86-d0950a32257b.png" width=80%>
[solder_+_mask_+_top]を選択し、ビアの場所が黒塗りになっていない事を確認します。
設定前
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/49bf9a71-f27b-3c54-73c0-ab8203790313.png" width=80%>

設定後
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/59a50754-16a6-748a-3acd-50f523dea3e6.png" width=80%>

最後に依頼する業者のサイトでビアが覆われていることを確認し、発注をしましょう。
※写真はPCBgogoの確認画面
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3716852/236d4b12-52ef-d9fa-7c0a-f62bd1454769.png" width=80%>
ルーティングテーブル解釈の仕方メモ

## はじめに
ルーティングテーブルの読み方をすぐ忘れるのでメモ

### 環境
Ubuntu 24.04
ISA x86_64

### おさらい
パケットの構成は、 [L2ヘッダ][L3ヘッダ][本体][おまけのL2チェックサム] が基本。

## 本文

下記は、ChatGPTに作ってもらった架空の`route -n`(あるいは、`netstat -rn`)コマンドの結果
```
行番号 Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
1. 0.0.0.0         192.168.1.1     0.0.0.0         UG    0      0        0 eth0
2. 10.0.0.0        192.168.2.1     255.255.255.0   UG    0      0        0 eth1
3. 192.168.1.0     *               255.255.255.0   U     0      0        0 eth0
4. 192.168.2.0     *               255.255.255.0   U     0      0        0 eth1
```

### 読み方
* 1行目
他のDestinationに含まれなかった結果デフォルトゲートウェイで送ることになったパケットはeth0から送る。
L2の宛先MACアドレスとしては、（eth0の所属するL2ネットワークで）IPアドレス192.168.1.1をMACアドレスに解決したものを利用する。L3のヘッダの宛先を、192.168.1.1にするわけでない（グローバルIPアドレスならグローバルIPアドレスをそのまま、192.168.1.1に送りたいなら192.168.1.1をそのままいれる）

* 2行目
10.0.0.0/24 の範囲のアドレスへ送付するとき（ex. 10.0.0.1）は、L2ヘッダのMACアドレスはL2ネットワークに 192.168.2.1 のIPアドレスを解決したものを使う。また、Gatewayが存在することから、10.0.0.0/24の中のアドレスはeth1の所属するL2ネットワークの中には存在しないことがわかる。

* 3行目
Gatewayが*なので、全てのIPアドレスが同一リンクにあることがわかる。つまり、例えば192.168.1.1宛に送る場合、L2ヘッダは192.168.1.1をARPでMACアドレスに解決したものを使う。L3ヘッダはもちろん、そのまま使う。

### Flagsについて
ルートの状態を示すフラグ。例：U（使用中）、G（ゲートウェイを経由する）、H（ホストルート）、R（リダイレクト）。

### Metric
ルートの優先度。低い値が優先され

### Use
このルートが使用された回数

### Iface
実際にデータを送信するインターフェース名

## 逆引き
### アドレスに対して選択されるルーティングテーブル行を得る
ip route get <IPアドレス>

### MTUとは
Maximum Transmission Unit。L2で一度に送信できる最大データ量（単位はバイト）。1500バイトが多く、イーサネットのヘッダー（通常14バイト）とIPヘッダー（通常20バイト）を差し引いた1460バイトが実際のデータ部分となる。（L2=Wi-Fiの場合、ヘッダのサイズももう少し大きいことが多い気がするので、Wi-Fiは一度に送れるペイロードが少ないかも。）手元で見ると、ループバックインターフェースだけMTU=65536(0x10000)であった。適切に小さくしないと、フラグメンテーションが起きる（1つのパケットを経路の途中で分割されること）
`sudo ip link set dev eth0 mtu 1400` などで変更できる。
Ruby で Hash#to_proc を使ってリストの要素に採番する

Ruby で[ブロック付きメソッド呼び出し](https://docs.ruby-lang.org/ja/latest/doc/spec=2fcall.html#block)を省略する際、最後の引数に `&obj` という形で `#to_proc` を持つオブジェクトを指定する。

経験的にはメソッド名の Symbol を指定することが圧倒的に多く、時々 Method や Proc という感じがする。 Hash も `#to_proc` があるので指定できるけれども、使った覚えが無い。

https://docs.ruby-lang.org/ja/latest/method/Hash/i/to_proc.html

Hash でなにか良い使い道が無いか考えたところ、リストの要素に番号（非負整数のID）を振って置換する際に便利そうだったのでやってみた。

## コード

```ruby
def ord() = Hash.new { |h, k| h[k] = h.size }

# 登場順に採番
p "abracadabra".each_char.map(&ord())
#=> [0, 1, 2, 0, 3, 0, 4, 0, 1, 2, 0]

# 小さい順に採番（座標圧縮）
ord_s = ord()
"mississippi".each_char.sort.each(&ord_s)
p "mississippi".each_char.map(&ord_s)
#=> [1, 0, 3, 3, 0, 3, 3, 0, 2, 2, 0]
p ord_s
#=> {"i"=>0, "m"=>1, "p"=>2, "s"=>3}
```

※ `ord` の `()` は書かなくてもいいが、メソッドと変数を区別しやすいよう明示している

## 仕組み
リストの各要素について Hash のキーに対応する値を参照する場合は、わざわざブロックを書かなくても `&obj` で済む。

```ruby
h = {1 => 10, 2 => 20, 3 => 30}
seq = [1, 2, 3]

# これらは同じ
seq.map { |elem| h[elem] }
seq.map(&h)
```

今回の用途では「 Hash にキーが無ければ生成・保存する」という処理を追加している。

```ruby
seq.map do |elem|
  h[elem] = elem * 10 unless h.key?(elem)
  h[elem]
end
```

これは Hash に [default_proc](https://docs.ruby-lang.org/ja/latest/method/Hash/i/default_proc=3d.html) を設定しておくことでブロックから追い出せる。（別のブロックを書くことにはなるが）

```ruby
h.default_proc = ->(hash, key) { hash[key] = key * 10 }
seq.map do |elem|
  h[elem]
end
```

採番については、ゼロ始まりとしておけば `採番済みの要素数 == 次の採番の番号` になるため、 `Hash#size` で次の番号を作成できる。

## おまけ
同じ考え方によって、即席のメモ化にも使える。コード長については普通に書いたほうが短いが、 Hash を外部の変数に用意せず使い捨てできる長所はある。

```ruby
# 毎回 to_i を実行する
p "abracadabra".each_char.map { |s| s.to_i(36) }
#=> [10, 11, 27, 10, 12, 10, 13, 10, 11, 27, 10]

# 一度 to_i を実行した要素については、キャッシュした結果を返す
h = {}
p "abracadabra".each_char.map { |s| h[s] ||= s.to_i(36) }

# Hash をその場で作れば、キャッシュは使い捨てになる
p "abracadabra".each_char.map(&Hash.new { |h, s| h[s] = s.to_i(36) })
```
カスタム Nerves: USB カメラの UVC 準拠を確認する方法

## はじめに

USB カメラを使用する際、その多くが **UVC (USB Video Class)** に準拠しているかを確認することが重要です。これにより、専用ドライバを必要とせず、さまざまなオペレーティングシステムで利用できるようになります。

以前の記事「[カスタム Nerves: デバイスドライバを特定する方法](https://qiita.com/mnishiguchi/items/0d26a09ecd3e3f02f411)」では、USB カメラを含むデバイスのドライバを見つける方法について解説しました。その中で、UVC 準拠の確認が必要な場面がありました。

UVC 準拠デバイス一覧というものがありますが、すべてのデバイスが掲載されているわけではないようです。実際、多くの現代の USB ウェブカメラは、リストに記載されていなくても UVC 準拠であると言われています。

本記事では、USB カメラが UVC 準拠かどうかを確認する具体的な方法を紹介します。特に、[UVC 準拠デバイス一覧](https://www.ideasonboard.org/uvc/) に記載がない場合でも役立つ手順を解説します。

## 一部の UVC 準拠デバイスがリストに載っていない理由

UVC 準拠デバイス一覧にはすべてのデバイスが記載されているわけではありません。次のような理由が挙げられます。

1. **リストの不完全性**\
   デバイス一覧は手動で管理されており、新しいモデルやニッチなブランドの製品はまだ追加されていないことがあります。

2. **デフォルトの準拠性**\
   ほとんどの現代の USB カメラはプラグアンドプレイ機能を保証するために UVC 規格に準拠しています。

3. **リブランドされたデバイス**\
   一部のカメラはリブランド製品として販売されており、異なるベンダー ID や製品 ID を使用しているが、実際には UVC 準拠のモデルです。

4. **未報告のデバイス**\
   メーカーが自社の UVC 準拠デバイスをリストに報告していない場合があります。

これらの理由から、リストに載っていなくても UVC 準拠である可能性があります。


## UVC 準拠を確認する手順

USB カメラが UVC に準拠しているかを調べるための具体的な方法を紹介します。

### 1. `lsusb` コマンドで確認

`lsusb` コマンドを使用してシステムがデバイスを検出しているかを確認します。

```bash
lsusb
```

USB カメラの出力例

```
Bus 001 Device 003: ID 056e:701a Elecom Co., Ltd ELECOM 2MP Webcam
```

### 2. `uvcvideo` ドライバの確認

以下のコマンドを実行して`uvcvideo` ドライバがロードされているか確認します。

```bash
lsmod | grep uvcvideo
```

`uvcvideo` が表示されない場合は以下のコマンドで手動でロードします。

```bash
sudo modprobe uvcvideo
```

### 3. カメラの動作テスト

`v4l2-ctl` や `ffmpeg` を使用してカメラが動作するか確認します。

- 利用可能なビデオデバイスを一覧表示

  ```bash
  v4l2-ctl --list-devices
  ```

- ビデオストリームをテスト

  ```bash
  ffplay /dev/video0
  ```

### 4. UVC 準拠デバイスリストで検索

[UVC 準拠デバイス一覧](https://www.ideasonboard.org/uvc/#devices) を開きます。

まず、**Vendor ID (VID)** と **Product ID (PID)** を確認します。例として、`lsusb` コマンドを実行すると次のような出力が得られます。

```
Bus 001 Device 003: ID 056e:701a Elecom Co., Ltd ELECOM 2MP Webcam
```

この出力の中で、`ID` の後に続く `056e` が Vendor ID、`701a` が Product ID です。

次に、これらの ID を使用してデバイスを特定します。ブラウザの検索機能 (`Ctrl+F` または `Cmd+F`) を使用して、UVC 準拠デバイスリストにデバイスが掲載されているかを確認します。

### 5. 対応フォーマットの確認

`v4l2-ctl` を使用して対応しているビデオフォーマットと機能を確認します。

```bash
v4l2-ctl --list-formats-ext
```

サポートされているフォーマットとはカメラが対応している解像度やフレームレートの情報を指します。例として以下のような出力が考えられます。

```
[0]: 'YUYV 4:2:2' (640x480, 30.000 fps)
[1]: 'MJPEG' (1280x720, 25.000 fps)
```

これらのフォーマット情報が表示される場合、そのカメラは UVC 準拠である可能性が高いです。


## UVC 非準拠デバイスの場合

UVC に準拠していないデバイスでも利用できる場合があります。以下の方法を試してみてください。

1. **専用ドライバ**\
   メーカーのウェブサイトやサポートページで Linux 用のドライバを確認します。

2. **コミュニティのフィードバック**\
   [Linux UVC メーリングリスト](https://www.ideasonboard.org/uvc/#mailinglist)、Reddit、または Stack Overflow のフォーラムで情報を探します。

3. **リバースエンジニアリング**\
   上級ユーザーはカスタムドライバやカーネルパッチを検討できますが、このプロセスは複雑です。


## まとめ

**UVC 準拠デバイス一覧** は参考になりますが、完全ではありません。多くの USB ウェブカメラはリストに掲載されていなくても UVC 準拠です。`lsusb`、`v4l2-ctl`、`ffmpeg` を使って動作を確認してみてください。

非準拠の場合は、メーカーの専用ドライバを調べたり、フォーラムで情報を集めたり、リバースエンジニアリングを検討することが有効です。

詳しくは、[UVC Linux Device Drivers ページ](https://www.ideasonboard.org/uvc/) をご覧ください。

何か氣づいた点や改善提案があれば、コメントで共有していただけると嬉しいです。

![toukon-qiita-macbook_20230912_091808.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82804/fd5c55ec-4fe0-8af6-59bc-bab1ef3d182b.jpeg)
透過色を利用したときに表示される色を計算する

以下の画像は`#50E3D2`の背景に`#FFFFFF`を80%透過させた前景を被せたものです。

![Group 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591069/94392698-3942-53e8-351b-f8be0ef08b66.png)

全景の部分に表現されている色は何色になるのでしょうか。

スポイトで色を取り出してみると、`#50E3D2`の明度が下がったような色(`#DCF9F6`)になりました。

このような前景に透過色を配置したときの色を計算によって求めてみましょう。

:::note
この記事は主にブラウザの動作、CSSにおける色について考えています。
他の領域では異なる計算になる可能性があることに注意してください。
:::

[CSSの重ね合わせコンテキスト](https://developer.mozilla.org/ja/docs/Web/CSS/CSS_positioned_layout/Understanding_z-index/Stacking_context)をもとに考えていきます。CSSの重ね合わせコンテキストはHTML要素の三次元を概念化したものです。

W3Cでは重ね合わせコンテキストの[不透明度についての扱いについて](https://www.w3.org/TR/css-color-3/#transparency)が決められています。これによると透明度の計算は[シンプルなアルファ合成](https://www.w3.org/TR/2003/REC-SVG11-20030114/masking.html#SimpleAlphaBlending)で行われるようです。

シンプルなアルファ合成は以下のように計算されます。

```math
\begin{align}
B_r, B_g, B_b ・・・背景のRGB \\
B_a           ・・・背景の透明度 \\
F_r, F_g, F_b ・・・前景のRGB \\
F_a           ・・・前景の透明度 \\
\end{align}
```
```math
\begin{align}
R_r = (1 - F_a) \times B_r\times B_a + F_r\times F_a \\
R_g = (1 - F_a) \times B_g\times B_a + F_g\times F_a \\
R_b = (1 - F_a) \times B_b\times B_a + F_b\times F_a \\
R_a = 1 - (1 - F_a) \times (1 - B_a)
\end{align}
```
$R_{r,g,b,a}$は最終的に表示される色です。今回は$B_a = 1$を考えているので、さらに以下のように変換できます。
```math
\begin{align}
R_r = (1 - F_a) \times B_r + F_r\times F_a \\
R_g = (1 - F_a) \times B_g + F_g\times F_a \\
R_b = (1 - F_a) \times B_b + F_b\times F_a \\
R_a = 1
\end{align}
```

この色が透過色を利用したときに表示される色を求めるものです。

最初の例に当てはめて計算してみましょう。
背景色は`#50E3D2`なので`rgb`表記では`rgb(80 227 210)`です。前景色は`#FFFFFF`の80%なので`rgb(255 255 255 / .8)`です。
$F_{r,g,b} \times F_a$は$204$です。
$(1 - F_a) \times B_r$は$16$、$(1 - F_a) \times B_g$は$45.4$、$(1 - F_a) \times B_b$は$42$になります。
これらを代入すると、`rgb(220 249, 246)`が得られました(小数点は四捨五入しています)。
これはHEX形式にすると`#DCF9F6`なので、最初にスポイトで取った値と同じになりました。

最後に、背景色も透過しているケースを考えてみます。前景色はそのままで、背景色に70%の透過度を与えます。
![Group 2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591069/4013f21d-6c9c-d0da-610b-28d4476206d8.png)

この表示された色はスポイトで取ると`#E6FBF9`でした。

```math
\begin{align}
R_r = (1 - F_a) \times B_r\times B_a + F_r\times F_a \\
R_g = (1 - F_a) \times B_g\times B_a + F_g\times F_a \\
R_b = (1 - F_a) \times B_b\times B_a + F_b\times F_a \\
R_a = 1 - (1 - F_a) \times (1 - B_a)
\end{align}
```
先ほど確認した上記の数式にすべての値を当てはめると以下のようになります。
```math
\begin{align}
R_r = (1 - 0.8) \times 80\times 0.7 + 255\times 0.8 = 215.2 \\
R_g = (1 - 0.8) \times 227\times 0.7 + 255\times 0.8 = 235.78 \\
R_b = (1 - 0.8) \times 210\times 0.7 + 255\times 0.8 = 233.4 \\
R_a = 1 - (1 - 0.8) \times (1 - 0.7) = 0.94
\end{align}
```
透過度が$1$になるように値を調整すると、`rgb(229 251 248)`になります($R_{r,g,b}$それぞれの値を$R_a$で割っています)。
HEX形式に書き直すと、`#E5FBF8`です。これはスポイトで取った`#E6FBF9`とRedとBlueが$1$ずれた値になります。この誤差はスポイト機能を実装したアプリとの計算方法の違いから生まれたものだと考えています。例えば、透過度が1になるように値を計算する際に$R_{r,g,b}$の値を切り上げすれば同じようになります。

これで、前景色と背景色が透過度を持つ場合に表示される色について求められるようになりました。ツールを使えば簡単にわかる値ですが、計算方法を知ることでより色への理解をより深められたと思います。
あなた、どの細胞？

様々な細胞実験をやったことのある人は気づいたかもしれないけど、細胞ってヒトみたい。
いや、ヒトって細胞みたいっか:thinking:

**ここから細胞について豆知識がてら、細胞から学んだことについての話しようかなと。友達との雑談のように、あるいは気分転換程度の軽い気持ちで読んでいただければ！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957799/f66df7b8-483e-a5bd-62af-5c0626f00d97.png)

**細胞**はめちゃくちゃ種類があり（腎臓細胞、皮膚細胞、、）、全部違うように思われちゃうけど、**だいたい似ている**。培地の中に栄養があり、外からの急激な刺激などがない限り、生きていけれる。自分の知る限り、培養される細胞は大きく２つに分かれる。

１．**接着細胞** : 内向的なヒトみたい。あっちこっち行きたがらず、安定している。多くのヒトとかかわるのも嫌で、周りの何人かの友達とずっと一緒。多くなりすぎると元気じゃなくなっちゃう。
２．**浮遊細胞** : 社交的な方。とにかくみんなでいるのが好き。少なすぎて友達がいなくなると、落ち込んで、あまり増殖しなくなる。逆に、どんなに密でも元気で生きていけれる。

多く分けてこの２種類だけど、その中に、いつでも急ぐヒトのように成長が早いものだったり、成長が遅いけど、信じて待ってくれれば、ちゃんと劇的に成長し始めるものだったり、いつ電話しても笑って話してくれる友達のようにいつでも元気なものだったり様々。

ある細胞を育てていて、あー、だれみたいだなと思ったこともあるぐらいヒトと細胞って似ている。

いや、違うでしょ、ヒトって脳があって、ちゃんと考えて行動・コミュニケーションをしているから動物、植物よりも違うって言われているのに、さすがに細胞は？

ちゃんと見てみれば、動物でも、植物でも、ヒトでも、細胞でもやっていることが一緒で、形だけが違うと言える。ヒトだって、情報を受け取って、処理して、それに基づいて考えて、行動やコミュニケーションをするのに対して、細胞はセンサーで周りから情報を取得し、それに基づき代謝を変えたり、分泌したり、何かに作用したりするし、分泌などにより周りに情報も伝えることができる。

未だに納得していなければ、意見聞かせてください:ok_woman:！あるいはまあこう考える人もいるかと思い、笑ってください。

じゃ、あなた、どの細胞？
って聞いているのは、細胞って全部ある程度特徴があるので、ヒトを細胞で表すことが可能だと思っているから！自分の細胞名探してみようか？以下、完全に自作で、細胞のどういう特徴を意味しているかによって色々変わるので、ご理解・ご了承ください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957799/895fd1a6-dddf-9397-6c3b-bb50434f94f0.png)

ここから、もう一つ面白い考え方が生じる。ヒトが集まり社会を作り、地球上に暮らしている。細胞もヒトみたいにみんなで集まり、臓器として機能し（いわゆる国かもね）、臓器が協力して、生体・ヒト（これは地球・世界に至るかも）を作る。細胞をヒトに例えると、あなた細胞でありながら、細胞にとっては地球・世界かもね！<3

[*Click here!:wink:*](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957799/648c815e-e823-5569-3493-aacc5f6bbaa6.png)

*Have a wonderful day/week/month/year!*
テザリングで回線速度が遅い！と思ったら読む記事

スマホテザリングで回線速度がどうも遅いなぁ、と思ったら試してほしいことをまとめました。

# まずはキャリア公式の障害情報をチェック
障害発生している場合は手立てがないので最初にチェック。
ドコモ
https://www.docomo.ne.jp/info/network/?icid=CRP_menu_to_CRP_INFO_network
au
https://www.au.com/information/notice_mobile/network/
ソフトバンク
https://www.softbank.jp/mobile/info/personal/important/

# 通信制限にかかっていないか確認
月利用容量超過による制限、3日間利用量制限などでキャリア側から速度制限されているパターン。
※法人の場合は法人全体で容量をシェアするプランの場合もあるので、情シス担当者に確認。

# 電波が届きにくい場所にいないか確認
- キャリア公式サイトでサービスエリア内にいるか確認する
- 電車の中、高層階、トンネルの中、車の中、ビルとビルの間などは繋がりにくい。
- 屋内で「コンクリート製かつ複層ガラス」の場合、電波を通しにくい性質を持つため繋がりにくい。原始的だが窓を開けて窓際に行くととつながる場合がある。
- 意外と盲点なのが「半地下は繋がりにくい」。屋外は基地局、屋内や地下は屋内基地局やフェムトセル（モバイル基地局）で対策されていることが多いですが、半地下は両方の電波が届きにくいため接続が遅くなったり、途切れたりします。なるべく半地下は避けて屋内か屋外での通信をおすすめします。

# 「互換性を優先する」スイッチをオンにする
※iOS、ipadOS端末限定
（具体的にはiPhone 12モデル以降、iPad Pro 11インチ（第3世代以降）、iPad Pro 12.9インチ（第5世代以降）、iPad mini（第6世代））
テザリングを2.4GHz帯限定にすることでパフォーマンスは落ちますが、接続が安定する場合があります。

https://support.apple.com/ja-jp/119837#wifi

# (2.4GHz帯を使っている場合)　電子レンジなどで電波干渉されていないか確認する
有名な話ですが、2.4GHz帯は電子レンジなど他の家電や電子機器でも使われている周波数帯のため
電波干渉が起きやすいです。

- 最近のスマホは自動で空きチャネルに切り替えてくれるので、しばらく待つとつながるときがある。
- wifi analyzerなどで確認して、混雑しているチャネルがないか調べる。
https://forest.watch.impress.co.jp/library/software/wifianalyzer/
- 混雑している場合、5GHz帯に切り替えると安定することがある。

# USBテザリングを試してみる
速度はwi-fiテザリングの方が早いですが、環境によっては有線接続であるUSBテザリングのほうが接続が安定する場合があります。
※bluetoothテザリングは最新規格の3.0でも理論上の伝送速度が24Mbpsなので、試すならUSBテザリングのほうがおすすめ。

# それでも遅い場合は…
情シスに相談しましょう。
今更JuliaについてちゃんとPythonと実行速度について計測比較した

# なぜ今更比較するのか
- Juliaを知って3年経つけど他の言語と比較とかを自分でしてこなかったから
- 初学者でも簡単に速さについて驚いてもらいたいから
- Pythonを使っていたプログラムをJuliaにしただけで早くなった感動を共有したかったから

おおまかにはこのへんで他にも理由はありますが、長くなるので割愛します。

# 誰に向けてか
- Juliaについて知らない、触ったことない人
- Pythonに限界を感じている人
- プログラミングをやったことなくても簡単に速いプログラムが書きたい人

私自身プログラミング経験豊富であったり、JuliaやPythonについてなんでも知っているようなベテランではありません。
なのでこの記事はJuliaについて詳しい人や、Pythonをマスターして意のままに操れる人には何言ってんだもっと色々あるだろと思われる内容です。
逆に理系大学生は研究のプログラムがいまいちだと感じたら読んでみるといいかもしれません。

# なぜPythonなのか
- Juliaと比較されることが一番多い言語だと個人的に思った
- Juliaは汎用的言語だからPythonと比較したかった
- 機械学習をJuliaで取って代わりたい

当然インタプリタのPythonとコンパイラのJuliaは工夫をしなきゃJuliaが勝つに決まってますが、これは初学者向けなので難しい工夫とかをしなくても高速にかけるよということを示したかったのでPythonと比較してます。
そのため、静的言語も今回選考対象外です。

# 実行環境
- Python 3.10.12
- Julia 1.11.1
- Ubuntu 22.04.5

# 比較するプログラムについて
今回処理する内容は、
- プログラムが簡単
- 処理時間も増やしやすい
- 真値があって結果の比較もできる

この3点から交代級数をプログラムしたいと思います。

### 交代級数とは
交代級数とは分母の数が$1/2, 1/3, 1/4...$と増ふやしてそれを足し引きする数学の定番公式です。
式は以下のようにかけます

$$
S = \sum_{i=1}^{N} \frac{(-1)^{i-1}}{i}
$$

この級数の和は、$N$ を無限大にしたときに自然対数の2に収束します

$$
\lim_{N \to \infty} S_N = \ln(2)
$$

交代級数を使えば$N$を増やせば簡単に処理速度を重くでき、真値との比較もしやすいです。

# PythonとJuliaの実行速度の比較
実際の処理をしていきます。
環境はJuliaとPythonなのでやっぱりJupyterを使いたいと思います。

### Pythonのコード
```python
import time
import math

sum = 0;
N = 10000000;

start = time.perf_counter(); #計測開始

for i in range(1, N+1):
    sum += 1 / i * (-1) ** (i - 1);

stop = time.perf_counter(); #計測終了

print("交代和　    :", sum);
print("真値　　    :", math.log(2));
print("実行時間[s] :", stop-start);
```

### 実行結果
```
交代和　    : 0.6931471305601064
真値　　    : 0.6931471805599453
実行時間[s] : 2.1115614400005143
```

### Juliaのコード
```julia
sum = 0.0
N = 10000000

# @elapsedはスコープ内の処理時間の計算を行える
elapsed_time = @elapsed begin
    for i = 1:N
        sum += 1 / i * (-1)^(i - 1)
    end
end

println("交代和　    : ", sum)
println("真値　　    : ", log(2))
println("実行時間[s] : ", elapsed_time)
```

### 実行結果
```
交代和　    : 0.6931471305601064
真値　　    : 0.6931471805599453
実行時間[s] : 1.386452364
```

### 比較結果
**時間**
Pythonは2.1秒、Juliaは約1.4秒
0.7秒速いですがあんまりと思われる方もいるかもしれません

**計算結果**
有効桁数は7桁で、値はまさかの全く一緒、言語によって差が出るものもしばしばあるのですがすごい

**その他**
強いて言うならJuliaはlogや計測にライブラリを使わないのでちょっと楽

### ただし、こんなので終わるJuliaではない！
先程難しい工夫はしないと言いましたが、簡単な工夫で高速にできるならしてみたいですよね。
なんとJuliaは**関数にいれるだけ**で速くなります。

### Juliaのコード(関数)
```julia
# juliaの関数名はスネークケース推奨
function alternating_series()
    sum = 0.0
    N = 10000000
    
    for i = 1:N
        sum += 1 / i * (-1)^(i - 1)
    end
    return sum
end

# @elapsedで処理時間を変数に格納
elapsed_time = @elapsed sum = alternating_series()

# 結果を表示
println("交代和　    : ", sum)
println("真値　　    : ", log(2))
println("処理時間[s] : ", elapsed_time)
```

### 実行結果
```
交代和　    : 0.6931471305601064
真値　　    : 0.6931471805599453
処理時間[s] : 0.395924064
```

計算結果は変わらず、まさかの1秒も巻いて約4分の1に！！
Pythonより1.7秒も速いです！

# まとめ
- JuliaはPythonよりも圧倒的に速い
- ライブラリについても調べなくても数値計算に必要なものはデフォルトで入っている
- 精度などもPythonと一緒

Juliaは簡単でかつPythonより圧倒的に速いです。
インタプリタで1000万もループ回して2秒ちょいのPythonも十分ですがこの簡易的な内容での1.7秒はシステムが大きくなったり、複雑化してくると後々活きてくるので大切です。
こんなに簡単で速い言語のJulia是非みなさんもお試しあれ。
ブラウザ拡張機能でクリップボードを使う

# 概要

これは[初心者のブラウザ拡張機能 Advent Calendar 2024](https://qiita.com/advent-calendar/2024/browser-extension)の10日目の記事です。

ここではOSのクリップボードを使う方法を記載します。

# 使う関数

`navigator.clipboard`を使います。
[Clipboard](https://developer.mozilla.org/ja/docs/Web/API/Clipboard)

| 関数 | 機能 |
| ---- | ---- |
| navigator.clipboard.read() | 任意のデータの読み込み |
| navigator.clipboard.readText() | テキストデータの読み込み |
| navigator.clipboard.write() | 任意のデータの書き込み |
| navigator.clipboard.writeText() | テキストデータの書き込み |

:::note warn
`read()`と`write()`はすべてのブラウザに互換性があるわけではないため、使用する際は注意すること
:::

# 具体的なソースコード

クリップボードへテキストを書き込んで通知する拡張機能です。

```javascript:background.js
browser.menus.create(
  {
    id: "sample",
    title: "sample menu",
    contexts: ["page"],
  }
);

function clipboardWrite(data) {
  navigator.clipboard.writeText(data).then(
    () => {
      createNotification("OK", "コピーしました")
    },
    () => {
      createNotification("Failed", "失敗しました")
    },
  );
}

function createNotification(title, message) {
  browser.notifications.create({
    type: "basic",
    title: title,
    message: message,
  });
}

browser.menus.onClicked.addListener((info, tab) => {
  console.log("clicked");
  if (info.menuItemId === "sample") {
    clipboardWrite("hogehoge");
  }
});
```

`clipboardWrite`関数内で`writeText`関数を呼び出しクリップボードへ書き込みます。返り値が`Promise`なので`then`を使って成功時と失敗時で処理を分けます。

後続処理では成功、失敗に応じて通知メッセージを変えています。

```json:manifest.json
{
    "browser_specific_settings": {
        "gecko": {
          "id": "the-town-sample-clipboard@example.com"
        }
    },
    "manifest_version": 3,
    "name": "init-extention",
    "version": "0.1",
  
    "description": "クリップボードサンプル",
  
    "icons": {
      "48": "icons/icon-48.png"
    },

    "background": {
        "scripts": ["background.js"]
      },
  
      "permissions": [
        "menus",
        "notifications"
      ]
}
```

必要な権限はメニューと通知にアクセスするため`menus`と`notifications`を入れていますが、クリップボード関連ではないです。

:::note info
書き込み時はアクティブなタブへの権限が自動的に付与されます。
読み込み時は`clipboardRead`を割り当てる必要があります。
※Firefoxの場合
:::


# 参考

[クリップボードとのやりとり](https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard)
LINE WORKS API Bot の固定メニューを登録する


# LINE WORKS API を使った固定メニューの登録方法

LINE WORKS の Bot 固定メニューを API を使用して登録する方法を解説します。この記事では、Google Colab を使用して固定メニューを登録する Python スクリプトを紹介します。

## 必要な準備

以下の準備が必要です：

1. **Access Token**
2. **Bot ID**: 作成した Bot の ID を確認してください。
3. **必要なスコープ**: `bot`

## スクリプトを試す

以下の共有リンクをクリックして、Google Colab 上ですぐにスクリプトを試すことができます：

[Google Colab で固定メニューを試す](https://colab.research.google.com/drive/1JeZe633oC3t0tzxBXYhJ0cGjFCNJEX-0?usp=sharing)

1. 上記リンクをクリックして Google Colab にアクセスします。
2. **`ACCESS_TOKEN`** と **`BOT_ID`** を入力してください。
3. セルを実行すると、固定メニューが登録されます。

## スクリプトの説明

以下のスクリプトは、Google Colab にコピーして実行することもできます。

```python
import requests
import json

# 必要な変数を設定
ACCESS_TOKEN = "your_access_token"  # @param {type:"string"}
BOT_ID = 2000001  # @param {type:"integer"}

# 固定された API サーバー URL
API_SERVER = "https://www.worksapis.com"

# 固定メニューのデータを作成
MENU_DATA = {
    "content": {
        "actions": [
            {
                "type": "message",
                "label": "メッセージ送信",
                "text": "こんにちは！",
                "postback": "postback_message"
            },
            {
                "type": "uri",
                "label": "Webサイトを開く",
                "uri": "https://example.com"
            }
        ]
    }
}

# 固定メニューを登録する関数
def register_persistent_menu(menu_data):
    """固定メニューを登録する関数"""
    url = f"{API_SERVER}/v1.0/bots/{BOT_ID}/persistentmenu"
    headers = {
        "Authorization": f"Bearer {ACCESS_TOKEN}",
        "Content-Type": "application/json"
    }
    response = requests.post(url, headers=headers, data=json.dumps(menu_data))
    
    if response.status_code in [200, 201]:
        print("✅ 固定メニューが正常に登録されました！")
        print("Response:", response.json())
    else:
        print("❌ 固定メニューの登録に失敗しました。")
        print(f"Status Code: {response.status_code}")
        print(f"Response: {response.text}")

# メイン処理
def main():
    print("📤 固定メニューを登録中...")
    register_persistent_menu(MENU_DATA)

# 実行
if __name__ == "__main__":
    main()
```

これで LINE WORKS の固定メニューを登録することができます。質問があればコメントでお知らせください！
GitHubActionsのDockerとInsecureなレジストリを使いたいぼくの格闘記


## はじめに

みなさん、GitHubActionsって使ったことありますか？
多分これを読んでいる方は使ったことがあるのだと思います。

今回は、GitHubActionsで自前のDockerRegistryにPushしようとして詰まった話を書いていきたいと思います。

## 何があったのか

当時、私の構築したDockerRegistryはMicrok8sのアドオンのContainer-registryで建てたものでした。
しかし、これは当たり前ですが、Nginxを通していなかったので、SSL化されていませんでした。

なぜNginxを通していなかったかというとですね...グローバルIPの固定がされていなかったため、IngressのIPv6→Cloudflareという形でIPv4もプロキシを通して公開できるようにしていたのです。
しかし、ここで問題があり、Cloudflareを通すと、一回のアップロード制限がかかり、DockerPushに失敗するのです。

そこで、IPv6をそのまま指定したり、IPv4のポート指定でゴリ押そうとしたりしてハマったことを書いていこうと思います。

## IPv6は使えない

まず、GitHub側が用意してくれているDockerPushのWorkflowのホストのところをIPv6にして実行してみました。

### 結果

そもそもホストに辿り着かない皆のエラー...
ふぁ！？ですよね。
なぜだと思って、調べてみましたが、全く見つからず...

pingを飛ばすflowも組んでみましたが、GoogleのIPv6にすら繋がらなかったので、もしかするとGitHubActionsがIPv6に対応していないのかもしれません。

何はともあれ、IPv6は諦めました。

## Insecureの設定をする

ローカルのDockerでやった設定を思い出すと、何やらJSONのDockerの設定ファイルを編集した覚えがあります。
つまりは...**「GitHubActions上でもDockerConfigをいじっちゃえばいいんだ！！」**
ということで、`/etc/docker/daemon.json`を編集してみました。

```yaml
- name: Enable Docker configuration
  run: |
    SW_JSON=""
    SW_JSON="$(sudo cat /etc/docker/daemon.json | jq '.+{ "insecure-registries":["${{ secrets.K8S_DOCKER_REGISTHOST }}"],"max-concurrent-uploads": 1,"debug":true }')"
    echo "${SW_JSON}" | sudo bash -c 'cat -- > /etc/docker/daemon.json'
    sudo cat /etc/docker/daemon.json
    sudo systemctl restart docker || sudo journalctl -xeu docker.service
    sudo systemctl status docker
  shell: bash
```

上から何をしているかというと、

1. SW_KSONに元からある`/etc/docker/daemon.json`を編集して、insecure-registriesに対象のホストを追加
2. `/etc/docker/daemon.json`に書き込む
3. dockerを再起動し、ログを表示
4. 最後にステータスを表示

これで、DockerにInsecureRegistry(SSL通信ができないRegistry)であることを設定でき、問題なく動貸すことができました。

## 最後に

今回のは少々やばいRegistryを使っていたんですよね。
というのも、**認証もSSLもない**とかいうガバガバだったのですよ...

最終的には、**Harbor**というHelmChartのRepoにもなるようなものを導入しました。
IPv4問題は、VPSにNginxを入れてリバースプロキシさせて解決しました。

Harborについては、以下の記事でも紹介しているので、興味がある方は読んでいただくと幸せになれるかもしれません。

https://qiita.com/yuito_it_/items/527cab05e392cb50c129
ISUCON時のgithub連携を1passwordとssh-agentで少し楽にする方法

ISUCON14お疲れ様でした。
私は昨年のISUCON13にも出場したのですが、ssh-agentの存在を知らず本番サーバーで自分以外pull、pushできないという事態に陥ったので今年はそちらを改善した（多分他の皆さんは当たり前のようにssh-agent使ってるなだろうなとは思いつつ）ので備忘録として記事に残そうと思います。

# 前提
私たちのチームでは、以下のフローでコードの改善を行っています。

1.本番サーバーにはいって、webapp配下のコードをgithubにあげる
2.各々がローカル環境にコードをCloneして各々のブランチで作業
3.ベンチを試したくなったら、他の人と被らないようにサーバーにSSHして、自身のブランチをpullしベンチを回す
4.スコアが上がっていそうだったら、mainブランチにmergeする

# 問題点
git pushをする際、githubの公開鍵をサーバーに持っていく or 認証のために本番サーバーでDeployKeyを生成してレポジトリのsettingに貼り付けたりとか何かとめんどくさいですね。

また、これが自分一人でやっている素振りの時ならいいのですが、本番サーバーには一つのgithuserしか登録されてないので、複数人で改善を行う際にはどのUserがpull、pushしたのかコミットログに残らないので不便です（多分？）

# 解決策
ssh-agentを使うとここらへんの面倒なところが多少楽になります。
ついでに、1passwordで秘密鍵を管理していると、もう少し幸せになれます。


## ssh-agentとは？
ssh-agentはSSHキー（特に秘密鍵）を管理するためのプログラムで、SSH認証をより便利かつ安全に行うために使われます。このエージェントを利用することで、複数のSSHセッションをまたいで、一度鍵のパスフレーズを入力すれば、その後はパスフレーズを再度入力する必要なくSSH接続ができるようになります。

こんな感じでgithubの秘密鍵を登録しておくと、接続先のサーバーでもその秘密鍵が使える（フォワーディング）ようになります
```
//keyを登録
ssh-add ~/.ssh/id_rsa
//ssh
ssh server
// keyが登録されているかを確認
ssh-add -l
//keyの情報が出力される（登録したkeyがフォワーディングされている）
```
しかし、keyの登録は永続的に続かず、セッションが終了すると破棄されます。

## 1passwordとは？
1passwordはいわゆるパスワードマネージャーというやつで、パスワードに限らず、文書やSSHキーまで秘匿化して一元管理してくれます。
https://developer.1password.com/
また、1passwordに登録したsshKeyも当然ssh-agentに登録することができます。つまり、fowrdingも行うことができます。

`ssh-add`のようにCLIのセッション上で管理していないので、先ほどの述べたような、セッション切れによるkeyの登録切れが起こらないのが、1passwordでssh-keyを管理する利点です。

## 手順

1. 1passwordでgithubの秘密鍵を登録しておく
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/468909/fd895293-7ba7-101d-6b56-2ca4f29b9327.png)

2. ssh-agentの設定を有効にしておく
setting→開発者からssh-agentの設定を有効にできます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/468909/8ea84e26-2cb4-c906-de36-8a37565b7d56.png)



3. ~/.ssh/configに1passwordに登録されてあるssh-keyを読み込む設定を追加
以下の設定をconfigに追加します
```
# 1pass
Host *
  IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
```


公式Doc
https://developer.1password.com/docs/ssh/get-started/#step-4-configure-your-ssh-or-git-client




4　./ssh/configにISUCONサーバーのHostの設定を追加する。
ForwardAgentをyesに設定しないと、フォワーディングされないので注意です。

```
Host isucon14-1
  HostName xxxx
  User isucon
  ForwardAgent yes
　IdentityFile ~/.ssh/hoge.pem ←練習の場合は鍵の指定必要
```

configを設定しない場合は`-A`でフォワーディングの指定をします。
```
ssh -A isucon@xxxx 
```



これで下準備は完了で、あとはいつも通りsshするだけです

ssh先のサーバーで正常にフォワーディングされているかを確認

```
ssh-add -l
2048 SHA256:hogeeeeeeeeeeeeeee id_rsa (RSA)
```

github.comにsshできるか確認
```
ssh -T git@github.com
ubuntu@ip-10-0-0-161:~$ ssh -T git@github.com
Hi kojikokojiko! You've successfully authenticated, but GitHub does not provide shell access.
```

# まとめ
1passwordとても便利なので、個人でもバンバン使っていきたいなと思いました！
また、ssh-agentのTipsは業務でもよく使うので、覚えておいて損はないと思います。
ゼロから作るDeepLearning #5 -数式がいっぱい-

※以下の企画です

https://qiita.com/tatataichi/private/56936a028e625e62af8e

前回に続いてゼロつくやっていきます。
今回も4章 -ニューラルネットワークの学習-の続きです。
微分から勾配までの内容になるので、コーディングというよりかは数式のほうがいっぱいでてきそうな予感です。

それでは頑張っていきます〜

**※数学的に厳密に正しいことを述べられる自信はないので、温かい目で見守ってください※**

# 4章 ニューラルネットワークの学習
## 数値微分
ニューラルネットワークでは、パラメータ（重みやバイアス）を更新する際に、関数の変化率を求める必要がある。これはいわゆる微分という作業だ。

数値微分の基本的な式は以下の通り。

```math
f'(x) = \frac{f(x+h) - f(x-h)}{2h}
```

- `h` は微小な値（一般的には `10^{-4}` 程度）
- **中央差分法**を使用することで精度が高まる

というのが本書から得た学び。
中央差分というのは、$f(x+h) - f(x-h)$の部分で、ある点$x$の前後$h$距離分で傾きを求める（直線を引く）イメージ。
片方だけだと、実際の傾きと誤差が生じてしまう可能性があるので、前後の二点で傾きをもとめた方が良いとのことだ。たしかに。

Pythonで数値微分を実装すると以下のようになる。

```python:微分
def numerical_diff(f, x):
    h = 1e-4  # 0.0001
    return (f(x + h) - f(x - h)) / (2 * h)
```

ちょっとテストしてみる。
2次関数 $y = 0.01x^2 + 0.1x$に対して、数値微分を行う。

```python:二次関数の関数
def function_1(x):
    return 0.01 * x**2 + 0.1 * x
```

点$x = 5$および $x = 10$で数値微分を実行してみる。

```python:実行
print(numerical_diff(function_1, 5)) 
print(numerical_diff(function_1, 10))
```
```terminal:結果
0.1999999999990898
0.2999999999986347
```
解析的な解は$\frac{df(x)}{dx} = 0.02x+0.1$であり、$x$をそれぞれ代入すると
* $x=5$のとき、$\frac{df(5)}{dx} = 0.2$
* $x=10$のとき、$\frac{df(10)}{dx} = 0.3$
なので、ちゃんと微分できていそう。おもろ。

あと、ここで地味に学びが合ったのは、**関数って関数を引数にできるんだ**ということ。

## 偏微分
関数が複数の変数を持つ場合、**一つの変数について微分を行う操作を偏微分**と呼ぶ。
学生のころめちゃくちゃやったわ。本当にちゃんと何も覚えていないけど。

例えば、$f(x_1, x_2) = x_1^2 + x_2^2$を偏微分してみる。
- $x_1$ に関する偏微分：
  ```math
  \frac{\partial f}{\partial x_1} = 2x_1
  ```
- $x_2$ に関する偏微分：
  ```math
  \frac{\partial f}{\partial x_2} = 2x_2
  ```
上式の成り立ちは非常にシンプルで、偏微分しない方の変数を0にしているだけ。

数値微分を用いて偏微分を行う場合、片方の変数を定数とみなして先述の微分のコードを用いるだけで処理ができる。
あえてここでアウトプットはしない。面倒なわけではない。断じて。


## 勾配
勾配は、**関数の出力を最も速く増加させる方向を示すベクトル**のことらしい。
要は「**全ての変数に対して偏微分を行ってその結果をベクトルとしてまとめたもの**」っぽい。
例えば($x_0, x_1$)に対して、($\frac{d(x_0)}{dx},\frac{d(x_1)}{dx}$)を求めるという感じ。

全然関係ないが、大学院の研究で「購買効果」という文言を多用しているが、こういう記事を書いた後だと高確率で「勾配効果」と変換される。

$f(x_1, x_2) = x_1^2 + x_2^2$の勾配は以下のように求められる。

```math
\nabla f(x) = 
\begin{bmatrix}
\frac{\partial f}{\partial x_1} \\
\frac{\partial f}{\partial x_2}
\end{bmatrix}
=
\begin{bmatrix}
2x_1 \\
2x_2
\end{bmatrix}
```
※$\nabla$はナブラと読む。

Pythonのコードに落とし込んでみる。

```python:勾配
def numerical_gradient(f, x):
    h = 1e-4  # 0.0001
    grad = np.zeros_like(x)  # x と同じ形状の配列を生成

    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x)  # f(x+h)

        x[idx] = tmp_val - h
        fxh2 = f(x)  # f(x-h)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        x[idx] = tmp_val  # 値を元に戻す

    return grad
```
ややこしい感じがするが、与えられた入力(ここでは$x$)の数分だけ微分の作業をおこなっているだけ。
それではテスト実行もしてみる。

```python:テスト
def function_2(x):
    return x[0]**2 + x[1]**2  # f(x_1, x_2) = x_1^2 + x_2^2

print(numerical_gradient(function_2, np.array([3.0, 4.0]))) 
print(numerical_gradient(function_2, np.array([0.0, 2.0]))) 
print(numerical_gradient(function_2, np.array([3.0, 0.0]))) 
```
```terminal:結果
[6. 8.]
[0. 4.]
[6. 0.]
```
多分いい感じ！

# まとめ
今回はほぼ数学の内容で、昔学んだことを思い出すことができた。
「ニューラルネットワークは勾配法を用いて学習を行う」というのはなんとかく知っているが、勾配について説明することはできなかった。
ちゃんと「勾配って何？」と聞かれたときにスマートに回答できるレベルになれるように復習していきます。
Baby Gear – Rent Cribs &amp; Strollers | Traveling Baby Company

[Traveling Baby](https://www.travelingbaby.com/) provides parents with easy to carry, superior quality apparatus to accompany infants during trips. Look for strollers, car seats, cribs, carriers and lots of other products intended to be easy to use when a child is with you. Buy bassinets, blankets, toys, and other products designed for travel at amazing prices. Traveling Baby has everything that a baby will require whether in terms of safety, comfort or simple happiness during a family trip.
OMCB029E(623)

https://onlinemathcontest.com/contests/omcb029/tasks/4219

公式の解説がわかりやすかったので、その焼き増し(自分の理解用)です。

まず、$n$を素因数分解します。 ($p$は素数で、$a$は正整数です。)
$n=p_1^{a_1}×p_2^{a_2}×…×p_N^{a_N}$

約数の個数$d(n)$は、
$d(n)=(a_1+1)×(a_2+1)×…×(a_N+1)$
です。
($d(4)$であれば$2^0,2^1,2^2$の$3$つ、$d(6)$であれば$2^0×3^0,2^0×3^1,2^1×3^0,2^1×3^1$の$4$つといった具合で機械的に求められます。)

$n^2$を素因数分解すると
$n^2=p_1^{2a_1}×p_2^{2a_2}×…×p_N^{2a_N}$
となるので、$d(n^2)$は、
$d(n^2)=(2a_1+1)×(2a_2+1)×…×(2a_N+1)$
です。

さて、$d(n^2)=3d(n)$を満たすためには、
$\frac{2a_1+1}{a_1+1}×\frac{2a_2+1}{a_2+1}×…×\frac{2a_N+1}{a_N+1}=3$
となる必要があります。

$\frac{2a+1}{a+1}$の部分だけ考えると、
$a=1,2,…$と増やしていくと、$\frac{3}{2},\frac{5}{3},…$となり$2$に収束するので、
$\frac{3}{2}≦\frac{2x+1}{x+1}<2$になります。

$(\frac{3}{2})^2<3<(\frac{3}{2})^3$
$2^1<3<2^2$
なので、$p$は$2$種類ということがわかります。
($p$が$1$種類ではどんな条件でも$3$より小さくなり、$3$種類では$3$より大きくなってしまうため)

一旦、素数$p$は置いておいて、指数の$a_1,a_2$を考えます。(便宜的に$a_1≧a_2$とします。)
$3$は素数なので、
$a_1+1=2a_2+1$ $…①$
$2a_1+1=3×(a_2+1)$ $…②$
で確定しても問題なさそうです。

$①$を変形した$a_1=2a_2$を$②$に代入すると
$2×(2a_2)+1=3×(a_2+1)$
$a_2=2$
$a_1=4$
となります。

つまり、$p_1^4×p_2^2$の形になることがわかります。
(この時点で、$p_1$と$p_2$の大小関係はわかっていません。)

あとは最小の$n$を知りたいので、小さい素数を割り当てるだけです。
なので、$(p_1,p_2)$は$(2,3)$か$(3,2)$です。

$2^4×3^2<3^4×2^2$なので、解は$n=2^4×3^2=144$となります。
c#で、なでしこ その7

# 概要
c#で、なでしこ、やってみた。
c#な、なでしこ、見つけたので、msbuildでコンパイルしてみた。
練習問題やってみた。

# 練習問題
ランチャーを書け。

# サンプルコード

```

「0：　メモ帳を開く」を表示する
「1：　電卓」を表示する
「2：　ペイント」を表示する
「>」と入力。
それを命令に代入。
もし命令が「0」に等しいならば
　「notepad.exe」をコマンド実行
もし命令が「1」に等しいならば
　「calc.exe」をコマンド実行
もし命令が「2」に等しいならば
　「mspaint.exe」をコマンド実行



```

# 実行結果

```
>cnako2 run.nako
0：　メモ帳を開く
1：　電卓
2：　ペイント
>
2
```

以上。
[OCI]jupyter notebookで作成したpythonプログラムを自動実行する(OL8)

# 1. はじめに
OL8上にインストールした jupyter notebook で作成した python プログラムを cron 登録し、自動実行させます。

### 前提条件
https://qiita.com/twakimura/items/e2b145e2460c388ba0cc
この記事を参考に Compute インスタンス上に jupyter notebook を導入しています。

# 2. コードの作成
ここでは、TESTテーブルの主キーをカウントアップした値とpython実行時に渡された引数を挿入する処理を記載しています。
プログラムに渡される引数は sys.argv[n] で取得できます。

```python:batch.ipynb
import os
import sys
import pandas as pd
from sqlalchemy import create_engine

# 引数チェック、引数が設定されていないとエラーとする。
try:
    param1 = sys.argv[1]  # パラメータ1
except IndexError:
    print("Error: You must provide one parameter.")
    sys.exit(1)

# oracledb接続情報の取得
un = os.environ.get('ORAUSER')
pw = os.environ.get('ORAPASS')
cs = os.environ.get('ORATNS')
wallet_dir = os.environ.get('TNS_ADMIN')
wallet_pw = os.environ.get('WALLETPASS')

# SQLAlchemyエンジンを作成
engine = create_engine(f'oracle+oracledb://{un}:{pw}@{cs}', connect_args={
    "config_dir": wallet_dir,
    "wallet_location": wallet_dir,
    "wallet_password": wallet_pw
})

# DB接続処理
with engine.connect() as conn:
    
    # 主キーの最大値をカウントアップして挿入キーにする
    query = "SELECT MAX(TESTNO) FROM TEST"
    df = pd.read_sql(query, con=conn)
    new_testno = df.loc[0, 'MAX(TESTNO)'] + 1
    
    # TESTNAMEには引数で渡された値を設定する。
    list = [[new_testno,param1]]
    df = pd.DataFrame(list, columns=["TESTNO", "TESTNAME"])
    
    # 既存のテーブルにデータを追加
    df.to_sql('test', con=engine, if_exists='append', index=False)

# 接続を閉じる
engine.dispose()
```

# 3. .pyファイルの作成
jupyter notebook で作成した .ipynb ファイルを linux のコマンドライン上で実行できるように .py ファイルに変換します。

    $ jupyter nbconvert --to script batch.ipynb
    [NbConvertApp] Converting notebook batch.ipynb to script
    [NbConvertApp] Writing 1131 bytes to batch.py
    $ ls -l batch.*
    -rw-r--r--. 1 oracle oinstall 3451 Dec  9 10:42 batch.ipynb
    -rw-r--r--. 1 oracle oinstall 1349 Dec  9 10:50 batch.py

.py ファイルが作成されました。
実行は以下でできます。

    $ python batch.py Test3

データが格納されたことを確認します。

    SQL> select * from TEST;

	    TESTNO TESTNAME
	---------- --------------
	         3 Test3
	         1 テスト1
	         2 テスト2

	SQL>

 # 4. cron登録
定期的に実行するバッチ処理はcronに登録して実行しますが、上記の .py ファイル実行コマンドをそのままcron登録してもうまく動きません。
cron実行時はユーザーのbash_profile等が考慮されないためです。
そのため、シェルファイルを作成し、その中で環境変数の読込や実行ディレクトリ指定、.py の実行を記載します。
cronの検証として毎分実行するようにしますので、引数には実行時の時間を渡して確認するようにしました。

```sh:run_batch.sh
#!/bin/bash
# 環境変数の読込
source ~/.bashrc
# 実行ディレクトリに移動（相対パスの記載がある場合は必要）
cd ~/python
# batch.pyの実行
python batch.py "$(date '+%T')"
```
作成したシェルには実行権限を付けておきましょう。
シェルの準備ができたらcron登録します。

    $ crontab -e
     * * * * * ~/sh/run_batch.sh >> ~/log/run_batch.log 2>&1

数分たったらcronの設定を#でコメントアウトして停止します。
データが格納されたことを確認します。

	SQL> select * from TEST;

	    TESTNO TESTNAME
	---------- --------------
	         3 Test3
	         1 テスト1
	         2 テスト2
	         4 01:52:01
	         5 01:53:02

	SQL>

これで jupyter notebook で作成した python プログラムを自動実行することができました。
[OCI]Compute上のjupyter notebookからWebスクレイピング(OL8)

# 1. はじめに
OL8上にインストールした jupyter notebook を使い、Webサイトからデータを取得します。

### 前提条件
https://qiita.com/twakimura/items/e2b145e2460c388ba0cc
この記事を参考に Compute インスタンス上に jupyter notebook を導入しています。

# 2. 必要なライブラリのインストール
まずhtmlを解析するライブラリをインストールします。

    $ pip3 install requests
    $ pip3 install bs4
    $ pip3 install selenium

また、リッチな解析のために selenium を使うため、Google Chrome の Web Driver もインストールします。

    $ sudo dnf install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
    $ sudo dnf info google-chrome-stable
	google-chrome                                                          14 kB/s | 4.4 kB     00:00
	Installed Packages
	Name         : google-chrome-stable
	Version      : 131.0.6778.108
	Release      : 1
	Architecture : x86_64
	Size         : 348 M
	Source       : google-chrome-stable-131.0.6778.108-1.src.rpm
	Repository   : @System
	From repo    : @commandline
	Summary      : Google Chrome
	URL          : https://chrome.google.com/
	License      : Multiple, see https://chrome.google.com/
	Description  : The web browser from Google
	             :
	             : Google Chrome is a browser that combines a minimal design with sophisticated technology
	             : to make the web faster, safer, and easier.

インストールした chrome のバージョンを確認し、同じバージョンの chromedriver をインストールします。

    $ google-chrome --version
    Google Chrome 131.0.6778.108
    $ pip3 install --user chromedriver-binary==131.0.6778.108
    ERROR: Could not find a version that satisfies the requirement chromedriver-binary==131.0.6778.108

chrome のバージョンに該当する chromedriver が見つからないというエラーが出ました。
通常、バージョンを合わせるのが正しいと思いますが、今回はエラーメッセージに出力されていた存在するドライババージョンの中で近しいバージョンのものをインストールしました。

    $ pip3 install --user chromedriver-binary==131.0.6778.87.0

# 3. Webスクレイピング
今回は netkeiba さんの開催日情報を取得します。
https://race.netkeiba.com/top/calendar.html?year=2024&month=11

URLは上記のような形式になっており、年と月の情報をURLパラメータとして渡すとその月のカレンダーが取得できます。

```python:getCalendar.ipynb
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
import chromedriver_binary
import pandas as pd

# CUIで扱うためのオプション設定
options = Options()
options.add_argument('--headless')  # ヘッドレスモード
options.add_argument('--disable-gpu')  # GPUの無効化
options.add_argument('--no-sandbox')  # サンドボックスを無効化（必要に応じて）
options.add_argument('--disable-dev-shm-usage')  # /dev/shm の容量制限を回避

# ドライバ設定とURL取得
driver = webdriver.Chrome(options=options)
page = 'https://race.netkeiba.com/top/calendar.html?year=2024&month=11'
driver.get(page)
source = driver.page_source
driver.quit()

# htmlソースからCalendar_Tableを取得
soup = BeautifulSoup(source, 'html.parser')
table = soup.find(class_="Calendar_Table")

# ヘッダー行を取得
headers = [th.text.strip() for th in table.find_all("th")]

# テーブルのデータ行を取得
rows = []
for tr in table.find_all("tr")[1:]:  # データ行を取得（ヘッダーをスキップ）
    cells = [td.text.strip() for td in tr.find_all("td")]
    rows.append(cells)

# DataFrameに変換
df = pd.DataFrame(rows, columns=headers)

# DataFrame表示
df
```

カレンダー情報を取得することができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2514221/50b9f420-7462-87c8-d305-b1b441f1da5f.png)