職場で使っているスケジュール管理のWebアプリはAPIとかがない。
その場合はスクレイピングがめんどうと思っていたのですが、割と簡単にできると分かったのでやってみました。
AngleSharpを使用しました。
これでJavaScriptのようにDOM操作ができます。
機械翻訳
AngleSharpは、HTML、SVG、MathMLのような角括弧ベースのハイパーテキストを解析する機能を提供する.NETライブラリです。バリデーションのないXMLもサポートされている。AngleSharpの重要な点は、CSSも解析できることです。付属のパーサーは、公式のW3C仕様に基づいて構築されています。これは、与えられたソースコードの完全に移植可能なHTML5 DOM表現を生成し、常用ブラウザでの結果との互換性を保証します。また、querySelector や querySelectorAll のような標準的な DOM 機能も、ツリーの走査のために機能します。
以前の記事のAPI使わないバージョンのようなものです。
準備
- .NET9 vscode windows11
- dotnet add package AngleSharp
スケジュールの取得
以前のこの内容を変更していきます。
他はだいたいそのままです。
ScheduleAPIクラスの内容です。
GetTaskTodoAsync
スケジュールを取得してアラート通知していない新たなスケジュールをリストで返します。
このリスト内容がトースト通知の内容になる。
public static async Task<List<Schedule>?> GetTaskTodoAsync()
{
if (ScheduleList is null) return null;
using var client = new HttpClient();
if (!await LoginAsync(client)) return null;
var responseBody = await NavigateToPageAsync(client);
if (string.IsNullOrEmpty(responseBody)) return null;
var newScheduleList = ParseSchedule(responseBody);
if (newScheduleList is null) return null;
var newScheduleListFilter = newScheduleList
.Where(x => !ScheduleList.Any(y => x.IssueId == y.IssueId
&& x.StartTime == y.StartTime)
&& CheckPeriodFlag(x.StartTime))
.ToList();
ScheduleList.AddRange(newScheduleListFilter);
return newScheduleListFilter;
}
LoginAsync
サイトへのログイン処理。
userid
とpassword
は使用するサイトのname
を調べます。
だいたいform
のinput
となっている所。
private static async Task<bool> LoginAsync(HttpClient client)
{
var loginData = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string?>("userid", ConfigurationManager.AppSettings["Id"]),
new KeyValuePair<string, string?>("password", ConfigurationManager.AppSettings["Pass"])
});
var response = await client.PostAsync(ConfigurationManager.AppSettings["UrlLogin"], loginData);
if (!response.IsSuccessStatusCode)
{
Console.WriteLine("ログイン失敗");
return false;
}
return true;
}
NavigateToPageAsync
スクレイピングしたいページの内容をここでは単に文字列として取得しています。
http://192.168.xx.xxx/schedule/day?viewDate=2025/02/04
のようなurlなので、日付部分を変更して当日のスケジュールを取得するようにしています。
private static async Task<string?> NavigateToPageAsync(HttpClient client)
{
var response = await client.GetAsync(ConfigurationManager.AppSettings["UrlPage"] + DateTime.Today.ToString("yyyy/MM/dd"));
if (!response.IsSuccessStatusCode) return null;
return await response.Content.ReadAsStringAsync();
}
ParseSchedule
取得したページの文字列をAngleSharpで解析して必要な情報をリストにして返します。
IssueId
StartTime
Title
がトースト通知で表示する内容。
IssueId
はトースト通知の方で、ページ開く時に使うためurlとしています。
private static List<Schedule>? ParseSchedule(string responseBody)
{
if (ScheduleList is null) return null;
var parser = new HtmlParser();
var doc = parser.ParseDocument(responseBody);
Console.WriteLine(doc.Title);
return doc.GetElementsByClassName("aligned inCompany")
.Select(x => new Schedule()
{
IssueId = ConfigurationManager.AppSettings["UrlIp"] + x.GetAttribute("data-href"),
StartTime = ((IHtmlTableRowElement)x).Cells[0].TextContent.Trim().Split("~")[0],
Title = ((IHtmlTableRowElement)x).Cells[1].TextContent.Trim()
})
.ToList();
}
CheckPeriodFlag
開始時間が指定時間内か確認してboolで返します。
private static bool CheckPeriodFlag(string? startTime)
{
if (!DateTime.TryParse(startTime, out DateTime targetTime)) return false;
TimeSpan timeDifference = targetTime - DateTime.Now;
return 0 <= timeDifference.TotalMinutes
&& timeDifference.TotalMinutes
<= (int.TryParse(ConfigurationManager.AppSettings["TargetMinutes"], out var result) ? result : 0);
}
おわり
APIがなくても簡単にスクレイピングできました。
ヘッドレスなのでブラウザがチラつくような事もなく、常駐アプリとしていい感じにできました。
タスクトレイ
スケジュール取得してトースト通知したいアプリなので、普段はタスクトレイで常駐させておきます。
タスクトレイに入れるにはwinフォームの機能が必要でした。