OzoneAsai commited on
Commit
d6926f4
1 Parent(s): b709c04

Update scrape_images_worker.py

Browse files
Files changed (1) hide show
  1. scrape_images_worker.py +172 -171
scrape_images_worker.py CHANGED
@@ -1,171 +1,172 @@
1
- import os
2
- import re
3
- from playwright.sync_api import sync_playwright
4
- import requests
5
- import sys
6
- from PIL import Image, UnidentifiedImageError
7
- from io import BytesIO
8
- log_file = "app_log.txt" # ログファイルのパス
9
-
10
- # ログフォーマットの定義
11
- log_format = '%(asctime)s - %(levelname)s - %(message)s'
12
-
13
- import logging
14
- file_handler = logging.FileHandler(log_file, encoding='utf-8')
15
- # ログの設定
16
- logging.basicConfig(
17
- level=logging.INFO, # ログレベルをINFOに設定
18
- format='%(asctime)s - %(levelname)s - %(message)s', # ログのフォーマットを指定
19
- handlers=[
20
- logging.StreamHandler(sys.stdout), # 標準出力にログを出力
21
- file_handler,
22
- ]
23
- )
24
- logger = logging.getLogger(__name__)
25
-
26
- # 安全なフォルダ名を生成する関数
27
- def generate_safe_folder_name(url):
28
- # URLから安全なフォルダ名を生成(ファイル名に使えない文字を除去)
29
- safe_name = re.sub(r'[^a-zA-Z0-9_\-]', '_', url)
30
- return safe_name
31
-
32
- # 画像を保存する関数 (JPG 80%の品質で保存)
33
- def save_image_as_jpg(image_url, save_folder, image_name):
34
- if not os.path.exists(save_folder):
35
- os.makedirs(save_folder)
36
- logger.info(f"フォルダを作成しました: {save_folder}")
37
-
38
- try:
39
- response = requests.get(image_url, timeout=10)
40
- response.raise_for_status() # HTTPエラーが発生した場合例外を投げる
41
- except requests.exceptions.RequestException as e:
42
- logger.error(f"画像のダウンロード中にエラーが発生しました: {e}")
43
- return
44
-
45
- try:
46
- image = Image.open(BytesIO(response.content))
47
- except UnidentifiedImageError:
48
- logger.warning(f"未識別の画像ファイル: {image_url}. スキップします。")
49
- return
50
- except Exception as e:
51
- logger.error(f"画像のオープン中にエラーが発生しました: {e}")
52
- return
53
-
54
- # 保存時に JPG に変換し、品質80%で保存
55
- image_path = os.path.join(save_folder, image_name)
56
- try:
57
- image.convert("RGB").save(image_path, "JPEG", quality=80)
58
- logger.info(f"画像を保存しました: {image_path}")
59
- except Exception as e:
60
- logger.error(f"画像の保存中にエラーが発生しました: {e}")
61
-
62
- # 画像の再帰的取得
63
- def scrape_images_by_page(url, folder_name='scraped_images'):
64
- # URLが"/"で終わっている場合、スラッシュを削除
65
- original_url = url
66
- url = url.rstrip('/')
67
- logger.info(f"処理するURL: {url}")
68
-
69
- with sync_playwright() as p:
70
- browser = p.chromium.launch(headless=False) # ブラウザを表示して操作
71
- page = browser.new_page()
72
-
73
- # 初期ページにアクセス
74
- page.goto(url)
75
- logger.info(f"ページにアクセスしました: {url}")
76
-
77
- # ページが完全に読み込まれるまで待機
78
- page.wait_for_load_state('networkidle')
79
- logger.info("ページの読み込みが完了しました。")
80
-
81
- # lazy-loading属性を無効にするためのJavaScriptを挿入
82
- try:
83
- page.evaluate("""
84
- document.querySelectorAll('img[loading="lazy"]').forEach(img => {
85
- img.setAttribute('loading', 'eager');
86
- img.src = img.src; // 画像を強制的にリロード
87
- });
88
- """)
89
- logger.info("lazy-loadingを無効化しました。")
90
- except Exception as eval_error:
91
- logger.warning(f"JavaScriptの評価中にエラーが発生しました: {eval_error}")
92
-
93
- # フォルダ名を生成
94
- safe_folder_name = generate_safe_folder_name(url)
95
- folder_path = os.path.join(folder_name, safe_folder_name)
96
- logger.info(f"保存先フォルダ: {folder_path}")
97
-
98
- # ページ数を取得
99
- try:
100
- # ページ数が格納されているセレクタからテキストを取得
101
- page_count_selector = 'div.tag-container:nth-child(8) > span:nth-child(1) > a:nth-child(1) > span:nth-child(1)'
102
- page_count_text = page.locator(page_count_selector).text_content().strip()
103
- num_pages = int(re.search(r'\d+', page_count_text).group())
104
- logger.info(f"セレクタ '{page_count_selector}' からページ数を取得: {num_pages}")
105
- except Exception as e:
106
- logger.warning(f"セレクタ '{page_count_selector}' からページ数を取得できませんでした: {e}")
107
- # セレクタが見つからない場合のフォールバック
108
- try:
109
- fallback_selector = 'section.reader-bar:nth-child(2) > div:nth-child(2) > button:nth-child(3) > span:nth-child(3)'
110
- page.wait_for_selector(fallback_selector, timeout=5000)
111
- num_pages_text = page.locator(fallback_selector).text_content().strip()
112
- num_pages = int(re.search(r'\d+', num_pages_text).group())
113
- logger.info(f"セレクタ '{fallback_selector}' からページ数を取得: {num_pages}")
114
- except Exception as e2:
115
- logger.error(f"ページ数の取得に失敗しました: {e2}")
116
- num_pages = 1 # デフォルトで1ページとする
117
-
118
- logger.info(f"総ページ数: {num_pages}")
119
-
120
- # 各ページにアクセスして画像を取得
121
- for i in range(1, num_pages + 1):
122
- page_url = f"{url}/{i}"
123
- page.goto(page_url)
124
- logger.info(f"ページにアクセスしました: {page_url}")
125
-
126
- # ページが完全に読み込まれるまで待機
127
- page.wait_for_load_state('networkidle')
128
- logger.info(f"ページ {i} の読み込みが完了しました。")
129
-
130
- try:
131
- # 画像を取得するセレクタ
132
- img_selector = '#image-container > a > img'
133
- img_elements = page.locator(img_selector)
134
- img_count = img_elements.count()
135
- logger.info(f"ページ {i} の画像数: {img_count}")
136
-
137
- if img_count == 0:
138
- logger.warning(f"ページ {i} に画像が見つかりません。")
139
- continue
140
-
141
- for j in range(img_count):
142
- try:
143
- image_element = img_elements.nth(j)
144
- image_url = image_element.get_attribute('src')
145
- if not image_url:
146
- # data-srcなどに画像URLが格納されている場合
147
- image_url = image_element.get_attribute('data-src')
148
- logger.info(f"取得した画像URL (ページ {i}, 画像 {j + 1}): {image_url}")
149
-
150
- if image_url:
151
- # ファイル名にページ番号と画像番号を含め、位取りを適用
152
- image_name = f'page_{str(i).zfill(5)}_img_{str(j + 1).zfill(5)}.jpg'
153
- save_image_as_jpg(image_url, folder_path, image_name)
154
- except Exception as e:
155
- logger.error(f"ページ {i}, 画像 {j + 1} の処理中にエラーが発生しました: {e}")
156
- continue
157
- except Exception as e:
158
- logger.error(f"ページ {i} の画像取得中にエラーが発生しました: {e}")
159
- continue
160
-
161
- browser.close()
162
- logger.info("ブラウザを閉じました。")
163
-
164
- if __name__ == "__main__":
165
- if len(sys.argv) < 2:
166
- logger.error("使用方法: python scrape_images_worker.py <URL>")
167
- sys.exit(1)
168
-
169
- url = sys.argv[1] # コマンドライン引数でURLを受け取る
170
- folder_name = 'scraped_images' # デフォルトのフォルダ名
171
- scrape_images_by_page(url, folder_name)
 
 
1
+ import os
2
+ import re
3
+ from playwright.sync_api import sync_playwright
4
+ import requests
5
+ import sys
6
+ from PIL import Image, UnidentifiedImageError
7
+ from io import BytesIO
8
+ os.systems("python3 -m playwright install")
9
+ log_file = "app_log.txt" # ログファイルのパス
10
+
11
+ # ログフォーマットの定義
12
+ log_format = '%(asctime)s - %(levelname)s - %(message)s'
13
+
14
+ import logging
15
+ file_handler = logging.FileHandler(log_file, encoding='utf-8')
16
+ # ログの設定
17
+ logging.basicConfig(
18
+ level=logging.INFO, # ログレベルをINFOに設定
19
+ format='%(asctime)s - %(levelname)s - %(message)s', # ログのフォーマットを指定
20
+ handlers=[
21
+ logging.StreamHandler(sys.stdout), # 標準出力にログを出力
22
+ file_handler,
23
+ ]
24
+ )
25
+ logger = logging.getLogger(__name__)
26
+
27
+ # 安全なフォルダ名を生成する関数
28
+ def generate_safe_folder_name(url):
29
+ # URLから安全なフォルダ名を生成(ファイル名に使えない文字を除去)
30
+ safe_name = re.sub(r'[^a-zA-Z0-9_\-]', '_', url)
31
+ return safe_name
32
+
33
+ # 画像を保存する関数 (JPG 80%の品質で保存)
34
+ def save_image_as_jpg(image_url, save_folder, image_name):
35
+ if not os.path.exists(save_folder):
36
+ os.makedirs(save_folder)
37
+ logger.info(f"フォルダを作成しました: {save_folder}")
38
+
39
+ try:
40
+ response = requests.get(image_url, timeout=10)
41
+ response.raise_for_status() # HTTPエラーが発生した場合例外を投げる
42
+ except requests.exceptions.RequestException as e:
43
+ logger.error(f"画像のダウンロード中にエラーが発生しました: {e}")
44
+ return
45
+
46
+ try:
47
+ image = Image.open(BytesIO(response.content))
48
+ except UnidentifiedImageError:
49
+ logger.warning(f"未識別の画像ファイル: {image_url}. スキップします。")
50
+ return
51
+ except Exception as e:
52
+ logger.error(f"画像のオープン中にエラーが発��しました: {e}")
53
+ return
54
+
55
+ # 保存時に JPG に変換し、品質80%で保存
56
+ image_path = os.path.join(save_folder, image_name)
57
+ try:
58
+ image.convert("RGB").save(image_path, "JPEG", quality=80)
59
+ logger.info(f"画像を保存しました: {image_path}")
60
+ except Exception as e:
61
+ logger.error(f"画像の保存中にエラーが発生しました: {e}")
62
+
63
+ # 画像の再帰的取得
64
+ def scrape_images_by_page(url, folder_name='scraped_images'):
65
+ # URLが"/"で終わっている場合、スラッシュを削除
66
+ original_url = url
67
+ url = url.rstrip('/')
68
+ logger.info(f"処理するURL: {url}")
69
+
70
+ with sync_playwright() as p:
71
+ browser = p.chromium.launch(headless=False) # ブラウザを表示して操作
72
+ page = browser.new_page()
73
+
74
+ # 初期ページにアクセス
75
+ page.goto(url)
76
+ logger.info(f"ページにアクセスしました: {url}")
77
+
78
+ # ページが完全に読み込まれるまで待機
79
+ page.wait_for_load_state('networkidle')
80
+ logger.info("ページの読み込みが完了しました。")
81
+
82
+ # lazy-loading属性を無効にするためのJavaScriptを挿入
83
+ try:
84
+ page.evaluate("""
85
+ document.querySelectorAll('img[loading="lazy"]').forEach(img => {
86
+ img.setAttribute('loading', 'eager');
87
+ img.src = img.src; // 画像を強制的にリロード
88
+ });
89
+ """)
90
+ logger.info("lazy-loadingを無効化しました。")
91
+ except Exception as eval_error:
92
+ logger.warning(f"JavaScriptの評価中にエラーが発生しました: {eval_error}")
93
+
94
+ # フォルダ名を生成
95
+ safe_folder_name = generate_safe_folder_name(url)
96
+ folder_path = os.path.join(folder_name, safe_folder_name)
97
+ logger.info(f"保存先フォルダ: {folder_path}")
98
+
99
+ # ページ数を取得
100
+ try:
101
+ # ページ数が格納されているセレクタからテキストを取得
102
+ page_count_selector = 'div.tag-container:nth-child(8) > span:nth-child(1) > a:nth-child(1) > span:nth-child(1)'
103
+ page_count_text = page.locator(page_count_selector).text_content().strip()
104
+ num_pages = int(re.search(r'\d+', page_count_text).group())
105
+ logger.info(f"セレクタ '{page_count_selector}' からページ数を取得: {num_pages}")
106
+ except Exception as e:
107
+ logger.warning(f"セレクタ '{page_count_selector}' からページ数を取得できませんでした: {e}")
108
+ # セレクタが見つからない場合のフォールバック
109
+ try:
110
+ fallback_selector = 'section.reader-bar:nth-child(2) > div:nth-child(2) > button:nth-child(3) > span:nth-child(3)'
111
+ page.wait_for_selector(fallback_selector, timeout=5000)
112
+ num_pages_text = page.locator(fallback_selector).text_content().strip()
113
+ num_pages = int(re.search(r'\d+', num_pages_text).group())
114
+ logger.info(f"セレクタ '{fallback_selector}' からページ数を取得: {num_pages}")
115
+ except Exception as e2:
116
+ logger.error(f"ページ数の取得に失敗しました: {e2}")
117
+ num_pages = 1 # デフォルトで1ページとする
118
+
119
+ logger.info(f"総ページ数: {num_pages}")
120
+
121
+ # 各ページにアクセスして画像を取得
122
+ for i in range(1, num_pages + 1):
123
+ page_url = f"{url}/{i}"
124
+ page.goto(page_url)
125
+ logger.info(f"ページにアクセスしました: {page_url}")
126
+
127
+ # ページが完全に読み込まれるまで待機
128
+ page.wait_for_load_state('networkidle')
129
+ logger.info(f"ページ {i} の読み込みが完了しました。")
130
+
131
+ try:
132
+ # 画像を取得するセレクタ
133
+ img_selector = '#image-container > a > img'
134
+ img_elements = page.locator(img_selector)
135
+ img_count = img_elements.count()
136
+ logger.info(f"ページ {i} の画像数: {img_count}")
137
+
138
+ if img_count == 0:
139
+ logger.warning(f"ページ {i} に画像が見つかりません。")
140
+ continue
141
+
142
+ for j in range(img_count):
143
+ try:
144
+ image_element = img_elements.nth(j)
145
+ image_url = image_element.get_attribute('src')
146
+ if not image_url:
147
+ # data-srcなどに画像URLが格納されている場合
148
+ image_url = image_element.get_attribute('data-src')
149
+ logger.info(f"取得した画像URL (ページ {i}, 画像 {j + 1}): {image_url}")
150
+
151
+ if image_url:
152
+ # ファイル名にページ番号と画像番号を含め、位取りを適用
153
+ image_name = f'page_{str(i).zfill(5)}_img_{str(j + 1).zfill(5)}.jpg'
154
+ save_image_as_jpg(image_url, folder_path, image_name)
155
+ except Exception as e:
156
+ logger.error(f"ページ {i}, 画像 {j + 1} の処理中にエラーが発生しました: {e}")
157
+ continue
158
+ except Exception as e:
159
+ logger.error(f"ページ {i} の画像取得中にエラーが発生しました: {e}")
160
+ continue
161
+
162
+ browser.close()
163
+ logger.info("ブラウザを閉じました。")
164
+
165
+ if __name__ == "__main__":
166
+ if len(sys.argv) < 2:
167
+ logger.error("使用方法: python scrape_images_worker.py <URL>")
168
+ sys.exit(1)
169
+
170
+ url = sys.argv[1] # コマンドライン引数でURLを受け取る
171
+ folder_name = 'scraped_images' # デフォルトのフォルダ名
172
+ scrape_images_by_page(url, folder_name)