Пример #1
0
 def __init__(self,
              queue_log: Queue[Any],
              ctx: Dict[str, Any],
              limit: int = 0):
     worker_configurer(queue_log, logger)
     super(MemoryObserverThread, self).__init__()
     self.ctx = ctx
     self.limit = limit
Пример #2
0
    def __init__(self, queue_log: Queue[Any], options: FirefoxOptions,
                 ffprofile: FirefoxProfile, capabilities: Dict[str, Any]):
        super(GetFirefoxDriverThread, self).__init__()
        worker_configurer(queue_log, logger)

        self.options = options
        self.fpro = ffprofile
        self.capabilities = capabilities
        self.driver = False
        self.re = False
Пример #3
0
def del_and_make_achievement(path: str, queue_log: Queue[Any]):
    """
    クローリング完了後に実行
    result/result_*に分けて保存されているデータをまとめて、result/achievement/に保存
    不要なtempファイルを削除する

    args:
        path: org_path /result_history/*/
    """
    worker_configurer(queue_log, logger)
    # ファイル位置を絶対パスで取得
    now_dir = os.path.dirname(os.path.abspath(__file__))
    del_temp_file(path, 0, 0)
    del_dir(path, 0, 0)
    logger.debug('making achievement...')
    make_achievement(path)
    logger.debug('making achievement...FIN!')
    logger.debug('merging server dir...')
    merge_server_dir(path)
    logger.debug('merging server dir...FIN!')
    cal_num_of_achievement(path)

    # 実行ディレクトリに戻る
    os.chdir(now_dir)
Пример #4
0
if __name__ == "__main__":
    """
    save_result を実行すると終了処理を全部やってくれる
    なぜか最後の処理だけされないことが多発したため作った
    """

    organization = "ritsumeikan"

    queue_log: Queue[Any] = Queue(-1)
    now_dir =  os.path.dirname(os.path.abspath(__file__))
    os.chdir(now_dir)
    log_listener = Process(target=log_listener_process,
                    args=(now_dir, queue_log, log_listener_configure))
    log_listener.start()
    logger = getLogger(__name__)
    worker_configurer(queue_log, logger)

    organization_path = organization_path = now_dir[0:now_dir.rfind('/')] + '/organization/' + organization
    print(organization_path)
    # result_historyディレクトリがなければ作成
    if not os.path.exists(organization_path + '/result_history'):
        os.mkdir(organization_path + '/result_history')
    dir_name = str(len(os.listdir(organization_path + '/result_history')) + 1)

    org_arg = {'result_no': dir_name, 'org_path': organization_path}

    logger.info('save used ROD before overwriting the ROD directory : START')
    save_rod(org_arg)
    logger.info('save used ROD before overwriting the ROD directory : DONE')

    logger.info('---dealing after fact---')
Пример #5
0
def summarize_alert_main(queue_log: Queue[Any], recv_q: Queue[Union[Alert,
                                                                    str]],
                         send_q: Queue[str], nth: int, org_path: str):
    """
    Alertが出た場合に記録するためのプロセス
    スレッドとの通信キューに何もなければ一旦スリープすることでCPU負荷を下げる

    Args:
    - recv_q: 各プロセスからのアラートデータを受け取るためのキュー, endがきたら終了
    - send_q: 
    - nth: 
    - org_path: organizationのパス
    """
    worker_configurer(queue_log, logger)
    alert_dir_path = org_path + '/alert'
    # alertディレクトリを作成
    if not path.exists(alert_dir_path):
        mkdir(alert_dir_path)

    # アラート受信スレッドとの通信用キュー
    data_list: threadQueue[Any] = threadQueue()

    t = Thread(target=receive_alert, args=(recv_q, data_list))
    t.start()

    while True:
        if not data_list.empty():
            try:
                temp = data_list.get()
            except Empty:
                logger.info('data_list is empty....')
                continue
            else:
                if temp == 'end':
                    logger.info('receive end!!')
                    t.join()
                    break

                alert = cast(Alert, temp)

                file_name = alert.file_name
                content = alert.content
                label = alert.label

                # label と content にnthを追加
                label = 'Nth,' + label
                content = str(nth) + ', ' + content

                # "falsification.cysec.cs.ritsumei.ac.jp"がURLに含まれる場合、ファイル名を変更する
                if ("falsification.cysec.cs.ritsumei.ac.jp"
                        in alert.url) or ("192.168.0.233" in alert.url):
                    file_name = "TEST_" + file_name

                # label と content を出力
                if file_name.endswith('.csv'):
                    if not path.exists(alert_dir_path + '/' + file_name):
                        w_file(alert_dir_path + '/' + file_name,
                               label + '\n',
                               mode="a")
                    w_file(alert_dir_path + '/' + file_name,
                           content + '\n',
                           mode="a")
                else:
                    w_file(alert_dir_path + '/' + file_name,
                           content + '\n',
                           mode="a")
            finally:
                data_list.task_done()
        else:
            sleep(0.1)

    data_list.join()
    logger.info("Summarize_alert: FIN")

    send_q.put('end')  # 親にendを知らせる
Пример #6
0
def configure_logger_for_use_extentions(queue_log: Queue[Any]):
    """
    Loggerをセット
    logger の使い方が正しくないがゆえにここでセットしないといけない
    """
    worker_configurer(queue_log, logger)
Пример #7
0
def main(organization: str):
    # 以下のwhileループ内で
    # このファイル位置のパスを取ってきてchdirする
    # 実行ディレクトリはこのファイル位置じゃないとバグるかも(ほぼ全て相対パスだから)
    now_dir = os.path.dirname(os.path.abspath(__file__))  # ファイル位置(src)を絶対パスで取得
    os.chdir(now_dir)

    # Logger の作成
    log_listener = Process(target=log_listener_process,
                    args=(now_dir, queue_log, log_listener_configure))
    log_listener.start()
    worker_configurer(queue_log, logger)
    worker_configurer_sys_command(queue_log)

    # 引数として与えられた組織名のディレクトリが存在するか
    organization_path = now_dir[0:now_dir.rfind('/')] + '/organization/' + organization
    if not os.path.exists(organization_path):
        logger.warning('You should check existing %s directory in ../organization/', organization)
        queue_log.put_nowait(None)
        log_listener.join()
        return 0

    # 既に実行中ではないか
    if os.path.exists(organization_path + '/running.tmp'):
        logger.warning("%s's site is crawled now.", organization)
        queue_log.put_nowait(None)
        log_listener.join()
        return 0
    else:
        # 実行途中ではない場合、ファイルを作って実行中であることを示す
        f = open(organization_path + '/running.tmp', 'w', encoding='utf-8')
        start_time = datetime.now().strftime('%Y/%m/%d, %H:%M:%S')
        f.write(start_time)
        f.close()

    # result_historyディレクトリがなければ作成
    if not os.path.exists(organization_path + '/result_history'):
        os.mkdir(organization_path + '/result_history')
    dir_name = str(len(os.listdir(organization_path + '/result_history')) + 1)

    org_arg = {'result_no': dir_name, 'org_path': organization_path}

    while True:
        # クローラを実行
        logger.info("""
    --- %s : %s th crawling---""", organization, org_arg['result_no'])
        p = Process(target=crawler_host, args=(queue_log, org_arg))
        p.start()
        p.join()
        exitcode: Optional[int] = p.exitcode
        if (exitcode == 255) or (exitcode < 0):
            # エラー落ちの場合?
            logger.error('operate_main ended by crawler error')
            break
        logger.info('crawling has finished.')

        # 孤児のchrome じゃなくてfirefoxをkill
        kill_chrome(process='geckodriver')
        kill_chrome(process='firefox-bin')

        logger.info('save used ROD before overwriting the ROD directory : START')
        save_rod(org_arg)
        logger.info('save used ROD before overwriting the ROD directory : DONE')

        logger.info('---dealing after fact---')
        dealing_after_fact(queue_log, org_arg)

        now = datetime.now()
        logger.info(f"""
    --- {organization} : {org_arg['result_no']} th crawling DONE ---
    {now}""")
        org_arg['result_no'] = str(int(org_arg['result_no']) + 1)
        logger.info(f"{organization} : {org_arg['result_no']} th crawling will start at 20:00")

        # 実行ディレクトリ移動
        os.chdir(now_dir)
        break

    # 実行中であることを示すファイルを削除する
    if os.path.exists(organization_path + '/running.tmp'):
        os.remove(organization_path + '/running.tmp')

    # Logを集めるリスナーを終了させる
    queue_log.put_nowait(None)
    log_listener.join()
Пример #8
0
def dealing_after_fact(queue_log: Queue[Any], org_arg: Dict[str, str]):
    worker_configurer(queue_log, logger)

    dir_name = org_arg['result_no']
    org_path = org_arg['org_path']

    # 偽サイトの情報を削除
    if '/organization/ritsumeikan' in org_path:
        del_falsification_RAD(org_path=org_path)

    # コピー先を削除
    shutil.rmtree(org_path + '/ROD/url_hash_json')
    shutil.rmtree(org_path + '/ROD/tag_data')

    # 移動
    logger.info('copy file to ROD from RAD : START')
    shutil.copytree(
        org_path + '/RAD/df_dict', org_path + '/ROD/df_dicts/' +
        str(len(os.listdir(org_path + '/ROD/df_dicts/')) + 1))
    shutil.move(org_path + '/RAD/url_hash_json',
                org_path + '/ROD/url_hash_json')
    shutil.move(org_path + '/RAD/tag_data', org_path + '/ROD/tag_data')
    shutil.move(org_path + '/RAD/url_db', org_path + '/ROD/url_db')
    """
    if os.path.exists('RAD/screenshots'):
        # スクショを撮っていたら、0サイズの画像を削除
        p = Process(target=del_0size.del_0size_and_rename, args=('RAD/screenshots',))
        p.start()
        p.join()
        if os.path.exists('ROD/screenshots'):
            shutil.rmtree('ROD/screenshots')
        shutil.move('RAD/screenshots', 'ROD/screenshots')
    """
    logger.info('copy file to ROD from RAD : DONE')

    # make_filter_from_past_data.pyの実行 (tfidfの計算には少し時間(1分くらい)がかかるのでプロセスを分けて
    logger.info('Run function of tf_idf.py : START')
    p = Process(target=make_idf_dict_frequent_word_dict,
                args=(
                    queue_log,
                    org_path,
                ))  # type: ignore
    p.start()

    # 立命館サイトは make_host_set()を自動で実行。
    # 今回の収集データと過去のデータをマージする作業なので、自動実行すると、今回検出された外部URLが安全なURLとして登録されてしまうため
    # ちゃんと人が判断してから追加したほうがいい。立命館サイトは面倒なので自動でやっちゃう。
    if '/organization/ritsumeikan' in org_path:
        p2 = Process(target=make_request_url_iframeSrc_link_host_set,
                     args=(
                         queue_log,
                         org_path,
                     ))  # type: ignore
        p2.start()
    else:
        p2 = None

    p.join()
    if p2 is not None:
        p2.join()
    logger.info('Run function of tf_idf.py : DONE')

    # alertされたデータから新しいフィルタを作る (linkとrequest URL用
    logger.info('Making Filter             : START')
    make_filter(org_path=org_path)
    if '/organization/ritsumeikan' in org_path:  # 立命館サイトは merge_filter()を自動で実行。
        merge_filter(org_path=org_path)
    logger.info('Making Filter             : DONE')

    # RADの削除
    logger.info('delete RAD                : START')
    shutil.rmtree(org_path + '/RAD')
    logger.info('delete RAD                : DONE')

    # resultの移動
    logger.info('mv result to check_result : START')
    result_history_path = org_path + '/result_history/' + dir_name
    shutil.move(src=org_path + '/result', dst=result_history_path)
    logger.info('mv result to check_result : DONE')

    # crawler_deinit.pyの実行
    del_and_make_achievement(result_history_path, queue_log)
Пример #9
0
def clamd_main(queue_log: Queue[Any], recvq: Queue[str], sendq: Queue[Union[str, bool]], org_path: str):
    worker_configurer(queue_log, logger)
    # clamAVのデーモンが動いているか確認
    while True:
        try:
            logger.info('Clamd Process connect...')
            cd = pyclamd.ClamdAgnostic()
            pin = cd.ping()
            logger.info('Clamd Process connected!!!')
            break
        except ValueError:
            logger.info('Clamd Process waiting for clamd start....')
            sleep(3)
        except Exception as err:
            pin = False
            logger.exception(f'Exception has occur: {err}')
            break
    sendq.put(pin)   # 親プロセスにclamdに接続できたかどうかの結果を送る
    if pin is False:
        os._exit(0) # type: ignore # 接続できなければ終わる

    # EICARテスト
    eicar = cd.EICAR() # type: ignore
    cd.scan_stream(eicar) # type: ignore

    t = Thread(target=receive, args=(recvq,))    # クローリングプロセスからのデータを受信するスレッド
    t.setDaemon(True)
    t.start()
    while True:
        if not data_list:
            if end:
                break
            # クローリングプロセスからデータが送られていなければ、3秒待機
            sleep(3)
            continue
        temp = data_list.popleft()
        url = temp[0]
        url_src = temp[1]
        byte = temp[2]
        # clamdでスキャン
        if len(byte) < 25 * 1000000: # Max filesize 25MB
            try:
                result = cd.scan_stream(byte) # type: ignore
            except Exception as err:
                logger.exception(f'Exception has occur, URL={url}, {err}')
                clamd_error.append(url + '\n' + str(err))
            else:
                # 検知されると結果を記録
                if result is not None:
                    w_file(org_path + '/alert/warning_clamd.txt', "{}\n\tURL={}\n\tsrc={}\n".format(result, url, url_src), mode="a")
                    if not os.path.exists(org_path + '/clamd_files'):
                        os.mkdir(org_path + '/clamd_files')
                    w_file(org_path + '/clamd_files/b_' + str(len(listdir(org_path + '/clamd_files'))+1) + '.clam',
                        url + '\n' + str(byte), mode="a")
                logger.info('clamd have scanned: %s', url)
        else:
            logger.info("big file... save to log file")
            clamd_error.append(url + '\n' + "Over 25.0MB file")

        # エラーログが一定数を超えると外部ファイルに書き出す
        if len(clamd_error) > 100:
            text = ''
            for i in clamd_error:
                text += i + '\n'
            w_file('error_clamd.txt', text, mode="a")
            clamd_error.clear()

    text = ''
    for i in clamd_error:
        text += i + '\n'
    if clamd_error:
        w_file('error_clamd.txt', text, mode="a")
    clamd_error.clear()

    logger.debug("Clamd end")
    sendq.put('end')   # 親にendを知らせる
Пример #10
0
def get_fox_driver(queue_log: Queue[Any],
                   screenshots: bool = False,
                   user_agent: str = '',
                   org_path: str = '') -> Union[bool, Dict[str, Any]]:
    """
    Firefoxを使うためのdriverをヘッドレスモードで起動
    ファイルダウンロード可能
    RequestURLの取得可能(アドオンを用いて)
    ログコンソールの取得不可能(アドオンの結果は</body>と</html>の間にはさむことで、取得する)
    """
    worker_configurer(queue_log, logger)

    logger.debug("Setting FireFox driver...")
    # headless FireFoxの設定

    options: FirefoxOptions = make_firefox_options()
    profile: FirefoxProfile = make_firefox_profile(org_path, user_agent)
    caps: Dict[str, Any] = make_firefox_caps()

    # Firefoxのドライバを取得。ここでフリーズしていることがあったため、スレッド化した
    # Todo: メモリが足りなかったらドライバーの取得でフリーズする
    try:
        t = GetFirefoxDriverThread(queue_log=queue_log,
                                   options=options,
                                   ffprofile=profile,
                                   capabilities=caps)
        t.start()
        t.join(10.0)
    except Exception as err:
        # runtime error とか
        logger.info(f'Faild to get Firefox Driver Thread, retrying: {err}')
        sleep(10.0)
        pass

    if t.re == False:
        # 1度だけドライバ取得をリトライする
        logger.info('retry to get webdriver')
        if type(t.driver) == WebDriver:
            logger.info('quit past driver')
            driver = cast(WebDriver, t.driver)
            quit_driver(driver)  # 一応終了
        try:
            t = GetFirefoxDriverThread(queue_log=queue_log,
                                       options=options,
                                       ffprofile=profile,
                                       capabilities=caps)
            t.start()
            t.join(10.0)
        except Exception as err:
            # runtime error とか
            logger.debug(
                f'Faild to get Firefox Driver Thread again, Failed: {err}')
            t.re = False
            pass

    if t.re == False:
        # ドライバ取得でフリーズする等のエラー処理
        logger.info("Failed to getting driver: thread freezed")
        if type(t.driver) == WebDriver:
            driver = cast(WebDriver, t.driver)
            quit_driver(driver)  # 一応終了させて
        return False
    if t.driver is False:
        # 単にエラーで取得できなかった場合
        logger.info("Failed to getting driver: thread couse error")
        return False

    if type(t.driver) == WebDriver:
        driver = cast(WebDriver, t.driver)
        driver.set_window_size(1280, 1024)
    else:
        return False

    # 拡張機能のwindowIDを取得し、それ以外のwindowを閉じる
    # geckodriver 0.21.0 から HTTP/1.1 になった? Keep-Aliveの設定が5秒のせいで、5秒間driverにコマンドがいかなかったらPipeが壊れる.
    # 0.20.1 にダウングレードするか、seleniumを最新にアップグレードすると、 Broken Pipe エラーは出なくなる。
    wait = WebDriverWait(driver, 5)
    watcher_window = get_watcher_window(driver, wait)
    if watcher_window is False:
        logger.warning("Fail to get watcher window, return fail")
        quit_driver(driver)
        return False
    watcher_window = cast(Union[str, int], watcher_window)

    logger.debug("Setting FireFox driver... FIN!")
    return {"driver": driver, "wait": wait, "watcher_window": watcher_window}
Пример #11
0
def worker_configurer_sys_command(queue_log: Queue[Any]):
    """
    Loggerをセット
    """
    worker_configurer(queue_log, logger)
Пример #12
0
def make_idf_dict_frequent_word_dict(queue_log: Queue[Any],org_path: str):
    """
    クローリング後にサーバごとの
    tf-idf値
    ページのロードに行ったリクエストのURL集合
    iframeのsrc先URLの集合
    をまとめて保存する
    また、リンク、リクエストURLの既知サーバを示すホワイトリストのフィルタを作成する
    """
    worker_configurer(queue_log, logger)
    df_dict: Dict[str, Dict[str, Any]] = dict()   # {file名(server.json) : {単語:df値, 単語:df値, ...}, server : {df辞書}, ... , server : {df辞書} }
    if not os.path.exists(org_path + '/ROD/idf_dict'):
        os.mkdir(org_path + '/ROD/idf_dict')

    n = 100   # 頻出単語上位n個を保存
    if not os.path.exists(org_path + '/ROD/frequent_word_' + str(n)):
        os.mkdir(org_path + '/ROD/frequent_word_' + str(n))

    dir_list = os.listdir(org_path + '/ROD/df_dicts/')  # このフォルダ以下の全てのdfフォルダをマージしてidf辞書を作る
    for df_dict_dir in dir_list:
        pickle_file_list = os.listdir(org_path + '/ROD/df_dicts/' + df_dict_dir)
        for pickle_file in pickle_file_list:
            # 途中からpickleファイルに変更した
            with open(org_path + '/ROD/df_dicts/' + df_dict_dir + '/' + pickle_file, 'rb') as f:
                dic = pickle.load(f)   # df辞書ロード

            if len(dic) == 0:  # 中身がなければ次へ
                continue
            # df_dictはpickleファイルだが、頻出単語ファイルはjsonにするため
            file_name_json = pickle_file[0:pickle_file.find('.pickle')] + '.json'
            if file_name_json not in df_dict:
                df_dict[file_name_json] = dict()
            df_dict[file_name_json] = dict(Counter(df_dict[file_name_json]) + Counter(dic))   # 既存の辞書と同じキーの要素を足す
    # この時点で、df辞書のマージ終わり(同じ単語の出現文書数を足し合わせた)

    # 頻出単語辞書の作成
    for file_name, dic in df_dict.items():
        frequent_words: list[str] = list()
        for word, _ in sorted(dic.items(), key=lambda x: x[1], reverse=True):
            if word != 'NumOfPages':
                frequent_words.append(word)
            if len(frequent_words) == n:
                break
        with open(org_path + '/ROD/frequent_word_' + str(n) + '/' + file_name, 'w') as f:
            json.dump(frequent_words, f)

    # idf辞書の作成
    # 前回データからのみ
    pickle_files = os.listdir(org_path + '/RAD/df_dict')
    c = 0
    for pickle_file in pickle_files:

        idf_dict: Dict[str, float] = dict()
        with open(org_path + '/RAD/df_dict/' + pickle_file, 'rb') as f:
            dic = pickle.load(f)
        if len(dic) == 0:
            continue
        N = dic['NumOfPages']  # NumOfPageはそのサーバで辞書を更新した回数 = そのサーバのページ数
        if N == 0:
            continue
        for word, count in dic.items():
            if not word == 'NumOfPages':
                idf = log(N / count) + 1
                idf_dict[word] = idf
        # 前回に出現していなかった単語のtf-idfを計算するためのidf値
        idf = log(N) + 1  # 初登場の単語は、countを1でidf値を計算
        idf_dict['NULLnullNULL'] = idf

        if idf_dict:
            # df_dictはpickleファイルだが、idfファイルはjsonにするため
            file_name_json = pickle_file[0:pickle_file.find('.pickle')] + '.json'
            with open(org_path + '/ROD/idf_dict/' + file_name_json, 'w') as f:
                json.dump(idf_dict, f)
        c += 1
Пример #13
0
def make_request_url_iframeSrc_link_host_set(queue_log: Queue[Any], org_path: str):
    """
    1. lis = RAD/tempの中のpickleファイルを順番に開いていく(ファイルの中身は辞書)
    2. url_set = 過去のデータ(ROD/[object]_url/ホスト名.json)のファイルを開く
    3. 辞書から特定のkey(object_list)のデータを取得
    4. それぞれのデータ(pick[obj])を過去(ROD)のデータ(url_set)とマージする
    5. マージしたデータ(url_set)を ROD/[object]_url/ホスト名.json に保存
    """
    worker_configurer(queue_log, logger)
    object_list = ['request', 'iframe', 'link', 'script']

    # RODに保存dirがなければ作る
    for obj in object_list:
        if not os.path.exists(org_path + '/ROD/' + obj + '_url'):
            os.mkdir(org_path + '/ROD/' + obj + '_url')

    # 今回のクローリングで集めたデータのpickleファイルのリストを取得
    lis = os.listdir(org_path + '/RAD/temp')
    global request_url, iframe_url, link_url, script_url   # global変数じゃないとexec関数で未定義のエラーが出る(たしか
    file_name_set = set()  # .pickleを.jsonに名前変更した集合

    for file in lis:
        # 1.
        with open(org_path + '/RAD/temp/' + file, 'rb') as f:
            pick = pickle.load(f)
        file = file[file.find('_') + 1:file.find('.')] + '.json'
        file_name_set.add(file)

        # それぞれのデータを過去のデータとマージしてjsonで保存
        for obj in object_list:
            # 2.
            if os.path.exists(org_path + '/ROD/' + obj + '_url/' + file):
                with open(org_path + '/ROD/' + obj + '_url/' + file, 'r') as f:
                    url_set = set(json.load(f))
            else:
                url_set = set()
            # 3. 4.
            if obj in pick and pick[obj]:
                url_set.update(pick[obj])
            # 5.
            if url_set:
                exec(obj + "_url.update(url_set)")
                with open(org_path + '/ROD/' + obj + '_url/' + file, 'w') as f:
                    json.dump(list(url_set), f)

    # 今回見つからなかったが、過去に回ったことのあるサーバのRODデータをmatome.jsonに入れるためにそれぞれの集合に追加する
    # 上記の処理では、tempディレクトリを参考にしているため、今回のクローリングで見つからなかったサーバのデータは
    # それぞれの集合(request_urlとiframe_urlとlink_url)に入っていないため、過去の情報を追加する
    if '/organization/ritsumeikan' in org_path:
        file_name_set.add('falsification-cysec-cs-ritsumei-ac-jp.json')  # 偽サイト情報はmatome.jsonに入れない

    # ROD/request, iframe, script, linkの各JSONデータをmatome.jsonとしてまとめる
    for obj in object_list:
        file_names = os.listdir(org_path + '/ROD/' + obj + '_url')
        for file_name in file_names:
            # matome.jsonは参照しない(RODには前回使ったmatome.jsonがあるのでそれは無視)
            if 'matome.json' in file_name:
                continue
            # matome.json以外で、過去に見つけていたが今回見つからなかったサーバの情報を追加
            # TODO: 未完成のところ
            if file_name not in file_name_set:
                with open(org_path + '/ROD/' + obj + '_url/' + file_name, 'r') as f:
                    tmp = json.load(f) # type: ignore
                    try:
                        exec(obj + "_url.update(set(tmp))")
                    except Exception as err:
                        logger.exception(f'{err}')
        # 全サーバの情報をまとめた集合を保存、クローリング時にはこれを使う
        with open(org_path + '/ROD/' + obj + '_url/matome.json', 'w') as f:
            try:
                exec("json.dump(list(" + obj + "_url), f)")
            except Exception as err:
                logger.exception(f'{err}')