#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
スクレイピングユーティリティ
* サイトURLと、selectorsを指定して、スクレイピングする
Todo:
- docstringを整える
"""
# standard library
# 3rd party packages
import sys
import copy
from dataclasses import dataclass
import pickle
import pyperclip # クリップボード
import json
import inspect
# local source
from helper import chromeDriver
# 最大再起回数を1万回にする
sys.setrecursionlimit(10000)
[ドキュメント]
@dataclass(frozen=True)
class ScrapingValue:
"""値オブジェクト"""
site_url: str = None
selectors: dict = None
def __init__(self, site_url, selectors):
"""完全コンストラクタパターン
:param site_url: list 処理対象サイトURL
:param selectors: list スクレイピングする際のXPATH
"""
if not site_url:
raise ValueError(f"{self.__class__.__name__}.{inspect.stack()[1].function}"
f"引数エラー:site_url=None")
if not selectors:
raise ValueError(f"{self.__class__.__name__}.{inspect.stack()[1].function}"
f"引数エラー:selectors=None")
if not isinstance(site_url, str):
raise ValueError(f"{self.__class__.__name__}.{inspect.stack()[1].function}"
f"引数エラー:site_urlがstrではない")
if not isinstance(selectors, dict):
raise ValueError(f"{self.__class__.__name__}.{inspect.stack()[1].function}"
f"引数エラー:selectorsがdictではない")
object.__setattr__(self, "site_url", site_url)
object.__setattr__(self, "selectors", selectors)
[ドキュメント]
class Scraping:
"""スクレイピングのユーティリティ"""
value_object: ScrapingValue = None
def __init__(self, value_object=None, selectors=None):
if value_object:
if isinstance(value_object, ScrapingValue):
self.value_object = value_object
elif isinstance(value_object, str):
if selectors:
self.value_object = ScrapingValue(value_object, selectors)
else:
raise ValueError(f"{self.__class__.__name__}.{inspect.stack()[1].function}"
f"引数エラー:selectors=None")
else:
raise ValueError(f"{self.__class__.__name__}.{inspect.stack()[1].function}"
f"引数エラー:selectorsがstrではない")
else:
raise ValueError(f"{self.__class__.__name__}.{inspect.stack()[1].function}"
f"引数エラー:value_object=None")
[ドキュメント]
def get_value_object(self):
"""値オブジェクトを取得する"""
return copy.deepcopy(self.value_object)
[ドキュメント]
def scraping_chrome_driver(self):
""" TODO: ChromeDriverから、dictでスクレイピング結果を受け取りたい
:return:
"""
__value_object = self.get_value_object()
__chrome_driver = chromeDriver.ChromeDriver(__value_object.site_url, __value_object.selectors)
return {'title': __chrome_driver.get_title(), 'last_image_url': __chrome_driver.get_last_image_url()}
[ドキュメント]
def create_save_text(self):
"""保存用文字列の作成
:return: str 保存用文字列の作成
"""
buff = json.dumps(self.value_object.site_url, ensure_ascii=False) + '\n' # サイトURL追加
buff += json.dumps(self.value_object.selectors, ensure_ascii=False) + '\n' # セレクタ追加
return buff
[ドキュメント]
def clip_copy(self):
"""クローリング結果をクリップボードにコピーする
:return: bool 成功/失敗=True/False
"""
if not self.value_object:
return False
buff = self.create_save_text()
pyperclip.copy(buff) # クリップボードへのコピー
return True
[ドキュメント]
def save_text(self, save_path):
"""データをファイルに、以下の独自フォーマットで保存する
* サイトURL
* セレクタ
:param save_path: str セーブする独自フォーマットなファイルのパス
:return: bool 成功/失敗=True/False
"""
if not self.value_object:
return False
with open(save_path, 'w', encoding='utf-8') as work_file:
buff = self.create_save_text()
work_file.write(buff)
return True
[ドキュメント]
def load_text(self, load_path):
"""独自フォーマットなファイルからデータを読み込む
:param load_path: str ロードする独自フォーマットなファイルのパス
:return: bool 成功/失敗=True/False
"""
with open(load_path, 'r', encoding='utf-8') as work_file:
buff = work_file.readlines()
__site_url = json.loads(buff[0].rstrip('\n'))
del buff[0]
__selectors = json.loads(buff[0].rstrip('\n'))
del buff[0]
self.value_object = ScrapingValue(__site_url, __selectors,)
return True
[ドキュメント]
def save_pickle(self, save_path):
"""シリアライズしてpickleファイルに保存する
:param save_path: str セーブするpickleファイルのパス
:return: bool 成功/失敗=True/False
"""
if not save_path:
return False
with open(save_path, 'wb') as work_file:
pickle.dump(self.value_object, work_file)
return True
[ドキュメント]
def load_pickle(self, load_path):
"""pickleファイルを読み込み、デシリアライズする
:param load_path: str ロードするpickleファイルのパス
:return: bool 成功/失敗=True/False
"""
if not load_path:
return False
with open(load_path, 'rb') as work_file:
self.value_object = pickle.load(work_file)
return True