techno_memo

個人用の技術メモ。python・ROS・AI系のソフトウェア・ツールなどの情報を記載

Webサイトからのデータ取得 ①Google系Webサービスのデータ取得(サイト検索・画像検索・Youtube検索)

やりたいこと

 pythonで下記のGoogleWebサービスの結果を自動取得する

  • Gooogle検索 (キーワードにヒットした検索結果のタイトル、リンク一覧)

  • Google画像検索 (キーワードにヒットした検索結果の画像ファイル)

  • Youtube動画検索 (キーワードにヒットした検索結果の動画ファイル)

f:id:sd08419ttic:20190227004611p:plain

Google検索(キーワードにヒットした検索結果のタイトル、リンク一覧)

実装手順

①requestsライブラリでgoogleに検索を要求し、結果を取得

②Beautifulsoupライブラリでタイトル・リンクを抽出

③日本語エンコード変換に注意しつつ結果をpandas dataframeに保存

※検索結果の説明文も取得することは可能だが、タイトルと紐づけて取得するのが難しかった (リンクだけ表示されるものがある)ので省略

    def Get_GoogleWebSearch_Result(self,keyword,num=100):
        '''
        GoogleWeb検索結果と説明文の取得
        '''
        result_df = pd.DataFrame(columns=['Title','Link'] )
        title_list = []
        link_list = []

        print("Loading...")
        res = requests.get("https://google.com/search?num="+str(num)+"&q=" + " ".join(keyword))
        res.encoding = res.apparent_encoding
        res.raise_for_status()
 
        # 上位の検索結果のリンクを取得する
        soup = BeautifulSoup(res.content, "lxml")
        link_elems = soup.select('.r > a')         #タイトル・リンク
        link_elems2 = soup.select('.s > .st')      #説明文 (タイトル・リンクと一緒に表示させたかったがずれるので取得のみ)
 
        # 各結果をブラウザのタブで開く
        num_open = min(100, len(link_elems2))
        for i in range(num_open):
             tex_temp = link_elems[i].get_text()    #タイトル文字列の処理
             try:
                print(tex_temp)
             except:    #機種依存文字を含む場合はエンコード可能な文字だけを出力
                tex_temp = tex_temp.encode('cp932',"ignore")
                tex_temp = tex_temp.decode('cp932')
                print(tex_temp)

             link_temp = "https://google.com" + link_elems[i].get("href")
        
             #結果への追加
             title_list.append(tex_temp)
             link_list.append(link_temp)
             #tex_temp2 = link_elems2[i].get_text() #説明文の保存 (タイトル・リンクと一緒に表示させたかったがずれるので取得のみ)
             #try:
             #   print(tex_temp2)
             #except:    #機種依存文字を含む場合はエンコード可能な文字だけを出力
             #   tex_temp2 = tex_temp2.encode('cp932',"ignore")
             #   tex_temp2 = tex_temp2.decode('cp932')
             #   print(tex_temp2)
             #webbrowser.open("https://google.com" + link_elems[i].get("href"))

        result_df['Title'] = pd.Series(title_list)
        result_df['Link'] = pd.Series(link_list)

        return result_df

Google画像検索 (キーワードにヒットした検索結果の画像ファイル)

実装手順

①画像保存先ディレクトリの存在を確認し、すでにある場合はツリーごと削除(自動でリネーム保存されるため)

google_images_downloadライブラリで画像検索結果をフォルダに保存

    def Get_GoogleImageSearch_Result(self,keyword,imnum=100):
        '''
        Google画像検索結果の取得 (取得した画像は実行ファイルと同じ階層のdownloadフォルダに検索ワードのフォルダを作って保存)
        '''
        imnum = int(min(99,imnum))   #100以上の場合はchrome driverの設定が必要
        target_dir =os.path.dirname(os.path.abspath(__file__))+"/downloads/"+keyword
        if os.path.exists(target_dir):
            shutil.rmtree(target_dir)

        response = google_images_download.googleimagesdownload()
        arguments = {"keywords":keyword,"limit":imnum,"la":"Japanese","pr":"test"}   #creating list of arguments
        response.download(arguments)
        return

Youtube動画検索 (キーワードにヒットした検索結果の動画ファイル)

実装手順

①requestsライブラリでYoutubeに検索を要求し、結果を取得

②Beautifulsoupライブラリでタイトル・リンクを抽出

※①②については下記記載の参考リンクにある処理を転用させていただきました

③pytubeライブラリで取得したリンクから動画をmp4形式でダウンロード

※動画形式によっては失敗する場合がある。成功したものだけを取得する。

    def Get_YoutubeSearch_Result(self,keyword,imnum=20):
        '''
        Youtube検索結果の取得 (取得した画像は実行ファイルと同じ階層のmovieフォルダに検索ワードのフォルダを作って保存)
        '''
        imnum = int(min(20,imnum))
        contents = list(Youtube.search(keyword, num=imnum))

        target_dir =os.path.dirname(os.path.abspath(__file__))+'/movie/'+keyword+"/"
        if os.path.exists(target_dir):
            shutil.rmtree(target_dir)
        os.makedirs(target_dir)

        for indx in range(len(contents)):
            tex_title = contents[indx].title.encode('cp932',"ignore")
            tex_title = tex_title.decode('cp932')
            print(tex_title)
            try:
                yt = YouTube(contents[indx].url)
                stream = yt.streams.get_by_itag(22) #MP4動画(720p)は22/ MP4音楽:140 失敗する動画はyt.streams.all()で表示後個別調整すれば取得可能
                stream.download(target_dir)
            except:
                print("error")
        return

実装済ソースコード

github.com

参考サイト

Google検索結果のスクレイピング

su-gi-rx.com

Google画像検索の取得ライブラリ Google-images-download

github.com

Youtube検索結果のスクレイピング方法

qiita.com

Youtube動画ダウンロードライブラリ Pytube

github.com

データマージ方法① 複数ファイルに記載された集計データに対するマージ

やりたいこと

 複数ファイルに記載された集計データのマージ   * 各種の集計結果 (アンケート、帳票など)

* 時系列データ (タイムスタンプの処理)については別記事で記載予定

複数ファイルに記載された集計データのマージ

 一般的によく使われそうなケースを想定して場合分けして考える

①複数グループのデータのマージ

  • 重複する対象がない複数グループに対して同じ項目の計測をしたデータ

(下記では複数のチームに対する身体測定結果の例でイメージを記載)

[f:id:sd08419ttic:20190224192344p:plain]

実装

pandasのconcat関数を適用する

pandasのデータフレームリスト(以前記載した方法で取得)を引数とすると下記の処理でマージできる。

    def merge_data_simlpe(self,input_pd_list):
        '''
        最もシンプルなデータのマージ (pandas concatを使った縦方向の結合)
        '''
        result = input_pd_list[0]
        for dnameindx in range(len(input_pd_list)-1) :
            result = pd.concat([result,input_pd_list[dnameindx+1]])

        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result

②同一対象を含むデータのマージ

  • 重複する対象を含む複数ファイルのデータ (同じ対象を別の時間で計測した結果など)

(下記では同じ組織に対する年度毎の身体測定結果の例でイメージを記載)

f:id:sd08419ttic:20190224204859p:plain

実装①

各dataframeに新しい列を追加し、ファイル名の情報を追加する

追加したdfをpandasのconcat関数を適用して結合する

    def merge_data_with_filenamecol(self,input_pd_list,fname_list):
        '''
        ファイル名を新しい列として追加するマージ
        '''
        result = input_pd_list[0]

        fname, ext = os.path.splitext( os.path.basename(fname_list[0]) )    #ファイル名/拡張子の取得
        result["file_name"] = fname
        for dnameindx in range(len(input_pd_list)-1) :
            fname, ext = os.path.splitext( os.path.basename(fname_list[dnameindx+1]) )    #ファイル名/拡張子の取得
            temp_df = input_pd_list[dnameindx+1]
            temp_df["file_name"] = fname
            result = pd.concat([result,temp_df])

        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result
実装②

各dataframeの列にファイル名を添え字として保存する (下記の例)

f:id:sd08419ttic:20190224220712p:plain

上図において全て存在しているものを残す場合と、存在しない場合はでNanとする場合の2つの実装を考慮する。

(この2つはデータの種類によってどちらの形式を選択するか考慮したほうが良いと考えたため)

    def merge_data_with_merge_only_common(self,input_pd_list,fname_list):
        '''
        一番左の列の要素をキーとして各ファイルのデータを添え字付きでマージする処理 (全てのファイルにキーが存在する行のみ表示)
        '''
        result = input_pd_list[0]
        fname, ext = os.path.splitext( os.path.basename(fname_list[0]) )    #ファイル名/拡張子の取得
        ori_columns = result.columns.tolist()
        for colindx in range(len(ori_columns)-1):
            ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
        result.columns = ori_columns
        key_name = result.columns[0]    #一番左の列をキーとする

        for dnameindx in range(len(input_pd_list)-1) :
            fname, ext = os.path.splitext( os.path.basename(fname_list[dnameindx+1]) )    #ファイル名/拡張子の取得
            temp_df = input_pd_list[dnameindx+1]
            ori_columns = temp_df.columns.tolist()
            for colindx in range(len(ori_columns)-1):
                ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
            temp_df.columns = ori_columns
            result = pd.merge(result,temp_df,on=key_name)
        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result

    def merge_data_with_merge_all_data(self,input_pd_list,fname_list):
        '''
        一番左の列の要素をキーとして各ファイルのデータを添え字付きでマージする処理 (キーが不在の場合はNanで表示)
        '''
        result = input_pd_list[0]
        fname, ext = os.path.splitext( os.path.basename(fname_list[0]) )    #ファイル名/拡張子の取得
        ori_columns = result.columns.tolist()
        for colindx in range(len(ori_columns)-1):
            ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
        result.columns = ori_columns
        key_name = result.columns[0]    #一番左の列をキーとする

        for dnameindx in range(len(input_pd_list)-1) :
            fname, ext = os.path.splitext( os.path.basename(fname_list[dnameindx+1]) )    #ファイル名/拡張子の取得
            temp_df = input_pd_list[dnameindx+1]
            ori_columns = temp_df.columns.tolist()
            for colindx in range(len(ori_columns)-1):
                ori_columns[colindx+1] = ori_columns[colindx+1]+"_"+fname   #列名に添え字を追加
            temp_df.columns = ori_columns
            result = pd.merge(result,temp_df,on=key_name, how='outer')
            print(result)

        result = result.reset_index(drop=True)    #インデックスを振りなおす

        return result

実装結果は下記に記載

github.com

その他集約するファイル名の命名ルールで判定する、データ種別で結合可能か判定するなどのアプローチがあるが長くなりそうなので別記事で記載する。

画像・動画ファイル読み込み方法 (フォルダ配下の画像/動画/ドキュメントを一括読み込み)

やりたいこと

 ユーザーが指定したローカルフォルダにある下記ファイルを画像形式で読み込む

  • jpgファイル

  • pngファイル

  • aviファイル

  • mp4ファイル

  • pdf ファイル (各ページを画像として取得)

 フォルダ・ファイルパスを指定すると拡張子からフォーマットを識別して取得

f:id:sd08419ttic:20190220235434p:plain

 動画は全フレーム/PDFは全ページを読み込む

 読み込み結果はpandas dataframeのリスト(ファイル数分)、ファイル名のリストとして出力する

事前準備

 pdfファイルの読み込みにpdf2imageというライブラリを利用する

github.com

 上記ライブラリはpopperというアプリを動かす必要があるため、下記サイトからダウンロードして保存する

blog.alivate.com.au

 解凍後にbinファイルのパスを環境変数に指定する必要がある。  下記githubスクリプトではpythonファイル内でパスを指定する処理を入れているのでフォルダ名を変更すれば利用できる。

実装

実装結果

https://github.com/sd08419ttic/python_tech_memo

各フォーマットファイルをnumpy ndarray として読み込む

画像・動画はopencvの関数を利用する (動画はフレームを1フレームづつ読み込み、成功した場合は保持する)

pdfはPILファイルをndarrayに変換した後で、色の順序をBGRにして保存する

    def read_image_data(self,filename):
        '''
        画像データを読みとる (jpg/png/bmp)
        '''
        result = cv2.imread(filename)
        return result

    def read_movie_data(self,filename):
        '''
        動画ファイルを読み込む(avi,mp4,wmv)
        '''
        result_list = []
        try:
            cap = cv2.VideoCapture(filename)
            ret, frame = cap.read()             #最初のフレームを取得
        except:
            print(filename ,"could not loaded!")
            traceback.print_exc()
            return result_list

        indx = 0
        while (cap.isOpened()):
            ret, frame = cap.read()             #最初のフレームを取得
            if ret == True: #読み込み成功判定
                #img = cv2.cvtColor(frame, cv2.COLOR_BGR)
                result_list.append(frame)
                print("deb_movie",indx)
                indx = indx +1
            else:
                break
        cap.release()
        return result_list

    def read_pdf_page(self,filename):
        '''
        PDFファイルを画像として読み込む (ページを一括変換)
        '''
        result_list = []
        try:
            images = convert_from_path(filename)
            for indx in range(len(images)):
                img_temp = np.asarray(images[indx])
                img_temp = img_temp[:, :, ::-1].copy()    #RGB->BGR
                result_list.append(img_temp)
        except:
            print(filename, "could not loaded!")
            traceback.print_exc()
            result_list = null
        return result_list

参考サイト・書籍

haitenaipants.hatenablog.com

参考書 Pythonではじめるデータラングリング ―データの入手、準備、分析、プレゼンテーション

www.amazon.co.jp

トップページ

このブログのコンセプト

業務等で活用しやすい『機能単位』でノウハウをまとめる

  • イラスト1つでやりたいことがわかる構成とする

  • 一般的なPCの開発環境(Windows+Anaconda等)ですぐに試せるコードをgithubに残す

  • 詳細な機能の記事(各種ライブラリに関する使い方説明など) は他サイトのリンクにとどめる

カテゴリ

pythonによるデータ分析

  • 業務等でpythonスクリプトを用いたデータ処理・業務改善を行うことを想定した場合のノウハウまとめ

f:id:sd08419ttic:20190321220444p:plain

データ読み込み

題目 説明 リンク
数値データの読み込み 指定したフォルダの各種ファイル(csv/xlsx/json等)から数値データを読み込む リンク
画像・動画データの
読み込み
指定したフォルダの画像・動画ファイル(jpg/png/avi等)から数値データを読み込む リンク
Webサイトからの
データ取得
WEBサイトから情報を取得する リンク
命名規則・作成日などの
条件付きファイル探索
条件にマッチしたファイルだけを探索して取得する リンク

前処理

題目 説明 リンク
画像データの前処理 画像データを処理しやすいように整える(サイズ・ノイズ除去など) リンク
数値データの前処理 数値データのノイズ・欠損値を処理する リンク
数値データマージ・削減 データの内容に応じてマージ・削減する処理 リンク

特徴抽出/パターン認識

題目 説明 リンク
物体検出① 簡易的な物体検出を行う(テンプレートマッチング/色検出/エッジ検出) リンク
物体検出② YoloV3を用いたWebカメラ画像の物体検出 リンク
統計処理 入力データの特性を算出して可視化する TBD
機械学習 データの特性を学習し、適切に分類する TBD

GUI出力/ユーザー要求処理

題目 説明 リンク
GUI tkinterを用いたGUI実装 リンク
グラフ表示 matplotlibを用いたデータの可視化 リンク
グラフ表示(アニメーション) matplotlibのアニメーション描画 リンク
exe化 pyinstllerを用いたpythonスクリプトの実行ファイル化 リンク
ユーザー入力の受理 pygameを用いたキーボード/ゲームパッド操作取得 リンク

電子工作プロトタイピング

f:id:sd08419ttic:20190221233311p:plain

前提知識

題目 説明 リンク
開発環境 プロトタイプ用ハード・開発環境に関する考え方 リンク
Raspberry Pi開発環境(ハード) 必要なハードウェアについて リンク
Raspberry Pi開発環境(ソフト) Ubuntu mate/ROSの導入方法について リンク
ソフトウェアアーキテクチャ プロトタイピング時のソフトウェアアーキテクチャについて リンク

環境構築

題目 説明 リンク
Pythonソフト構造 複数機能・ファイルから成り立つプロジェクトの構築 リンク
Python処理タイミング 各機能の動作タイミングの設計方法 TBD
Arduinoソフト構造 複数機能・ファイルから成り立つプロジェクトの構築 リンク
Arduino処理タイミング 各機能の動作タイミングの設計方法 リンク

通信機能

題目 説明 リンク
ArduinoとPCの通信 シリアル通信(USBケーブル) リンク
PCとの接続 リモートデスクトップ/カメラ画像の送信/信号の送信 TBD
クラウドサービスとの接続 IFTTT連携 (Google Home/スマートフォン) TBD

ROS開発

題目 説明 リンク
ROS基礎 概念/用語/コマンドの使い方 リンク
ROS基礎2 プロジェクト構築 リンク
PCとの通信 Ubuntu PCとRaspberry piとの接続/制御信号の通信 リンク
ロボットモデルの記述 URDFファイルにロボットのモデルを記述する リンク
移動ロボット基礎 シミュレーション上でロボットを移動させる リンク
移動ロボットの走行経路 ロボットの走行経路を設定する リンク
移動ロボットの経路追従 走行経路を追従する車速・角速度の制御 リンク

センシング

題目 説明 リンク
カメラセンシング Raspberry pi カメラを使った画像処理 リンク
各種センサ実装 各種センサの実装 TBD

pandasのデータファイル読み込み方法 (フォルダ配下のファイルを一括読み込み)

やりたいこと

 Pandasでユーザーが指定したローカルフォルダにある下記データファイルを読み込む

  • csvファイル

  • tsvファイル

  • xls/xlsxファイル

  • jsonファイル

 フォルダ・ファイルパスを指定すると拡張子からフォーマットを識別して取得

f:id:sd08419ttic:20190219221412p:plain

 各データファイルは1行目に項目ラベル、2行目以降にデータが記述されていることが前提

 読み込み結果はpandas dataframeのリスト(ファイル数分)、ファイル名のリストとして出力する

実装

実装結果

github.com

各フォーマットファイルをpandas data frame として読み込む

pandasの各種フォーマットの読み込み関数を利用する 入力パスの拡張子からそれぞれの関数を呼び分ける

#csvデータの読み込み
    def read_file_autohandle(self,input_path):
        '''
        入力ファイルから拡張子を自動判定してpandasに読み込む
        '''
        root, ext = os.path.splitext(input_path)  #拡張子の取得

        #拡張子毎に読み込みを実施
        if ext == ".csv":
            result = self.read_csv_data(input_path)
        elif ext == ".tsv":
            result = self.read_tsv_data(input_path)
        elif (ext == ".xls") or (ext == ".xlsx"):
            result = self.read_xls_data_firstseat(input_path)
        elif ext == ".json":
            result = self.read_json_data(input_path)
        return result


    def read_csv_data(self,filename):
        '''
        csvデータを読みとる
        '''
        result = pd.read_csv(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_tsv_data(self,filename):
        '''
        tsvデータを読みとる
        '''
        result = pd.read_table(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_xls_data_firstseat(self,filename):
        '''
        xlsx or xlsファイルの1枚目のシートを読みとる
        '''
        result = pd.read_excel(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_xls_data_allsheat(self,filename):
        '''
        xlsx or xlsファイルの全てのシートを読み込む
        '''
        result = pd.read_excel(filename, encoding="shift-jis", sheetname =None)    #日本語データ(Shift-Jis)を含む場合を想定
        return result

    def read_json_data(self,filename):
        '''
        jsonファイルを読み込む
        '''
        result = pd.read_json(filename, encoding="shift-jis")    #日本語データ(Shift-Jis)を含む場合を想定
        return result

フォルダ内のファイルリストを探索する

フォルダ内のファイルリストを作成し、指定した拡張子に該当するものだけを抽出する サブフォルダ含めたファイルリストの作成はos.walkを用いる

    def __init__(self, ext="ALL"):
        '''
        コンストラクタ
        (読み込み対象とする拡張子を引数extで指定可能)
        '''
        if ext !="ALL":
            self.target_ext = [ ext ]
        else:
            self.target_ext = [".csv",".tsv",".xls",".xlsx",".json"]
        return

    def get_file_path_list_in_folder(self,folder_path):
        '''
        入力フォルダパス直下にあるファイルのリストを作成する
        '''
        if folder_path[-1] != "\\":
            folder_path = folder_path + "\\"


        file_full_path_list = []
        # Search_Current_Directry
        for file in os.listdir(folder_path):
            root, ext = os.path.splitext(file)
            for indx2 in range(len(self.target_ext)):
                if ext == self.target_ext[indx2]:
                    file_fullpath = folder_path + file
                    file_full_path_list.append(file_fullpath)
        return file_full_path_list

    def get_file_path_list_in_folder_recrussive(self,folder_path):
        '''
        入力フォルダパスの配下(サブフォルダ含む)にあるファイルのリストを作成する
        '''
        if folder_path[-1] != "\\":
            folder_path = folder_path + "\\"
        file_full_path_list = []

        for folder, subfolders, files in os.walk(folder_path):
            for indx in range(len(files)):
                root, ext = os.path.splitext(files[indx])
                for indx2 in range (len(self.target_ext)):
                    if ext == self.target_ext[indx2]:
                        file_fullpath = folder+'/'+ files[indx]
                        file_full_path_list.append(file_fullpath)
        return file_full_path_list

参考サイト・書籍

pandasへのデータ読み込み yolo.love

参考書 Pythonではじめるデータラングリング ―データの入手、準備、分析、プレゼンテーション

www.amazon.co.jp