|
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 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
Imports System Imports System.IO Imports System.Text.RegularExpressions Imports System.Collections.Generic Imports System.Net.Http ' ★追加: HttpClient を使用するため Module Program Public Class MtDataPhotoExtractor 'クラス名の重複を修正 ' 写真とブログ情報の関連付けを保持する構造体 Public Structure PhotoArticleInfo Public FileName As String Public BlogTitle As String Public PostDate As DateTime Public LocalFilePath As String ' ★追加: ダウンロードしたファイルのパスを保持 Public OriginalImageUrl As String ' ★追加: 元の画像URLを保持 End Structure ''' <summary> ''' MT形式ファイルを解析し、写真情報を抽出してダウンロードします。 ''' </summary> ''' <param name="mtFilePath">MT形式のエクスポートファイルのパス</param> ''' <param name="downloadDirectory">画像をダウンロードするディレクトリ</param> ''' <returns>抽出された写真情報のリスト</returns> Public Function ExtractPhotoInfoFromMt(mtFilePath As String, downloadDirectory As String) As List(Of PhotoArticleInfo) Dim photoList As New List(Of PhotoArticleInfo)() ' このファイル独自の区切り文字として、「--------」に改行が続くパターンを指定 Dim delimiters() As String = { "--------" & vbLf, "--------" & vbCrLf } ' ファイルの存在確認 If Not File.Exists(mtFilePath) Then Console.WriteLine($"エラー: ファイルが見つかりません - {mtFilePath}") Return photoList End If ' ダウンロードディレクトリの存在確認と作成 If Not Directory.Exists(downloadDirectory) Then Try Directory.CreateDirectory(downloadDirectory) Console.WriteLine($"ダウンロードディレクトリを作成しました: {downloadDirectory}") Catch ex As Exception Console.WriteLine($"エラー: ダウンロードディレクトリの作成に失敗しました - {downloadDirectory} - {ex.Message}") Return photoList ' ディレクトリ作成失敗時はダウンロード処理を行わない End Try End If Dim mtContent As String = File.ReadAllText(mtFilePath, System.Text.Encoding.UTF8) Dim articleBlocks() As String = mtContent.Split(delimiters, StringSplitOptions.RemoveEmptyEntries) Console.WriteLine($"MT形式データから {articleBlocks.Length} 件の記事ブロックを検出しました。") For Each block As String In articleBlocks Dim currentTitle As String = "タイトル不明" Dim currentPostDate As DateTime = DateTime.MinValue Dim currentBody As String = "" ' 本文を保持する変数 Dim currentExtendedBody As String = "" '追記を保持する変数 Dim isBodySection As Boolean = False Dim isExtendedBodySection As Boolean = False Dim lines() As String = block.Split(New String() {vbLf}, StringSplitOptions.None) For i As Integer = 0 To lines.Length - 1 Dim line As String = lines(i).TrimStart() If line.StartsWith("TITLE:", StringComparison.OrdinalIgnoreCase) Then currentTitle = line.Substring("TITLE:".Length).Trim() isBodySection = False isExtendedBodySection = False ElseIf line.StartsWith("DATE:", StringComparison.OrdinalIgnoreCase) Then Dim dateString = line.Substring("DATE:".Length).Trim() If DateTime.TryParse(dateString, currentPostDate) Then ' パース成功 Else currentPostDate = DateTime.MinValue Console.WriteLine($"警告: 日付のパースに失敗しました - {dateString} (記事タイトル: {currentTitle})") End If ElseIf line.StartsWith("BODY:", StringComparison.OrdinalIgnoreCase) Then currentBody = line.Substring("BODY:".Length).Trim() isBodySection = True isExtendedBodySection = False ElseIf line.StartsWith("EXTENDED BODY:", StringComparison.OrdinalIgnoreCase) Then currentExtendedBody = line.Substring("EXTENDED BODY:".Length).Trim() isBodySection = False isExtendedBodySection = True ElseIf line = "-----" Then If isBodySection Then isBodySection = False isExtendedBodySection = True ElseIf isExtendedBodySection Then isExtendedBodySection = False End If Else If isBodySection Then currentBody &= Environment.NewLine & line ElseIf isExtendedBodySection Then currentExtendedBody &= Environment.NewLine & line End If End If Next ' 抽出した本文から画像ファイル名を検索 Dim imgTagRegex As New Regex("<img\s+[^>]*?src=[""'](?<url>[^""']*)[""'][^>]*?>", RegexOptions.IgnoreCase) Dim matches As MatchCollection = imgTagRegex.Matches(currentBody) For Each match As Match In matches Dim imageUrlOrPath As String = match.Groups("url").Value Dim fileName As String = "" Try Dim uri As New Uri(imageUrlOrPath) fileName = Path.GetFileName(Uri.UnescapeDataString(uri.LocalPath)) Catch ex As UriFormatException fileName = Path.GetFileName(imageUrlOrPath) End Try If Not String.IsNullOrEmpty(fileName) Then Dim photoInfo As New PhotoArticleInfo() photoInfo.FileName = fileName photoInfo.BlogTitle = currentTitle photoInfo.PostDate = currentPostDate photoInfo.OriginalImageUrl = imageUrlOrPath ' ★画像のダウンロード処理 Dim localFilePath As String = Path.Combine(downloadDirectory, fileName) Dim success As Boolean = DownloadImage(imageUrlOrPath, localFilePath) If success Then photoInfo.LocalFilePath = localFilePath photoList.Add(photoInfo) Console.WriteLine($"ダウンロード成功: {fileName} -> {localFilePath}") Else Console.WriteLine($"ダウンロード失敗: {fileName} (URL: {imageUrlOrPath})") photoInfo.LocalFilePath = "" photoList.Add(photoInfo) End If End If Next Next Return photoList End Function ''' <summary> ''' 指定されたURLから画像をダウンロードし、指定されたパスに保存します。 ''' </summary> ''' <param name="imageUrl">画像のURL</param> ''' <param name="localFilePath">保存先のパス</param> ''' <returns>ダウンロードが成功したかどうか</returns> Private Function DownloadImage(imageUrl As String, localFilePath As String) As Boolean Try Using client As New HttpClient() ' ★修正: HttpClientを使用 Dim response As HttpResponseMessage = client.GetAsync(imageUrl).Result ' 同期処理 response.EnsureSuccessStatusCode() ' エラーレスポンスは例外を投げる Dim fileBytes As Byte() = response.Content.ReadAsByteArrayAsync().Result ' 同期処理 File.WriteAllBytes(localFilePath, fileBytes) End Using Return True Catch ex As Exception Console.WriteLine($"エラー: 画像のダウンロードに失敗しました - {imageUrl} -> {localFilePath} - {ex.Message}") Return False End Try End Function ''' <summary> ''' 抽出した写真情報をCSVファイルに保存します。 ''' </summary> ''' <param name="photoList">保存する写真情報のリスト</param> ''' <param name="csvFilePath">出力するCSVファイルのパス</param> Public Sub SavePhotoInfoToCsv(photoList As List(Of PhotoArticleInfo), csvFilePath As String) Try Using writer As New StreamWriter(csvFilePath, False, System.Text.Encoding.UTF8) ' ヘッダー行を書き込む writer.WriteLine("ファイル名,ブログタイトル,投稿日時,ダウンロードパス,元URL") ' 各写真情報を書き込む For Each photo In photoList Dim escapedFileName = EscapeCsv(photo.FileName) Dim escapedBlogTitle = EscapeCsv(photo.BlogTitle) Dim postDateString = If(photo.PostDate = DateTime.MinValue, "", photo.PostDate.ToString("yyyy/MM/dd HH:mm:ss")) Dim escapedLocalFilePath = EscapeCsv(photo.LocalFilePath) Dim escapedOriginalImageUrl = EscapeCsv(photo.OriginalImageUrl) writer.WriteLine($"{escapedFileName},{escapedBlogTitle},{postDateString},{escapedLocalFilePath},{escapedOriginalImageUrl}") Next End Using Console.WriteLine($"写真情報を {csvFilePath} に保存しました。") Catch ex As Exception Console.WriteLine($"エラー: CSVファイルの保存に失敗しました - {ex.Message}") End Try End Sub ' CSV書き出し用に特殊文字をエスケープするヘルパー関数 Private Function EscapeCsv(value As String) As String If value Is Nothing Then Return "" If value.Contains(",") OrElse value.Contains("""") OrElse value.Contains(Environment.NewLine) Then Return $"""{value.Replace("""", """""") }""" Else Return value End If End Function ' --- このクラスの使用例 --- ' (実際の使用時は、適切な場所に記述してください) Public Shared Sub Main() Try Dim extractor As New MtDataPhotoExtractor() Dim mtFile = "D:\gooBlog引越しTest\export_blog_2.txt" ' ★ここをあなたのMT形式ファイルのパスに変更してください★ Dim csvFile = "D:\gooBlog引越しTest\photo_list.csv" ' ★ここを保存したいCSVファイルのパスに変更してください★ Dim downloadDir = "D:\gooBlog引越しTest\images\" ' ★ここをダウンロード先のディレクトリパスに変更してください★ Dim extractedPhotos As List(Of PhotoArticleInfo) = extractor.ExtractPhotoInfoFromMt(mtFile, downloadDir) If extractedPhotos.Count > 0 Then extractor.SavePhotoInfoToCsv(extractedPhotos, csvFile) Console.WriteLine($"合計 {extractedPhotos.Count} 件の写真情報が抽出されました。") Else Console.WriteLine("写真情報は抽出されませんでした。本文中の画像タグの形式を確認してください。") End If Catch ex As Exception Console.WriteLine($"予期せぬエラーが発生しました: {ex.Message}") Finally Console.WriteLine("処理が完了しました。Enterキーを押して終了...") Console.ReadKey() End Try End Sub End Class End Module |
上のコードはGeminiに相談して、作ってもらったものです。