定期的にローカル環境からスクレイピングしたデータをBigQueryへ送信します。
このプログラムの実装時に考えなければいけなかった環境面の問題をメモします。
- ローカル環境 vs クラウド環境
- 接続エラーが出る
- 環境変数がリセットされる
いったん上の項目だけ作成したものの、本記事の内容は今後作業・勉強を経てアップデート予定。
ローカル環境 vs クラウド環境
前提
クラウド環境のほうが管理しやすいのですが、ローカル環境で実装しました。
何種類かの閲覧パターン=cookieに基づいて表示される広告の情報が欲しかったからです。
Cookieはもともとクライアント(ブラウザ)側で持つものなので、
ローカル環境でプログラムを動かしたほうが良いと判断しました。
対処した内容
Chromeのユーザープロファイル 情報を使い、プロファイル単位でcookieを丸ごと利用できるようにしました。
課題
今後はこれをクラウドで完結できるようにしたい。できるのかな、、
2019/12/27追記:↓ 足がかりとしてこんなことを調べてみました↓
【Google広告】オーディエンスリスト・SSPへのデータ送信内容の検証ノート(Cookie/HTTPリクエスト)
接続エラーが出る
問題
BigQueryで一回目はデータを送ることができても、2回目のループで以下のエラーが出ました。
一旦Googleストレージにアップロードすることで回避できるか検証したが、やはり発生しました。
ConnectionResetError: [WinError 10054] 既存の接続はリモート ホストに強制的に切断されました。
調べた記事
https://codeday.me/jp/qa/20190418/650536.html
https://stackoverflow.com/questions/34635787/bigquery-job-connexion-closed
http://100mds.blogspot.com/2013/05/wsaeconnreset-10054.html
▽以下は15分以内の短いループで実行したときに発生
def job():
for i in (スクレイピングする種類の一覧):
for x in range(スクロール&データ取得のループ回数):
UploadCSVtoBigquery(...)
________________________________________________________________
ログ
upload to BigQuery
Traceback (most recent call last):
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 672, in urlopen
chunked=chunked,
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 421, in _make_request
six.raise_from(e, None)
File "<string>", line 3, in raise_from
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 416, in _make_request
httplib_response = conn.getresponse()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 1336, in getresponse
response.begin()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 306, in begin
version, status, reason = self._read_status()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 267, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "C:\Users\masato\Documents\python\lib\socket.py", line 589, in readinto
return self._sock.recv_into(b)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 1071, in recv_into
return self.read(nbytes, buffer)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 929, in read
return self._sslobj.read(len, buffer)
ConnectionResetError: [WinError 10054] 既存の接続はリモート ホストに強制的に切断されました。
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\masato\Documents\python\lib\site-packages\requests\adapters.py", line 449, in send
timeout=timeout
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 720, in urlopen
method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\util\retry.py", line 400, in increment
raise six.reraise(type(error), error, _stacktrace)
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\packages\six.py", line 734, in reraise
raise value.with_traceback(tb)
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 672, in urlopen
chunked=chunked,
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 421, in _make_request
six.raise_from(e, None)
File "<string>", line 3, in raise_from
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 416, in _make_request
httplib_response = conn.getresponse()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 1336, in getresponse
response.begin()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 306, in begin
version, status, reason = self._read_status()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 267, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "C:\Users\masato\Documents\python\lib\socket.py", line 589, in readinto
return self._sock.recv_into(b)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 1071, in recv_into
return self.read(nbytes, buffer)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 929, in read
return self._sslobj.read(len, buffer)
urllib3.exceptions.ProtocolError: ('Connection aborted.', ConnectionResetError(10054, '既存の接続はリモート ホストに強制的に切断されました。', None, 10054, None))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "main.py", line 57, in <module>
job()
File "main.py", line 51, in job
UploadCSVtoBigquery(profile_label+table_date)
File "C:\Users\masato\Documents\GitHub\yahoo_scraping\uploadCSVtoBigquery.py", line 24, in __init__
job = client.load_table_from_file(source_file, table_ref, job_config=job_config)
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\bigquery\client.py", line 1461, in load_table_from_file
file_obj, job_resource, num_retries
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\bigquery\client.py", line 1756, in _do_resumable_upload
stream, metadata, num_retries
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\bigquery\client.py", line 1799, in _initiate_resumable_upload
transport, stream, metadata, _GENERIC_CONTENT_TYPE, stream_final=False
File "C:\Users\masato\Documents\python\lib\site-packages\google\resumable_media\requests\upload.py", line 351, in initiate
retry_strategy=self._retry_strategy,
File "C:\Users\masato\Documents\python\lib\site-packages\google\resumable_media\requests\_helpers.py", line 116, in http_request
return _helpers.wait_and_retry(func, RequestsMixin._get_status_code, retry_strategy)
File "C:\Users\masato\Documents\python\lib\site-packages\google\resumable_media\_helpers.py", line 150, in wait_and_retry
response = func()
File "C:\Users\masato\Documents\python\lib\site-packages\google\auth\transport\requests.py", line 216, in request
method, url, data=data, headers=request_headers, **kwargs
File "C:\Users\masato\Documents\python\lib\site-packages\requests\sessions.py", line 533, in request
resp = self.send(prep, **send_kwargs)
File "C:\Users\masato\Documents\python\lib\site-packages\requests\sessions.py", line 646, in send
r = adapter.send(request, **kwargs)
File "C:\Users\masato\Documents\python\lib\site-packages\requests\adapters.py", line 498, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(10054, '既存の接続はリモート ホストに強制的に切断されました。', None, 10054, None))
▽以下は15分以上の長いループで実行したときに発生
def job():
for i in (スクレイピングする種類の一覧):
for x in range(スクロール&データ取得のループ回数):
UploadCSVtoBigquery(...)
________________________________________________________________
ログ
quit driver
upload to BigQuery
Traceback (most recent call last):
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 672, in urlopen
chunked=chunked,
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 421, in _make_request
six.raise_from(e, None)
File "<string>", line 3, in raise_from
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 416, in _make_request
httplib_response = conn.getresponse()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 1336, in getresponse
response.begin()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 306, in begin
version, status, reason = self._read_status()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 267, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "C:\Users\masato\Documents\python\lib\socket.py", line 589, in readinto
return self._sock.recv_into(b)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 1071, in recv_into
return self.read(nbytes, buffer)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 929, in read
return self._sslobj.read(len, buffer)
ConnectionResetError: [WinError 10054] 既存の接続はリモート ホストに強制的に切断されました。
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\masato\Documents\python\lib\site-packages\requests\adapters.py", line 449, in send
timeout=timeout
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 720, in urlopen
method, url, error=e, _pool=self, _stacktrace=sys.exc_info()[2]
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\util\retry.py", line 400, in increment
raise six.reraise(type(error), error, _stacktrace)
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\packages\six.py", line 734, in reraise
raise value.with_traceback(tb)
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 672, in urlopen
chunked=chunked,
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 421, in _make_request
six.raise_from(e, None)
File "<string>", line 3, in raise_from
File "C:\Users\masato\Documents\python\lib\site-packages\urllib3\connectionpool.py", line 416, in _make_request
httplib_response = conn.getresponse()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 1336, in getresponse
response.begin()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 306, in begin
version, status, reason = self._read_status()
File "C:\Users\masato\Documents\python\lib\http\client.py", line 267, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "C:\Users\masato\Documents\python\lib\socket.py", line 589, in readinto
return self._sock.recv_into(b)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 1071, in recv_into
return self.read(nbytes, buffer)
File "C:\Users\masato\Documents\python\lib\ssl.py", line 929, in read
return self._sslobj.read(len, buffer)
urllib3.exceptions.ProtocolError: ('Connection aborted.', ConnectionResetError(10054, '既存の接続はリモート ホストに強制的に切断されました。', None, 10054, None))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\masato\Documents\python\lib\site-packages\google\auth\transport\requests.py", line 123, in __call__
method, url, data=body, headers=headers, timeout=timeout, **kwargs
File "C:\Users\masato\Documents\python\lib\site-packages\requests\sessions.py", line 533, in request
resp = self.send(prep, **send_kwargs)
File "C:\Users\masato\Documents\python\lib\site-packages\requests\sessions.py", line 646, in send
r = adapter.send(request, **kwargs)
File "C:\Users\masato\Documents\python\lib\site-packages\requests\adapters.py", line 498, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(10054, '既存の接続はリモート ホストに強制的に切断されました。', None, 10054, None))
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "main_avoidSecurityError.py", line 72, in <module>
schedule.run_pending()
File "C:\Users\masato\Documents\python\lib\site-packages\schedule\__init__.py", line 563, in run_pending
default_scheduler.run_pending()
File "C:\Users\masato\Documents\python\lib\site-packages\schedule\__init__.py", line 94, in run_pending
self._run_job(job)
File "C:\Users\masato\Documents\python\lib\site-packages\schedule\__init__.py", line 147, in _run_job
ret = job.run()
File "C:\Users\masato\Documents\python\lib\site-packages\schedule\__init__.py", line 466, in run
ret = self.job_func()
File "main_avoidSecurityError.py", line 59, in job
UploadCSVtoBigquery(table_date)
File "C:\Users\masato\Documents\GitHub\yahoo_scraping\uploadCSVtoBigquery.py", line 34, in __init__
job = client.load_table_from_file(source_file, table_ref, job_config=job_config)
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\bigquery\client.py", line 1461, in load_table_from_file
file_obj, job_resource, num_retries
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\bigquery\client.py", line 1756, in _do_resumable_upload
stream, metadata, num_retries
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\bigquery\client.py", line 1799, in _initiate_resumable_upload
transport, stream, metadata, _GENERIC_CONTENT_TYPE, stream_final=False
File "C:\Users\masato\Documents\python\lib\site-packages\google\resumable_media\requests\upload.py", line 351, in initiate
retry_strategy=self._retry_strategy,
File "C:\Users\masato\Documents\python\lib\site-packages\google\resumable_media\requests\_helpers.py", line 116, in http_request
return _helpers.wait_and_retry(func, RequestsMixin._get_status_code, retry_strategy)
File "C:\Users\masato\Documents\python\lib\site-packages\google\resumable_media\_helpers.py", line 150, in wait_and_retry
response = func()
File "C:\Users\masato\Documents\python\lib\site-packages\google\auth\transport\requests.py", line 212, in request
self._auth_request, method, url, request_headers
File "C:\Users\masato\Documents\python\lib\site-packages\google\auth\credentials.py", line 124, in before_request
self.refresh(request)
File "C:\Users\masato\Documents\python\lib\site-packages\google\oauth2\service_account.py", line 334, in refresh
access_token, expiry, _ = _client.jwt_grant(request, self._token_uri, assertion)
File "C:\Users\masato\Documents\python\lib\site-packages\google\oauth2\_client.py", line 146, in jwt_grant
response_data = _token_endpoint_request(request, token_uri, body)
File "C:\Users\masato\Documents\python\lib\site-packages\google\oauth2\_client.py", line 105, in _token_endpoint_request
response = request(method="POST", url=token_uri, headers=headers, body=body)
File "C:\Users\masato\Documents\python\lib\site-packages\google\auth\transport\requests.py", line 128, in __call__
six.raise_from(new_exc, caught_exc)
File "<string>", line 3, in raise_from
google.auth.exceptions.TransportError: ('Connection aborted.', ConnectionResetError(10054, '既存の接続はリモート ホストに強制的に切断されました。', None, 10054, None))
対処した内容
これにはかなり詰まりました。
ジョブのループ実行をpythonの「shcedule」ライブラリで行っていたが、 WindowsOSの「タスクスケジューラー」 を使うことで再現しなくなりました。
課題
もっとちゃんとした方法があると思いたいので、これは暫定対処という扱いです。勉強兼ねて根本原
因・解決方法を今後調べます。
(また、タスクスケジューラーでの実行でエラーになっても、少なくともライブラリ「pysnooper」ではログが吐き出されなかった。このままだとエラーを検知できないので暇なときにでも確認したい)
@pysnooper.snoop(r"C:\Users\...\log.txt")```
# 環境変数がリセットされる
## 前提
BigQuery(またはCloudStorage)にデータを送るためにはそれらのAPIクライアントを使うための認証を通す必要がある。そのために認証キーのパスを設定することが前提。
>環境変数 GOOGLE_APPLICATION_CREDENTIALS を設定して、アプリケーション コードに認証情報を指定します。
[BigQuery クライアント ライブラリ>認証の設定](https://cloud.google.com/bigquery/docs/reference/libraries?hl=ja#setting_up_authentication
)
## 問題
しかし、上記公式ドキュメントに記載の通り、
>この変数は現在のシェル セッションにのみ適用されるため、新しいセッションを開く場合は、変数を再度設定してください。
なのでセッションが切れた場合は、以下のように「GOOGLE![undefined]()
_APPLICATION_CREDENTIALS」を環境変数としてセットしていないときのエラーが出ます。
C:\Users\masato\Documents\GitHub\yahoo_scraping>py uploadCSVtoBigquery.py
Traceback (most recent call last):
File "uploadCSVtoBigquery.py", line 2, in
client = bigquery.Client()
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\bigquery\client.py", line 176, in init
project=project, credentials=credentials, _http=_http
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\client.py", line 226, in init
_ClientProjectMixin.init(self, project=project)
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\client.py", line 178, in init
project = self._determine_default(project)
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud\client.py", line 193, in _determine_default
return _determine_default_project(project)
File "C:\Users\masato\Documents\python\lib\site-packages\google\cloud_helpers.py", line 186, in _determine_default_project
_, project = google.auth.default()
File "C:\Users\masato\Documents\python\lib\site-packages\google\auth_default.py", line 321, in default
raise exceptions.DefaultCredentialsError(_HELP_MESSAGE)
google.auth.exceptions.DefaultCredentialsError: Could not automatically determine credentials. Please set GOOGLE_APPLICATION_CREDENTIALS or explicitly create credentials and re-run the application. For more information, please see https://cloud.google.com/docs/authentication/getting-started
今回はループ実行をpythonの「shcedule」ライブラリからWindowsOSの「タスクスケジューラー」を使うことにより、ジョブの定期実行ごとにコマンドプロンプトのウィンドウが立ち上がるようになり、つまりはセッションがリセットされるようになりました。
また副産物として、Windowsで搭載されているツールなのでログオフ時の挙動やジョブ実行時にスリープを解除したりといったOSの制御も可能になりました。こういったものはシステム的に役割分離したほうが良いのかな、というのが学びです。
## 対処した内容
実行ファイルで認証キーのパスを設定する方針を立てました。
クライアントを立てる前に **osライブラリ** で認証キーのパスを指定することで認証が通りました。
import os
from google.cloud import bigquery
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = r'C:\Users\masato\Documents\GitHub\credentials\bananapj-adc1fd3f90dd.json'
client = bigquery.Client()
## 課題
特になし
---
何かあればお手数ですが、本記事か以下アカウントでお知らせください!
**\ Follow Me! /**
[**Qiitaアカウント**](https://qiita.com/MasatoEmata)
[**Twitterアカウント**](https://twitter.com/masatoemata)