示例#1
0
    def analysis_url(self, divs: List[webdriver.remote.webelement.WebElement],
                     target_author: str) -> List[BookInfo]:
        """ 検索結果解析
        検索結果から以下を解析  
        本のタイトル  
        発売日  
        著者名  
        価格  
        商品ページへのURL  
        [I] html_info : requets.get結果  
        [O] BookInfo : 解析結果
        """
        Debug.tmpprint("func : analysis_url")
        book_infos: List[BookInfo] = []
        # 情報解析/取得
        for div in divs:
            book_info = BookInfo()
            # 著者名
            book_info.author = self.__get_book_author(div)
            # TODO:著者名が検索対象と一致しなければとばす
            #      マッチ条件どうするか
            #if book_info.author != target_author:
            #    continue
            # タイトル
            book_info.title = self.__get_book_title(div)
            # 発売日
            book_info.date = self.__get_book_date(div)
            # 価格
            book_info.price = self.__get_book_price(div)
            # 商品URL
            book_info.url = self.__get_book_url(div, book_info.title)
            # 結果保持
            book_infos.append(book_info)

        return book_infos
 def __create_url_for_csv(self, filename: str) -> None:
     """  検索データ情報リスト生成(csvから生成)  
     著者名リストを生成.別途著者名をキーとしたハッシュマップを生成し,データとして検索開始日を保持する  
     [I] filename : 確認対象ファイル名  
     """
     Debug.tmpprint("func : create_url_for_csv")
     encode_str = "utf-8"
     csv_type = CsvUtil.is_utf8_file_with_bom(filename)
     if csv_type is CsvEncodeType.UTF_8_BOM:
         encode_str = "utf-8-sig"
     elif csv_type is CsvEncodeType.SHIFT_JIS:
         encode_str = "shift_jis"
     else:
         encode_str = "utf-8"
     serach_list_dict = {}
     # csv読込み
     with open(filename, mode="r", encoding=encode_str, newline="") as csvfile:
         reader = csv.reader(csvfile)
         next(reader)   #  ヘッダ読み飛ばし
         # データ生成
         for row in reader:
             # dictionaryに著者名をキーとしてデータを保持
             if row[1] == "":
                 row[1] = datetime.datetime.today().strftime("%Y/%m/%d") # 期間が空白なら,実行日を設定
             serach_list_dict[row[0]] = row[1]
     # 結果を保持
     self.search_infos = serach_list_dict
 def create_search_info_list(self, filename:str) -> None:
     """ 検索データ情報リストの生成  
     著者名リストを生成.別途著者名をキーとしたハッシュマップを生成し,データとして検索開始日を保持する  
     [I] filename : 確認対象ファイル名  
     [O] dict : キー=著者名,データ=検索開始日
     """
     Debug.tmpprint("func : create_search_info_list")
     if filename.find(".csv") > 0:
         self.__create_url_for_csv(filename)
     else:
         self.__create_url_for_db()
def main():
    Debug.tmpprint("Start\n")
    # ファイルチェック
    # ファイルが見つからなかった場合は,新規にファイルを作成し,終了する
    #if check_search_file(READ_FILE_NAME) == False:
    #    print("finish!")
    #    return

    # クローリング用クラス生成
    book_crawling = BookInfoCrawling()
    # DB設定
    #if book_crawling.set_table_key("nyasai") is False:
    #    print("finish!")
    #    return
    # 検索データ取得
    book_crawling.create_search_info_list(READ_FILE_NAME)
    # 著者リスト生成
    book_crawling.create_author_list()
    # 著者リスト保持
    target_author_list = book_crawling.get_author_list()
    # url生成
    url_list = book_crawling.create_url()
    # 検索
    all_book_infos: List[BookInfo] = []
    book_scraping = BookInfoScraping()
    search_cnt = 0
    for url in url_list:
        # 検索結果取得
        Debug.tmpprint(url)
        search_result_divs = book_crawling.exec_search(url)
        # 正常に結果が取得できた場合
        if len(search_result_divs) != 0:
            # 解析実行
            one_author_book_info = book_scraping.analysis_url(
                search_result_divs, target_author_list[search_cnt])
            # 結果をリストに保持
            all_book_infos.append(one_author_book_info)
            # 結果出力
            #output_result(one_author_book_info, book_crawling.get_author_list()[search_cnt], book_crawling.get_serch_info()[book_crawling.get_author_list()[search_cnt]])
            search_cnt += 1

    # ドライバクローズ
    book_crawling.cloase_driver()
    # 結果出力
    # TODO: 既に一度出力していれば無視するか?それとも毎回全上書きを行うか?
    # 既にファイルが有れば,そのファイルとの差分をとって結果を何かしらで通知.
    # ファイルがなければ全て通知
    output_result_for_csv(all_book_infos, book_crawling.get_author_list(),
                          book_crawling.get_serch_info())
    output_result_for_html(all_book_infos, book_crawling.get_author_list(),
                           book_crawling.get_serch_info())
    print("finish!")
 def create_url(self) -> List[str]:
     """ URL生成  
     著者名から検索用URLを生成する  
     [I] name_data : 著者名リスト  
     [O] list : URLリスト
     """
     Debug.tmpprint("func : create_url")
     url_list = []
     # 全key名でURLを生成し,listに保持
     for search_name in self.__author_list:
         Debug.tmpprint(search_name)
         url_list.append(AMAZON_SEARCH_URL + urllib.parse.quote(search_name.encode("utf-8")) + AMAZON_SEARCH_URL2) # 日本語を16進数に変換
     return url_list
def check_search_file(filename: str) -> bool:
    """ 検索リストファイル確認  
    ファイルが存在しなければ,新たに生成する  
    [I] filename : 確認対象ファイル名
    """
    Debug.tmpprint("func : check_search_file")
    if os.path.isfile(filename) == True:
        return True
    else:
        print("検索対象リストが見つかりません.新規に生成します.")
        # ファイル生成
        strs = "著者名(名字と名前の間は空白を入れないこと),取得開始期間(空なら実行日を開始日として取得)\n"
        with open(filename, mode="w", encoding="utf-8-sig",
                  newline="") as csvfile:
            csvfile.write(strs)
        return False
 def __create_url_for_db(self) -> None:
     """  検索データ情報リスト生成(DBから生成)  
     著者名リストを生成.別途著者名をキーとしたハッシュマップを生成し,データとして検索開始日を保持する  
     [I] filename : 確認対象ファイル名  
     """
     Debug.tmpprint("func : __create_url_for_db")
     serach_list_dict = {}
     # DB読込み
     serach_info = self.__db_ctrl.get_db_search_list()
     # データ生成
     for row in serach_info:
         # dictionaryに著者名をキーとしてデータを保持
         if row.search_date is None:
             row.search_date = datetime.datetime.today().strftime("%Y/%m/%d") # 期間が空白なら,実行日を設定
         serach_list_dict[row.author_name] = row.search_date
     # 結果を保持
     self.search_infos = serach_list_dict
 def add_book_info(self, author: str, date: str):
     """ 検索対象情報追加  
     [I] author : 著者名 [I] date 出力対象日閾値
     """
     # 接続
     self.__connect_db()
     # 追加
     try:
         query_str = "insert into " + self.__book_info_tbl_name + " values (?, ?)"
         self.__book_info_tbl_cursor.execute(query_str, (author, date))
     except sqlite3.Error as e:
         Debug.dprint(e.args[0])
     # output
     for row in self.__book_info_tbl_cursor.execute(
             "select * from %s" % self.__book_info_tbl_name):
         Debug.tmpprint(row)
     # 接続解除
     self.__disconnect_db()
def output_result_for_csv(all_book_infos: List[List[BookInfo]],
                          author_list: List[str], output_date: dict):
    """ CSV書き込み  
    著者名と本リスト,URL,発売日,価格を保存する  
    [I] all_book_infos : 解析後の本情報(全著者分),author_list : 著者名リスト,output_date : 出力対象の日付
    """
    Debug.tmpprint("func : output_result_for_csv")
    output_filename = "new_book_info_" + datetime.datetime.now().strftime(
        "%Y%m%d%H%M%S") + ".csv"
    #csvオープン
    with open(output_filename, mode="a", newline="",
              encoding='utf-8-sig') as csvfile:
        csvfile.write("著者名,タイトル,発売日,価格,商品URL\n")
        # 検索対象データ分ループ
        for author_cnt in range(0, len(author_list), 1):
            book_infos = all_book_infos[author_cnt]
            # 1検索対象データの結果リストから,著者名が一致するもののみを取得
            book_info_cnt = 0
            for book_info in book_infos:
                try:
                    if book_info.author.find(author_list[author_cnt]) != -1:
                        # 期間が指定日以降なら保存する
                        if datetime.datetime.strptime(book_info.date, "%Y/%m/%d") \
                            >= datetime.datetime.strptime(output_date[author_list[author_cnt]],"%Y/%m/%d"):
                            output_str = ""
                            # 著者名
                            output_str += book_info.author + ","
                            # タイトル
                            output_str += book_info.title + ","
                            # 発売日
                            output_str += book_info.date + ","
                            # 価格
                            output_str += "\"" + book_info.price + "\","
                            # 商品URL
                            output_str += book_info.url + "\n"
                            csvfile.write(output_str)
                except IndexError:
                    print("IndexError!! -> " + book_info.author)
                    book_info_cnt -= 1
                    continue
                book_info_cnt += 1
 def set_user_info_key(self, user_name: str) -> bool:
     """ ユーザ情報テーブル主キー設定  
     [I] username ユーザ名  
     [O] 結果
     """
     result = False
     try:
         # DB接続
         self.__connect_db()
         # 検索
         query_str = "select * from " + USER_INFO + " where user_name=?"
         self.__user_info_tbl_cursor.execute(query_str, (user_name, ))
         if self.__user_info_tbl_cursor.fetchone is not None:
             # 検索対象情報テーブル名保持
             self.__book_info_tbl_name = user_name + BOOK_INFO
             result = True
     except sqlite3.Error as e:
         Debug.dprint(e.args[0])
     # 未登録情報
     self.__disconnect_db()
     return result
示例#11
0
 def __get_book_price(self,
                      div: webdriver.remote.webelement.WebElement) -> str:
     """ 本の価格取得  
     [I] div : htmlデータ  
     [O] list : 本の価格リスト
     """
     Debug.tmpprint("func : get_book_price")
     # 価格部分抽出
     html_element = lxml.html.fromstring(div.get_attribute('innerHTML'))
     prices = html_element.xpath(
         "//div[contains(@class, 'sg-row')]"\
         "//div[contains(@class, 'sg-col-inner')]"\
         "//div[contains(@class, 'a-row')]"\
         "//span[contains(@class, 'a-price')]"
         "//span[contains(@class, 'a-offscreen')]")
     formatting_author_str: str = ""
     for price in prices:
         formatting_author_str = price.text_content().encode(
             "utf-8").decode("utf-8")
         Debug.tmpprint(formatting_author_str)
         break
     return formatting_author_str
 def get_db_search_list(self) -> list:
     """ 著者名リスト取得  
     [O] 著者リスト(DBAuthorInfo型)
     """
     search_list = []
     try:
         # DB接続
         self.__connect_db()
         # 全データ取得
         query_str = "select * from " + self.__book_info_tbl_name
         self.__book_info_tbl_cursor.execute(query_str)
         # 1データずつリストに保持
         for row in self.__book_info_tbl_cursor.fetchall():
             book_info = DBAuthorInfo()
             book_info.author_name = row[0]
             book_info.search_date = row[1]
             search_list.append(book_info)
     except sqlite3.Error as e:
         Debug.dprint(e.args[0])
     # 接続解除
     self.__disconnect_db()
     return search_list
def output_result_for_html(all_book_infos: List[List[BookInfo]],
                           author_list: List[str], output_date: dict):
    """ HTML書き込み  
    著者名と本リスト,URL,発売日,価格を保存する  
    [I] all_book_infos : 解析後の本情報(全著者分),author_list : 著者名リスト,output_date : 出力対象の日付
    """
    Debug.tmpprint("func : output_result_for_html")
    output_filename = "new_book_info_" + datetime.datetime.now().strftime(
        "%Y%m%d%H%M%S") + ".html"
    #csvオープン
    with open(output_filename, mode="a", newline="") as htmlfile:
        # 検索対象データ分ループ
        for author_cnt in range(0, len(author_list), 1):
            book_infos = all_book_infos[author_cnt]
            # 1検索対象データの結果リストから,著者名が一致するもののみを取得
            book_info_cnt = 0
            for book_info in book_infos:
                try:
                    if book_info.author.find(author_list[author_cnt]) != -1:
                        # 期間が指定日以降なら保存する
                        if datetime.datetime.strptime(book_info.date,"%Y/%m/%d") \
                            >= datetime.datetime.strptime(output_date[author_list[author_cnt]],"%Y/%m/%d"):
                            output_str = ""
                            # 著者
                            output_str += book_info.author + "<br/>"
                            # タイトル
                            output_str += book_info.title + "<br/>"
                            # 発売日
                            output_str += book_info.date + "<br/>"
                            # 価格
                            output_str += book_info.price + "<br/>"
                            # 商品URL
                            output_str += '<a href="' + book_info.url + 'target="_blank"' + '">Link</a><br/><br/>'
                            htmlfile.write(output_str)
                except IndexError:
                    print("IndexError!! -> " + book_info.author)
                    book_info_cnt -= 1
                    continue
                book_info_cnt += 1
示例#14
0
 def __get_book_date(self,
                     div: webdriver.remote.webelement.WebElement) -> str:
     """ 本の発売日取得 
     [I] div : htmlデータ  
     [O] list : 本の発売日リスト
     """
     Debug.tmpprint("func : get_book_date")
     # 発売日部分取得
     html_element = lxml.html.fromstring(div.get_attribute('innerHTML'))
     dates = html_element.xpath(
         "//div[contains(@class, 'sg-row')]"\
         "//div[contains(@class, 'sg-col-inner')]"\
         "//div[contains(@class, 'a-section a-spacing-none')]"\
         "//div[contains(@class, 'a-row a-size-base a-color-secondary')]"\
         "//span[contains(@class, 'a-size-base a-color-secondary a-text-normal')]")
     formatting_title_str: str = ""
     for date in dates:
         formatting_title_str = datetime.datetime.strptime(
             date.text_content().encode("utf-8").decode("utf-8"),
             "%Y/%m/%d").strftime("%Y/%m/%d")
         Debug.tmpprint(formatting_title_str)
         break
     return formatting_title_str
示例#15
0
 def __get_book_url(self, div: webdriver.remote.webelement.WebElement,
                    title_info: list) -> str:
     """ 本の商品ページURL取得  
     [I] div : htmlデータ  
     [O] list : 本の商品ページリスト
     """
     Debug.tmpprint("func : get_book_url")
     # URL部分抽出
     html_element = lxml.html.fromstring(div.get_attribute('innerHTML'))
     urls = html_element.xpath(
         "//div[contains(@class, 'sg-row')]"\
         "//div[contains(@class, 'sg-col-inner')]"\
         "//div[contains(@class, 'a-section a-spacing-none')]"\
         "//h2[contains(@class, 'a-size-mini a-spacing-none a-color-base s-line-clamp-2')]"\
         "//a[contains(@class, 'a-link-normal a-text-normal')]"\
         "//@href")
     LINK_URL = "https://www.amazon.co.jp"
     result_url_str = ""
     for url in urls:
         result_url_str = LINK_URL + url
         Debug.tmpprint(result_url_str)
         break
     return result_url_str
示例#16
0
 def __get_book_title(self,
                      div: webdriver.remote.webelement.WebElement) -> str:
     """ 本のタイトル取得  
     [I] div : htmlデータ  
     [O] list : 本のタイトルリスト
     """
     Debug.tmpprint("func : get_book_title")
     # 商品タイトル部分抽出
     html_element = lxml.html.fromstring(div.get_attribute('innerHTML'))
     titles = html_element.xpath(
         "//div[contains(@class, 'sg-row')]"
         "//div[contains(@class, 'sg-col-inner')]"
         "//div[contains(@class, 'a-section a-spacing-none')]"
         "//h2[contains(@class, 'a-size-mini a-spacing-none a-color-base s-line-clamp-2')]"
         "//span[contains(@class, 'a-size-medium a-color-base a-text-normal')]"
     )
     formatting_title_str: str = ""
     for title in titles:
         formatting_title_str = title.text_content().encode("utf-8").decode(
             "utf-8")
         Debug.tmpprint(formatting_title_str)
         break
     return formatting_title_str
示例#17
0
    def __get_book_author(self,
                          div: webdriver.remote.webelement.WebElement) -> str:
        """ 本の著者名取得  
        [I] div : htmlデータ  
        [O] list : 本の著者名リスト
        """
        Debug.tmpprint("func : get_book_author")
        # 著者名部分抽出
        html_element = lxml.html.fromstring(div.get_attribute('innerHTML'))
        authors_link = html_element.xpath(
                "//div[contains(@class, 'sg-row')]"\
                "//div[contains(@class, 'sg-col-inner')]"\
                "//div[contains(@class, 'a-section a-spacing-none')]"\
                "//div[contains(@class, 'a-row a-size-base a-color-secondary')]"\
                "//a[contains(@class, 'a-size-base')]")
        authors_nonlink = html_element.xpath(
            "//div[contains(@class, 'sg-row')]"
            "//div[contains(@class, 'sg-col-inner')]"
            "//div[contains(@class, 'a-section a-spacing-none')]"
            "//div[contains(@class, 'a-row a-size-base a-color-secondary')]"
            "//span[contains(@class, 'a-size-base')]")
        formatting_author_str: str = ""
        is_continue = lambda target_str: target_str == "" or target_str == "、 " or target_str == ", "
        is_break = lambda target_str: target_str == " | "
        for author in authors_link:
            tmp_str = author.text_content().encode("utf-8").decode("utf-8")
            # 無効,区切りは無視
            if is_continue(tmp_str):
                continue
            # 日付との区切り部分のため終了
            if is_break(tmp_str):
                break
            # 改行と空白を削除
            tmp_str = tmp_str.replace("\n", "")
            tmp_str = tmp_str.replace(" ", "")
            formatting_author_str = tmp_str
            Debug.tmpprint(formatting_author_str)

        for author in authors_nonlink:
            tmp_str = author.text_content().encode("utf-8").decode("utf-8")
            # 無効,区切りは無視
            if is_continue(tmp_str):
                continue
            # 日付との区切り部分のため終了
            if is_break(tmp_str):
                break
            # 改行と空白を削除
            tmp_str = tmp_str.replace("\n", "")
            tmp_str = tmp_str.replace(" ", "")
            # 現状保持無しなら新規追加
            if len(formatting_author_str) == 0:
                formatting_author_str = tmp_str
            else:
                # 保持していれば文字列追加
                formatting_author_str += ("、" + tmp_str)
            Debug.tmpprint(formatting_author_str)
        return formatting_author_str
    def create_db(self, user_name: str) -> bool:
        """ DB生成
        """
        try:
            # 接続
            self.__connect_db()
            # テーブル生成
            # 未生成時のみ生成を行う
            #self.__user_info_tbl_cursor.execute("drop table if exists %s" % USER_INFO)
            # ユーザ情報テーブル
            self.__user_info_tbl_cursor.execute(
                "create table if not exists %s (user_name text primary key, table_name text)"
                % USER_INFO)
            #
            self.__book_info_tbl_name = user_name + BOOK_INFO
            #self.__book_info_tbl_cursor.execute("drop table if exists %s" % (table_name))
            self.__book_info_tbl_cursor.execute(
                "create table if not exists %s (author_name text primary key, date text)"
                % (self.__book_info_tbl_name))

            # 検索情報テーブルを追加
            query_str = "insert into " + USER_INFO + " values (?, ?)"
            # 現データ数取得
            #self.__user_info_tbl_cursor.execute("select count(*) from %s " % self.__user_info_tbl_name)
            #data_max = self.__user_info_tbl_cursor.fetchall()
            self.__user_info_tbl_cursor.execute(
                query_str, (user_name, self.__book_info_tbl_name))

        except sqlite3.Error as e:
            # 検索対象情報テーブル名保持
            self.__book_info_tbl_name = user_name + BOOK_INFO
            Debug.dprint(e.args[0])
            return False

        # output
        for row in self.__user_info_tbl_cursor.execute("select * from %s" %
                                                       USER_INFO):
            Debug.tmpprint(row)
            for row_sub in self.__book_info_tbl_cursor.execute(
                    "select * from %s" % row[1]):
                Debug.tmpprint(row_sub)

        # 接続解除
        self.__disconnect_db()
        return True
 def exec_search(self, url:str) -> List[webdriver.remote.webelement.WebElement]:
     """ 検索実行
     渡されたURLから検索処理を実行する  
     [I] url : 検索実行URL  
     [O] 検索結果
     """
     Debug.tmpprint("func : exec_search")
     self.__driver.get(url)
     body = self.__driver.find_element_by_tag_name("body")
     body.send_keys(Keys.CONTROL + 't')
     divs = self.__driver.find_elements_by_class_name('s-result-item')
     # 失敗時は一定時間待ってから再度取得を試みる
     if len(divs) == 0:
         is_ok = False
         for retry in range(0,REQUEST_RETRY_NUM,1):
             time.sleep(REQUEST_WAIT_TIME *( retry+1))
             # 再度実行
             divs = self.__driver.find_elements_by_class_name('s-result-item')
             if len(divs) != 0:
                 is_ok = True
                 break
             else:
                 if retry == REQUEST_RETRY_NUM:
                     # リトライ上限に達した
                     Debug.dprint("request err -> " + self.__author_list[self.__search_cnt])
         if is_ok == False:
             # 最後まで成功しなかった場合該当データ削除
             self.__search_infos.pop(self.__author_list[self.__search_cnt])
             self.__author_list.pop(self.__search_cnt)
             # 結果なし
             divs = []
     # 検索ログ出力
     Debug.dprint("search author(" +  str(self.__search_cnt + 1) + "/" + str(self.get_author_list_num()) + ") -> " + self.__author_list[self.__search_cnt])
     # 検索数を進める
     self.__search_cnt += 1
     # 結果を返す
     return divs