0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#でAPIがないサイトのスクレイピング

Posted at

職場で使っているスケジュール管理の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

サイトへのログイン処理。
useridpasswordは使用するサイトのnameを調べます。
だいたいforminputとなっている所。

    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フォームの機能が必要でした。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?