PythonでSeleniumとopenpyxlを使い、EXCELの商品リストから自動でカートに入れる

#概要
大学のロボット部で部品を買うのですが、管理をしやすくするために個人でバラバラに買わずに購入品目をEXCELでまとめて購入担当にリストを渡すことで購入してもらっています。購入品をまとめる作業もリストからURLを一つ一つ通販サイトにコピーしてカートに入れるのも数が多くて面倒くさい!ということで、今回は既に作ってある商品リストのEXCELファイルを読み込ませて自動でカート追加まで行うプログラムを作成してみました。
簡単に書けそうだったので言語はpythonにしました。

#環境
ASUS zenbook UX310U
Windows10 home (64bit)
CPU Intel core i5 7200U
Google Chrme 81.0.4044.92
ChromeDriver　81.0.4044.69
Python 3.7.3(Anaconda)
エディタ Sublime Text3
selenium
openpyxl

#環境構築手順
Python 環境は Anaconda3です。Anaconadaをインストールしていない人はインストールしましょう。
##必要パッケージのインストール
[こちら](https://qiita.com/memakura/items/20a02161fa7e18d8a693)を参考に必要パッケージおよびドライバーをインストールしました。

##seleniumインストール
まずはanaconda promptを起動して新しく仮想環境を作ります。既にインストール済みのパッケージの依存関係によってはエラーが出ることがあるので新しい仮想環境にインストールすることを勧めます。

```
conda create -n selenium
```
仮想環境の切り替え

```
conda activate selenium
```

インストール

```
conda install selenium
```
##ChromeDriver のインストール

今回はpipでインストールし、パス通さないでスクリプト内でインポートします。
https://qiita.com/hanzawak/items/2ab4d2a333d6be6ac760
を参考にしてインストールを進めていけばできます。
以下に一応やった手順を示します。
google chromeのバージョンを確認すると81.0.4044.92だったので[ChromeDriverのダウンロードページ](http://chromedriver.chromium.org/downloads)で自分のchromeバージョンに近い番号を探してインストールします。(バージョンが下で近いものがいいかもしれません)

```
pip install chromedriver-binary==81.0.4044.69
```

ちなみに今回はpipでインストールしましたが、[こちらのサイト](https://qiita.com/memakura/items/20a02161fa7e18d8a693)にバイナリでダウンロードする方法も書いてあります。
##openpyxlのインストール
pythonでEXCELファイルを読み書きするパッケージです。詳しい使い方は[この方の記事](https://note.nkmk.me/python-openpyxl-usage/)が参考になると思います。
以下のコマンドでインストールします。

```
conda install openpyxl
```

#ExcelファイルにあるURLの取得
ひとまずExcelファイル内にある全てのURLと購入個数と購入場所の取得までやってみます。予めURLと購入個数のセルは決まっているので指定してリストにしています。何も書かれていないセルでエラーが出るのを防ぐためにfor文で取得しています。

![buy_excel.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/616291/b30eff6f-05fe-125f-69cf-0ecf433821f5.png)
今回は自作した写真にあるようなリストを使います。

```add_cart.py

import sys
from argparse import ArgumentParser as Parser
import openpyxl

parser = Parser(description='EXCELに書かれた商品リストを自動的にカートに入れるスクリプト')
parser.add_argument("path",type=str,help='excelファイルのパスを指定')
args = parser.parse_args()
wb = openpyxl.load_workbook(args.path)
sheet = wb['Sheet1']
url=[]
buy_quan=[]
site=[]
for sell in sheet.iter_rows(min_row=2,max_col=8):
	values = []
	for col in sell:
		values.append(col.value)
	if values[7] != None:
		url.append(values[7])
	if values[6] != None:
		buy_quan.append(values[6])
	if values[3] != None:
		site.append(values[3])


print(url)
print(buy_quan)
print(state)

```

実行するときは以下のコマンドです。引数にはExcelのファイルパスを指定します。実行してURL、購入個数、購入場所の順番でリストが表示されれば成功です。

```
python listup_from_excel.py filepass
```

#URL表示からカート追加まで
先程のプログラムの続きです。
今回は秋月電子の通販サイトでやってみます。まず、適当な商品ページで購入数量のテキストボックスを右クリックし、「検証」をクリックしてHTMLの要素を表示します。ハイライトされている部分で右クリックし、Xpathをコピーします。
詳しい方法は以下で解説されています。
https://youtu.be/AyicRGWa_70

``driver.find_element_by_xpath('xpass')
``
の引数にコピーしたXpathを貼り付けます。ちなみにXpathはシングルクォーテーションで囲みます。ダブルクオーテーションはだめです。

```add_cart.py
from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome()#chromeを開く
driver.get('http://akizukidenshi.com/catalog/g/gM-06187/')
wait=WebDriverWait(driver, 10)
#購入数量のxpathを指定する
element = driver.find_element_by_xpath('//*[@id="maincontents"]/div[2]/table/tbody/tr/td[3]/input')
element.clear()
element.send_keys("6")
element.send_keys(Keys.RETURN)
```
最後の```element.send_keys(Keys.RETURN)```は購入数量を入力した状態でEnterキーを押すとそのままカートに入るので横着しています。しかし、このままだと在庫切れでテキストボックスが表示されなかったときにカートに入れられません。

#全体のコード
使い回せるようにクラスにまとめました。正常にカートに追加できなかったときのために例外処理を入れています。

```add_from_excel.py
from argparse import ArgumentParser as Parser
import openpyxl

from selenium import webdriver
import chromedriver_binary
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.keys import Keys
url = []#URL
buy_quan = []#購入数量
site = []#購入サイト名
prduct_name = []#製品名
driver = webdriver.Chrome()# chromeを開く

def ListXlsElemet(url_row, buy_quan_row, site_row, proname_row): 
    parser = Parser(description='EXCELに書かれた商品リストを自動的にカートに入れるスクリプト')
    parser.add_argument("path", type=str, help='excelファイルのパスを指定')
    args = parser.parse_args()
    wb = openpyxl.load_workbook(args.path)
    sheet = wb['Sheet1']
    for sell in sheet.iter_rows(min_row=2, max_col=8):
        values = []
        for col in sell:
            values.append(col.value)
        if values[url_row] != None:
            url.append(values[url_row])
        if values[buy_quan_row] != None:
            buy_quan.append(values[buy_quan_row])
        if values[site_row] != None:
            site.append(values[site_row])
        prduct_name.append(values[proname_row])


class GoToCart:
    def __init__(self, arg_url, arg_quan, arg_site,arg_proname):
   
        self.url = arg_url
        self.buy_quan = arg_quan
        self.site = arg_site
        self.proname=arg_proname
    def CharClassifi(self, ch, target):  # 特定の文字列を含むかどうかを判定し、一致したらリストに追加する関数
        relist = []
        num = 0
        for char in self.site:
            if str(char) == ch:
                relist.append(target[num])

            num = num + 1
        return relist

    # サイト名とxpthsを渡すと必要数分カートに入れる関数
    def AddCart(self, site_name, num_xpath, cart_xpath):
        temp_url = self.CharClassifi(site_name, self.url)
        temp_quan = self.CharClassifi(site_name, self.buy_quan)
        temp_proname = self.CharClassifi(site_name, self.proname)
        num = 0
        helth=0
        for t_url in temp_url:
            try:
                driver.get(t_url)
                wait = WebDriverWait(driver, 10)
                # 購入数量のxpathを指定する
                t_element = driver.find_element_by_xpath(num_xpath)
                t_element.clear()
                t_element.send_keys(str(temp_quan[num]))
                c_element = driver.find_element_by_xpath(cart_xpath)#カートボタンのxpath
                c_element.click()
                
                
            except:
                    print(temp_proname[num])
                    print("カートに入れられません。商品が存在しないか在庫がない可能性があります")
                    continue
            num = num+1

class ChildGotoCart(GoToCart):
    def Akiduki(self):
        self.AddCart( "秋月", '//*[@id="maincontents"]/div[2]/table/tbody/tr/td[3]/input','//*[@id="maincontents"]/div[2]/table/tbody/tr/td[4]/input')

    def Sengoku(self):
        driver.execute_script("window.open()")#新規ウィンドウオープン
        driver.switch_to.window(driver.window_handles[1])#新規タブに切り替え
        self.AddCart("千石",'//*[@id="detail_price"]/tbody/tr/td[2]/input','//*[@id="detail_price"]/tbody/tr/td[3]/input')
    
def main():
    buy_handle = ChildGotoCart(url, buy_quan, site,prduct_name)
    ListXlsElemet(7, 6, 3,2)
    buy_handle.Akiduki()
    buy_handle.Sengoku()
    print("完了しました")
    return 0


if __name__ == "__main__":
    main()
```
##関数の使い方
```ListXlsElemet(URL,購入数量,購入サイト名,製品名)``` 各対応するExcelの列番号が入ります。一番左列から0です。
```AddCart(Excelに書いてあるサイト名,購入数量のXpath,カートボタンのXpath) ```
他のサイトを追加したい場合は```ChildGotoCart```クラスに追加して```main()```から呼び出すだけです。

実行するにはanadonda promptで引数にexcelのファイルパスを指定して実行します。

```
(selenium)C:\Users\username>python buy_from_excel.py filefass
```


#その他参考
[seleniumクイックリファレンス](https://www.seleniumqref.com/api/webdriver_gyaku.html)
[seleniumでリンクを別タブで開く](https://nade-nadegata.hatenablog.jp/entry/2018/09/13/001637)