0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

reCAPTCHAのサイトを毎日自動スクレイピングする (4/7: S3ファイル処理)

Posted at
  1. 要件定義〜python環境構築
  2. サイトのスクレイピング機構を作る
  3. ダウンロードしたファイル(xls)を加工し、最終成果物(csv)を作成するようにする
  4. S3からのファイルダウンロード / S3へのファイルアップロードをつくる
  5. 2captchaを実装
  6. Dockerコンテナで起動できるようにする
  7. AWS batchに登録

S3の操作

ファイル構成の変更

前回までで スクレイピング〜ファイルの加工までをだらだら書いていきましたが、このタイミングでファイルの分割をしました。

S3につなげる=Inputとなるwordsが大量になる=テスト実行に時間がかかるということで、テストモードを作るのがひとつの目的です。

今回はこんな感じに分けてみました。

ファイル構成
├── app
│   ├── drivers          seleniumドライバーを置く
│   └── source           
│       ├── run.py           メイン実行ファイル
│       ├── scraping.py      スクレイピング処理(第2回)
│       ├── make_outputs.py  ダウンロードしたファイルの加工処理(第3回)
│       ├── s3_operator.py   S3に関する処理(今回:第4回)
│       └── configs.py        環境変数やパスワードなど
└── tmp
    ├── files
    │   ├── fromS3       メイン実行
    │   ├── toS3         メイン実行
    │   └── download     スクレイピングでダウンロードしたファイルを置く
    └── logs             ログ(seleniumログなど)

メインの実行ファイル=run.py から、各ファイルの処理を呼び出します。
S3から取ってくるwordsは膨大なので、テスト時は実行時間が短くなるように「テスト実行モード」ができるようにしておきます。

run.py
if __name__ == '__main__':
  #テスト実行モードを作れるようにしておく
  parser = argparse.ArgumentParser(description='scraping batch')
  parser.add_argument('--run_mode', dest='run_mode', default = 'normal', help='run_mode: test | normal')
  args = parser.parse_args()
  
  # 環境ごとの変数を取ってくる
  env = os.getenv('BATCH_ENV', 'LOCAL')
  config = configs.load(env)
  
  #メイン処理  
  words = s3_operator.getFromS3(config) #S3からファイルを取得し、INPUTたるwordsを得る
  scraping.main(config,args,words) #スクレイピング処理(テスト実行時は2件で終わるようにしている)
  make_outputs.main(config,words) #ファイル処理
  s3_operator.sendToS3(config) #S3にファイルを送る

ちなみに、configs.pyはこんな感じです

configs.py
class Config:
    
    login_url = "XXXXXXXXXX"
    login_id =  "XXXXXXXXXX"
    login_password = "XXXXXXXXX"

    #S3
    region_name ="XXXXXXXXXX"
    input_path = "XXXXXXXXXXX"
    output_path = "XXXXXXXXXX"

    @staticmethod
    def load(env_name):
        if env_name == 'PRD':
            return PrdConfig()
        elif env_name == 'STG':
            return StgConfig()
        elif env_name == 'DEV':
            return DevConfig()
        else:
            return LocalConfig()

class LocalConfig(Config):
    access_key_id = 'XXXXXXXXX'
    secret_access_key = 'XXXXXXXXXX'
    bucket_name = 'XXXXX'

#...以下 DevConfig,StgConfig,PrdConfigも同様

S3からファイルを取得する処理

S3からファイルを取得する処理を書いていきます。

  • ダウンロードファイルの格納先を作る
  • S3のファイル名の一覧を取得
  • 一つずつダウンロード
  • ダウンロードしたファイルから取得

※実際にはファイルが複数あったので上記の流れにしたのですが…この記事では/YYYYMMDD/words.csvしか取得しないことにするのでちょっと冗長になっちゃうなあ。まあいいか。

s3_operator
def getFromS3(config):
    #S3からダウンロードしたファイルの格納先を作る
    date = datetime.now().strftime('%Y%m%d')
    dldir_name = os.path.abspath(__file__ + '/../../../tmp/files/fromS3/'.format(date))
    dldir_path = Path(dldir_name)
    dldir_path.mkdir(exist_ok=True)
    download_dir = str(dldir_path.resolve())
    
    #S3に接続するクライアント情報を定義
    s3_client = boto3.client(
      's3',
      region_name=config.region_name,
      aws_access_key_id=config.access_key_id,
      aws_secret_access_key=config.secret_access_key,
    )
    
    #指定したbucket内の指定パスなファイルの一覧を取得
    key_prefix = config.output_path + '/{}/'.format(date)
    response = s3_client.list_objects_v2(Bucket=config.bucket_name,Prefix=key_prefix)
    if response["KeyCount"] == 0:
        print('オブジェクトが存在しません:{}'.format(key_prefix))
        return
    
    #ファイルを一つずつダウンロード
    for object in response["Contents"]:
        object_name = object["Key"]
        download_file_name = os.path.basename(object["Key"])
        if len(download_file_name) == 0:
            continue
        download_file_name = download_dir + '/' + download_file_name
        s3_client.download_file(Bucket=config.bucket_name, Key=object_name, Filename=download_file_name)
        print('オブジェクト「{}」をダウンロードしました。ダウンロード先:{}'.format(object_name,download_file_name))

    #wordsを得る
    download_file_name = download_dir + '/words.csv'
    return pd.read_csv(download_file_name).values.tolist()
    

S3にファイルを送る処理

こちらは、ファイルを送る処理。こっちは作ったものを送るだけなので簡単です

s3_operator
def exportToS3(config):
    date = datetime.now().strftime('%Y%m%d')
    s3_client = boto3.client(
        's3',
        region_name=config.region_name,
        aws_access_key_id=config.access_key_id,
        aws_secret_access_key=config.secret_access_key,
    )

    upfiles = glob.glob(os.path.abspath(__file__ + '/../../../tmp/files/toS3/{}/*'.format(date)))
    for f in upfiles :
        filename = os.path.split(f)[1]
        object_name = config.input_path + "/{}/{}".format(date,filename)
        s3_client.upload_file(f,config.bucket_name,object_name)

完成

これで、python run.pyすれば、目的を達成できるプログラムになりました。

が…

  • 毎週実行するには自分のPCで叩かなければならない
  • reCAPTCHAを自分で解除しなければならない
  • 実行中はPCを立ち上げっぱなしにする必要がある

という状況なので、次回からはそれをなんとかしていきます。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?