|
import os
|
|
import re
|
|
from playwright.sync_api import sync_playwright
|
|
import requests
|
|
import sys
|
|
from PIL import Image, UnidentifiedImageError
|
|
from io import BytesIO
|
|
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,
|
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
handlers=[
|
|
logging.StreamHandler(sys.stdout),
|
|
file_handler,
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def generate_safe_folder_name(url):
|
|
|
|
safe_name = re.sub(r'[^a-zA-Z0-9_\-]', '_', url)
|
|
return safe_name
|
|
|
|
|
|
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()
|
|
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
|
|
|
|
|
|
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'):
|
|
|
|
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("ページの読み込みが完了しました。")
|
|
|
|
|
|
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
|
|
|
|
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:
|
|
|
|
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]
|
|
folder_name = 'scraped_images'
|
|
scrape_images_by_page(url, folder_name)
|
|
|