gallary2 / scrape_images_worker.py
OzoneAsai's picture
Update scrape_images_worker.py
d6926f4 verified
raw
history blame
7.92 kB
import os
import re
from playwright.sync_api import sync_playwright
import requests
import sys
from PIL import Image, UnidentifiedImageError
from io import BytesIO
os.systems("python3 -m playwright install")
log_file = "app_log.txt" # ログファイルのパス
# ログフォーマットの定義
log_format = '%(asctime)s - %(levelname)s - %(message)s'
import logging
file_handler = logging.FileHandler(log_file, encoding='utf-8')
# ログの設定
logging.basicConfig(
level=logging.INFO, # ログレベルをINFOに設定
format='%(asctime)s - %(levelname)s - %(message)s', # ログのフォーマットを指定
handlers=[
logging.StreamHandler(sys.stdout), # 標準出力にログを出力
file_handler,
]
)
logger = logging.getLogger(__name__)
# 安全なフォルダ名を生成する関数
def generate_safe_folder_name(url):
# URLから安全なフォルダ名を生成(ファイル名に使えない文字を除去)
safe_name = re.sub(r'[^a-zA-Z0-9_\-]', '_', url)
return safe_name
# 画像を保存する関数 (JPG 80%の品質で保存)
def save_image_as_jpg(image_url, save_folder, image_name):
if not os.path.exists(save_folder):
os.makedirs(save_folder)
logger.info(f"フォルダを作成しました: {save_folder}")
try:
response = requests.get(image_url, timeout=10)
response.raise_for_status() # HTTPエラーが発生した場合例外を投げる
except requests.exceptions.RequestException as e:
logger.error(f"画像のダウンロード中にエラーが発生しました: {e}")
return
try:
image = Image.open(BytesIO(response.content))
except UnidentifiedImageError:
logger.warning(f"未識別の画像ファイル: {image_url}. スキップします。")
return
except Exception as e:
logger.error(f"画像のオープン中にエラーが発生しました: {e}")
return
# 保存時に JPG に変換し、品質80%で保存
image_path = os.path.join(save_folder, image_name)
try:
image.convert("RGB").save(image_path, "JPEG", quality=80)
logger.info(f"画像を保存しました: {image_path}")
except Exception as e:
logger.error(f"画像の保存中にエラーが発生しました: {e}")
# 画像の再帰的取得
def scrape_images_by_page(url, folder_name='scraped_images'):
# URLが"/"で終わっている場合、スラッシュを削除
original_url = url
url = url.rstrip('/')
logger.info(f"処理するURL: {url}")
with sync_playwright() as p:
browser = p.chromium.launch(headless=False) # ブラウザを表示して操作
page = browser.new_page()
# 初期ページにアクセス
page.goto(url)
logger.info(f"ページにアクセスしました: {url}")
# ページが完全に読み込まれるまで待機
page.wait_for_load_state('networkidle')
logger.info("ページの読み込みが完了しました。")
# lazy-loading属性を無効にするためのJavaScriptを挿入
try:
page.evaluate("""
document.querySelectorAll('img[loading="lazy"]').forEach(img => {
img.setAttribute('loading', 'eager');
img.src = img.src; // 画像を強制的にリロード
});
""")
logger.info("lazy-loadingを無効化しました。")
except Exception as eval_error:
logger.warning(f"JavaScriptの評価中にエラーが発生しました: {eval_error}")
# フォルダ名を生成
safe_folder_name = generate_safe_folder_name(url)
folder_path = os.path.join(folder_name, safe_folder_name)
logger.info(f"保存先フォルダ: {folder_path}")
# ページ数を取得
try:
# ページ数が格納されているセレクタからテキストを取得
page_count_selector = 'div.tag-container:nth-child(8) > span:nth-child(1) > a:nth-child(1) > span:nth-child(1)'
page_count_text = page.locator(page_count_selector).text_content().strip()
num_pages = int(re.search(r'\d+', page_count_text).group())
logger.info(f"セレクタ '{page_count_selector}' からページ数を取得: {num_pages}")
except Exception as e:
logger.warning(f"セレクタ '{page_count_selector}' からページ数を取得できませんでした: {e}")
# セレクタが見つからない場合のフォールバック
try:
fallback_selector = 'section.reader-bar:nth-child(2) > div:nth-child(2) > button:nth-child(3) > span:nth-child(3)'
page.wait_for_selector(fallback_selector, timeout=5000)
num_pages_text = page.locator(fallback_selector).text_content().strip()
num_pages = int(re.search(r'\d+', num_pages_text).group())
logger.info(f"セレクタ '{fallback_selector}' からページ数を取得: {num_pages}")
except Exception as e2:
logger.error(f"ページ数の取得に失敗しました: {e2}")
num_pages = 1 # デフォルトで1ページとする
logger.info(f"総ページ数: {num_pages}")
# 各ページにアクセスして画像を取得
for i in range(1, num_pages + 1):
page_url = f"{url}/{i}"
page.goto(page_url)
logger.info(f"ページにアクセスしました: {page_url}")
# ページが完全に読み込まれるまで待機
page.wait_for_load_state('networkidle')
logger.info(f"ページ {i} の読み込みが完了しました。")
try:
# 画像を取得するセレクタ
img_selector = '#image-container > a > img'
img_elements = page.locator(img_selector)
img_count = img_elements.count()
logger.info(f"ページ {i} の画像数: {img_count}")
if img_count == 0:
logger.warning(f"ページ {i} に画像が見つかりません。")
continue
for j in range(img_count):
try:
image_element = img_elements.nth(j)
image_url = image_element.get_attribute('src')
if not image_url:
# data-srcなどに画像URLが格納されている場合
image_url = image_element.get_attribute('data-src')
logger.info(f"取得した画像URL (ページ {i}, 画像 {j + 1}): {image_url}")
if image_url:
# ファイル名にページ番号と画像番号を含め、位取りを適用
image_name = f'page_{str(i).zfill(5)}_img_{str(j + 1).zfill(5)}.jpg'
save_image_as_jpg(image_url, folder_path, image_name)
except Exception as e:
logger.error(f"ページ {i}, 画像 {j + 1} の処理中にエラーが発生しました: {e}")
continue
except Exception as e:
logger.error(f"ページ {i} の画像取得中にエラーが発生しました: {e}")
continue
browser.close()
logger.info("ブラウザを閉じました。")
if __name__ == "__main__":
if len(sys.argv) < 2:
logger.error("使用方法: python scrape_images_worker.py <URL>")
sys.exit(1)
url = sys.argv[1] # コマンドライン引数でURLを受け取る
folder_name = 'scraped_images' # デフォルトのフォルダ名
scrape_images_by_page(url, folder_name)