②オーケーネットスーパー 自動でカートに入れる

開発経緯

前回エクセルで開発したが、色々な場所からアクセスできなく不便なのでスプレッドシートでリストを作成するように仕様変更した。

スプレッドシートの認証はほかのブログを参考にしてほしい。
参考にしたURL↓
https://tanuhack.com/operate-spreadsheet/
https://tanuhack.com/library-gspread/

開発環境

概要

前回とほとんど変わりませんがエクセルをスプレッドシートに変更しました。
【自動】OKネットスーパーのWEBページを開いてログイン情報を記入する。
【手動】reCAPTHAは手動で操作する。
【自動】スプレッドシートで買い物リストを作成して、そのリストに従いOKネットスーパーで検索して一番目に出てきたものをカートに入れる。
【手動】カートに入った買い物するものを確認し、配送日を選んで決済へ!!!

ソースコード

oknet221031.py

# coding:utf-8
from multiprocessing.resource_sharer import stop
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome import service as fs
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from time import sleep
import os
import signal
import gspread
from google.oauth2.service_account import Credentials
import yaml

with open('config.yml', 'r') as yml:
    config = yaml.safe_load(yml)
    myid = config['myid']
    mypass = config['mypass']
    SPREADSHEET_KEY  = config['SPREADSHEET_KEY']
    CHROMEDRIVER = config['CHROMEDRIVER']

def is_int(s):
    try:
        int(s)
        return True
    except ValueError:
        return False


#スプレッドシート認証
scope = ['https://www.googleapis.com/auth/spreadsheets','https://www.googleapis.com/auth/drive']
credentials = Credentials.from_service_account_file("credentials.json", scopes=scope)
gc = gspread.authorize(credentials)

# スプレッドシート(ブック)を開いて前準備
ws = gc.open_by_key(SPREADSHEET_KEY).sheet1
product_arry = ws.col_values(5)
value_arry = ws.col_values(2)
product_dict=(dict(zip(product_arry,value_arry)))

# ブラウザを開く。
chrome_service = fs.Service(executable_path=CHROMEDRIVER)
driver = webdriver.Chrome(service=chrome_service)
wait = WebDriverWait(driver=driver, timeout=30)

# OKネットスーパーのlogin画面を開く
driver.get("https://ok-netsuper.com/login")
wait.until(EC.presence_of_all_elements_located)

# ログイン操作
mail = driver.find_element(By.ID, "username")
password = driver.find_element(By.ID, "password")
mail.send_keys(myid)
password.send_keys(mypass)
sleep(20)
# reCAPTHA認証を人が操作する時間待機


# 配送日時のページを開く
driver.get("https://ok-netsuper.com/delivery-timetable/delivery-timetable-select")
wait.until(EC.presence_of_all_elements_located)
# 配送日時選択
driver.find_element(By.XPATH,"/html/body/div[2]/div[1]/div[2]/div/form/table/tbody/tr[3]/td[2]").click()
driver.find_element(By.ID,"continue-shopping").click()
sleep(1)

# タブを開く
driver.execute_script("window.open();")
# 開いたタブをアクティブにする
driver.switch_to.window(driver.window_handles[1])
# okホーム開く
driver.get("https://ok-netsuper.com/")
wait.until(EC.presence_of_all_elements_located)


try:
    for  product_name in product_arry:
        value = product_dict[product_name]
        if is_int(value):
            # 商品を検索欄選択
            search_product = driver.find_element(By.CSS_SELECTOR, "input.header-bottom-search-input")
            # 検索欄をクリア
            search_product.clear()
            # 検索欄に商品名を記入
            search_product.send_keys(product_name)
            # ENTERを押す
            search_product.send_keys(Keys.ENTER)
            wait.until(EC.presence_of_all_elements_located)
            # print(product_name)
            # print(value)
            try:
                for j in range(int(value)):
                    # 検索結果の一番目を個数分カートに入れる
                    driver.find_elements(By.CSS_SELECTOR,".ok-button")[4].click()
                    sleep(1)
            except IndexError:
                    # none_link = none_link + " " + product_name
                    print(product_name)
finally:
    os.kill(driver.service.process.pid,signal.SIGTERM)

driver.close
driver.quit

config.yml

myid : YOUR ID
mypass : YOUR PASSWORD
SPREADSHEET_KEY : APIKEY
CHROMEDRIVER : ドライバーのpath

改良したところ

sleepで時間指定でwebページが読み込みを待っていたのを、全要素が読み込まれるのまで待つに変更した。
ここでは30秒でタイムアウトすると設定した。

wait = WebDriverWait(driver=driver, timeout=30)
wait.until(EC.presence_of_all_elements_located)

設定の値をyamlに書いて呼び出すようにしコードを公開しやすくした。

引っかかったところ

  • API制限に引っかかる
    前回作成したプログラムと同様にスプレッドシートを一行一行処理していたらAPIの制限に引っかかってしまった。
    APIを叩く回数を減らせば解決できると考え、商品名と個数を配列で一括で取得することで解決した。
    配列で取得した商品名と個数を辞書形式に変換して、その辞書から商品名と付き合わせて個数を取得した。
  • データ型
    スプレッドシートから取得した個数のデータ型がintじゃなった。スプレッドシートでは数字は数値になっていると思い込んでしまったため気づくまでに時間がかかった。
    結局int()をやれば解決した。

現在の課題と今後の展望

  • 検索結果が出なかった時に別シートに商品名を出力するように変更したい。
  • 結果が2個以上でた際の処理を考える。
  • pythonを外出先からも起動できるよう考える。
  • reCAPTCHAのチェックが付いたら次の処理をできるようにしたい。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です