J'ai utilisé du sélénium pour automatiser l'estampage des boîtes de travail et l'ai déployé sur Google Cloud Functions. Je vais décrire les points auxquels j'étais accro à ce moment-là.
A travaillé chez SIer à Tokyo Ingénieur RPA → Prise en charge de l'introduction PoC de solutions IA, etc.
Je fais aussi Twitter ↓ https://twitter.com/m_schna2
Il a la configuration suivante. Lorsque je l'ai essayé, il a fallu un certain temps pour associer Google Drive et Cloud Functions.
L'URL id pw est intégrée, mais veuillez la modifier en conséquence.
main.py
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.chrome.options import Options
import jpholiday
import datetime
import requests, json
import os, time
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
import shutil
def punchClockWrapper(request):
def isBizDay():
dt_now = datetime.datetime.now()
dt_now = utc_to_jst(dt_now)
DATE = dt_now.strftime('%Y%m%d')
Date = datetime.date(int(DATE[0:4]), int(DATE[4:6]), int(DATE[6:8]))
if Date.weekday() >= 5 or jpholiday.is_holiday(Date):
return 0
else:
return 1
def punchClock():
#constant
url_jobcan = 'https://id.jobcan.jp/users/~~~~~~~~~~'
id = '~~~~~~~~~~~~~'
pw = '~~~~~~~~~~~~~'
#Paramétrage des options
options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--window-size=1280x1696')
options.add_argument('--no-sandbox')
options.add_argument('--hide-scrollbars')
options.add_argument('--enable-logging')
options.add_argument('--log-level=0')
options.add_argument('--v=99')
options.add_argument('--single-process')
options.add_argument('--ignore-certificate-errors')
#Fonctionnement backend de l'interface graphique
options.binary_location = os.getcwd() + "/headless-chromium"
driver = webdriver.Chrome(os.getcwd() + "/chromedriver", options=options) #Chargez le pilote qui exécute Chrome
#Ouvrir Job Can
driver.get(url_jobcan)
#Entrée e-mail
text = driver.find_element_by_id("user_email")
text.send_keys(id)
#saisie du mot de passe
text = driver.find_element_by_id("user_password")
text.send_keys(pw)
#Cliquez sur Connexion
btn = driver.find_element_by_name("commit")
btn.click()
#Cliquez sur le bouton tampon
btn = driver.find_element_by_id("adit-button-push")
btn.click()
#Attendez 8 secondes
time.sleep(8)
#ferme la fenêtre
driver.quit()
def toSlack():
WEB_HOOK_URL = "https://hooks.slack.com/services/~~~~~~~~~"
dt_now = datetime.datetime.now()
dt_now = utc_to_jst(dt_now)
dt_punch = dt_now.strftime('%Y année%m mois%jour j%H:%M:%S')
requests.post(WEB_HOOK_URL, data = json.dumps({
'text': str(dt_punch)+u'Estampage terminé', #Contenu de la notification
'username': '~~~~~~', #Nom d'utilisateur
'icon_emoji': u':smile_cat:', #icône
'link_names': 1, #Noms de lien
}))
def utc_to_jst(timestamp_utc):
timestamp_jst = timestamp_utc.astimezone(datetime.timezone(datetime.timedelta(hours=+9)))
return timestamp_jst
def writeLog(message):
dt_now = datetime.datetime.now()
dt_now = utc_to_jst(dt_now)
dt_punch = dt_now.strftime('%Y année%m mois%jour j%H:%M:%S')
with open(log_path, 'a') as f:
print(str(dt_punch)+u' '+message, file=f)
def downloadLog():
f = drive.CreateFile(drive_param)
f.GetContentFile(log_path)
def uploadLog():
f = drive.CreateFile(drive_param)
f.SetContentFile(log_path)
f.Upload()
#Réglage constant
log_path = "/tmp/punch_clock.log"
drive_param = {
'parents': [{
'id':'~~~~~~~~~~~'
}],
'id': '~~~~~~~~~~~',
'title': 'punch_clock.log'
}
#déplacer dir(settings.Puisque yaml est lu à partir du répertoire d'exécution, la valeur par défaut est le répertoire actuel)
#Doit être stocké dans un répertoire doté d'autorisations de lecture et d'écriture
os.chdir(os.path.dirname(os.path.abspath(__file__)))
shutil.copy2("credentials.json","/tmp/credentials.json")
#Authentification
gauth = GoogleAuth()
gauth.CommandLineAuth()
drive = GoogleDrive(gauth)
#Sous le traitement principal
downloadLog()#Télécharger les journaux depuis Google Drive
writeLog('Commencer le traitement') #Démarrer le journal
flg = isBizDay() #Jugement en semaine(Jours de semaine: 1, jours fériés: 0)
if flg == 1 :
punchClock() #Estampillage
toSlack() #Notification Slack
writeLog('Estampage terminé') #Fin du journal
else :
writeLog('Je ne l'ai pas tamponné car c'est un jour férié') #Fin du journal
uploadLog() #Télécharger les journaux sur Google Drive
return 'Terminé'
selenium Il s'agit du processus d'estampage principal (Punch Clock). Dans le code, je crains d'attendre 8 secondes après le marquage, mais je n'ai jamais manqué de tamponner, donc c'est probablement normal.
Notifier à l'aide de l'API webhook. Il y a des étapes pour les paramètres d'API à l'URL suivante. https://slack.com/intl/ja-jp/help/articles/115005265063-Slack-%E3%81%A7%E3%81%AE-Incoming-Webhook-%E3%81%AE%E5%88%A9%E7%94%A8#incoming-webhook-u12398u35373u23450
En ce qui concerne la méthode de déploiement, mettez tout le code et les modules de bibliothèque nécessaires dans le même dossier et téléchargez avec le code suivant, etc.
gcloud functions deploy punchClockWrapper --runtime python37 --trigger-http --region asia-northeast1 --memory 512MB
Ici, reportez-vous à l'URL suivante. https://blowup-bbs.com/gcp-cloud-functions-python3/
Pour ajouter un journal à Google Drive Téléchargez le journal précédent → Ajouter → Télécharger Cependant, étant donné que Google Drive gère par identifiant de fichier, même si vous importez avec le même nom, il sera téléchargé en tant que fichier distinct avec le même nom au lieu d'être écrasé par défaut. Par conséquent, il peut être écrasé en spécifiant l'ID du dossier parent et l'ID du fichier journal, mais l'ID du fichier journal ne peut-il pas être obtenu par une opération GUI normale? J'ai tourné le code ci-dessous pour vérifier l'identifiant.
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
import pprint
#Authentification
gauth = GoogleAuth()
gauth.CommandLineAuth()
drive = GoogleDrive(gauth)
#acquisition de données de fichier
file_id = drive.ListFile({'q':'title="punch_clock.log"'}).GetList()[0]['id']
f = drive.CreateFile({'id':file_id})
#Affichage des métadonnées du fichier
f.FetchMetadata()
pprint.pprint(f)
En passant, vous pouvez vérifier l'identifiant du dossier à partir de la partie url en l'ouvrant avec un navigateur. Vous pouvez vérifier la méthode sur le lien ci-dessous. https://tonari-it.com/gas-google-drive-file-folder-id/
PyDrive est une bibliothèque d'encapsuleurs pour l'API Google Drive. Au début, une authentification interactive est requise, mais json est créé après l'authentification réussie, et le json est référencé à partir de la deuxième fois, de sorte qu'il peut être incorporé dans le code pour automatiser Google Drive. Il y a peu de situations de code et c'est très soigné.
#Authentification
gauth = GoogleAuth()
gauth.CommandLineAuth()
drive = GoogleDrive(gauth)
C'est juste le comportement au moment de l'authentification Voir settings.yaml dans le répertoire courant → Reportez-vous au chemin spécifié dans save_credentials_file → Si json existe, l'authentification réussit et écrase json. Sinon, l'authentification est demandée avec du code et si du code est entré, json est créé dans le même chemin spécifié. Il faut être conscient de ce point.
Bien sûr, vous ne pouvez pas vous authentifier de manière interactive après le déploiement sur GCF. Je m'authentifie donc localement à l'avance, crée un fichier json et le stocke ensemble dans le dossier à déployer, mais dans GCF, le répertoire inscriptible est limité à / tmp. Et comme json est écrit (écrasé) même lorsque l'authentification est réussie, vous devez spécifier le chemin /tmp/~~~.json dans save_credentials_file. Ainsi, dans le code (sous le code), credentials.json est copié du répertoire de déploiement vers / tmp. Et dans les paramètres.yaml déployés ensemble, /tmp/credentials.json est spécifié dans l'élément save_credentials_file. À propos, le fichier settings.yaml de PyDrive doit être dans le répertoire courant, il a donc été déplacé vers le répertoire d'exécution du fichier.
#déplacer dir(settings.Puisque yaml est lu à partir du répertoire d'exécution, la valeur par défaut est le répertoire actuel)
#Doit être stocké dans un répertoire doté d'autorisations de lecture et d'écriture
os.chdir(os.path.dirname(os.path.abspath(__file__)))
shutil.copy2("credentials.json","/tmp/credentials.json")
Étant donné que la région par défaut du travailleur démarré par GCF est us-central1, qui est à 9 heures de congé de l'heure du Japon, l'heure décrite dans la sortie du journal et la notification Slack est corrigée par la fonction suivante. (J'aurais dû me déployer avec asia-north1 cette fois, mais ça change encore pour une raison quelconque ...)
def utc_to_jst(timestamp_utc):
timestamp_jst = timestamp_utc.astimezone(datetime.timezone(datetime.timedelta(hours=+9)))
return timestamp_jst
--embed id, pw, url C'est une faible priorité, mais j'aimerais l'améliorer un jour.
https://stackoverflow.com/questions/51487885/tmp-file-in-google-cloud-functions-for-python https://blowup-bbs.com/gcp-cloud-functions-python3/ https://dev.classmethod.jp/articles/python-time-string-timezone/ https://qiita.com/wezardnet/items/34b89276cdfc66607727
Recommended Posts