end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

microsoft listに添付されたexcelファイルをseleniumでダウンロード

Office365 (microsoft365)のmicrosoft listに添付されたexcelのリンクをクリックすると、 ファイルダウンロードにはならず、まずは、excel for webで起動されます。

selenium for pythonexcel for web からexcelファイルのダウンロードを 試みましたが、なぜか find_elements(By.CSS_SELECTOR)で elementを取得できない。

どうやら、excel for web の画面で用いられている iframe の影響らしい。

そこで、switch_to.frame() で iframe内へ切り替えることで解消。

python scriptとしては、おおよそ以下のような感じです。

#!python
# -*- coding: utf-8 -*-
from datetime import datetime
from selenium import webdriver
from selenium.webdriver.common.by    import By
from selenium.webdriver.common.keys  import Keys
from selenium.webdriver.edge.options import Options
from selenium.webdriver.edge.service import Service
from selenium.webdriver.support.ui   import Select
import subprocess
import sys
import time

CONF = {
    "browser": {
        "browser_driver" : ".\\msedgedriver.exe",
        "implicitly_wait": 10,
        "browser_options": [
            #"headless",
        ],
        "prefs": {
          #ファイルのダウンロード先
            "download.default_directory":"c:\\Users\\xcend0tknr\\tmp"
        }
    },
    "xmile_sso":{
        "url"      :"https://portaltop.xmile.sexy.co.jp/",
        "user_id"  :"ないしょ@id.sexy-g.com",
        "user_pw"  :"ないしょ",
        "proxy_pac":"http://portal.xmile.sexy.co.jp/proxy.pac"
    },
    "o365":{
        "qa_list_url":
        "https://sexyglobal-my.sharepoint.com/personal/ないしょ_id_sexy-g_com/Lists/A/AllItems.aspx"
}
}

def main():
    set_proxy_pac("off")
    browser = init_browser()

    login_to_xmile(browser)
    time.sleep(5)
    
    # microsoft listsにある問合せ一覧へ
    browser.get( CONF["o365"]["qa_list_url"] )
    time.sleep(10)

    # 各問合せへのurlを取得
    a_href_elms = browser.find_elements(By.CSS_SELECTOR,
                                        "div.field-_x8cea__x554f__x5185__x5bb9_ a")
    qa_urls = []
    for a_href_elm in a_href_elms:
        qa_urls.append( a_href_elm.get_attribute("href") )

    # 各問合せにある添付fileへののurlを取得
    for qa_url in qa_urls:
        browser.get( qa_url )
        time.sleep(5)

        attach_elms = browser.find_elements(
            By.CSS_SELECTOR, "div.ReactFieldEditor-Attachments-Renderer a")
        
        attach_urls = []
        for attach_elm in attach_elms:
            attach_url = attach_elm.get_attribute("href")
            attach_urls.append(attach_url)

        for attach_url in attach_urls:
            browser.get( attach_url )
            time.sleep(5)
            # excel for webは iframeで構成される為、そのiframeへ切替え
            browser.switch_to.frame(0)
            time.sleep(5)
            # excel for web画面の「ファイル」タブclick
            btn_elms =browser.find_elements(By.CSS_SELECTOR,"span#id__3")
            btn_elms[0].click()
            time.sleep(5)
            
            #browser.switch_to.default_content()
            
            # 「名前を付けて保存」click
            btn_elms =browser.find_elements(By.CSS_SELECTOR,"#FileSaveAsPage")
            btn_elms[0].click()
            time.sleep(5)
            # 「ダウンロード」click
            btn_elms =browser.find_elements(By.CSS_SELECTOR,"#DownloadACopy")
            btn_elms[0].click()
            time.sleep(5)
            break
    
def login_to_xmile(browser):
    browser.get( CONF["xmile_sso"]["url"] )

    button_elms = browser.find_elements(By.CSS_SELECTOR,"button.aad_login_button")
    button_elms[0].click()
    
    time.sleep( 2 )
    input_elms = browser.find_elements(By.CSS_SELECTOR,"input[name='loginfmt']")
    input_elms[0].send_keys( CONF["xmile_sso"]["user_id"] )
    time.sleep( 2 )
    
    input_elms[0].send_keys(Keys.ENTER)
    time.sleep( 2 )

    input_elms = browser.find_elements(By.CSS_SELECTOR,"input[name='passwd']")
    input_elms[0].send_keys( CONF["xmile_sso"]["user_pw"] )
    time.sleep( 2 )
    
    input_elms[0].send_keys(Keys.ENTER)
    time.sleep( 10 )
    return browser


def init_browser():
    browser = None
    try:
        browser_service = Service(
            executable_path=CONF["browser"]["browser_driver"] )

        browser_opts = Options()
        for tmp_opt in CONF["browser"]["browser_options"]:
            browser_opts.add_argument( tmp_opt )

        browser_opts.experimental_options["prefs"]=CONF["browser"]["prefs"]

        browser = webdriver.Edge(service = browser_service,
                                 options = browser_opts )
        # 要素が見つかるまで、最大 ?秒 待つ
        browser.implicitly_wait( CONF["browser"]["implicitly_wait"] )
    except Exception as e:
        print(e)
    return browser


# cf. https://qiita.com/fetaro/items/a3b3bd4ea197b600ac45
def set_proxy_pac(on_off):
    reg_key = \
        '"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"'

    if on_off == "off":
        off_cmd_cols = [
            'reg delete',
            reg_key,
            '/v "AutoConfigURL"',
            '/f']
        off_cmd_str = " ".join( off_cmd_cols )
        return exec_subprocess( off_cmd_str )

    if on_off == "on":
        on_cmd_cols = [
            'reg add',
            reg_key,
            '/v "AutoConfigURL"',
            '/t REG_SZ',
            '/d "%s"' % ( CONF["xmile_sso"]["proxy_pac"] ),
            '/f']
        on_cmd_str = " ".join( on_cmd_cols )
        return exec_subprocess( on_cmd_str )

def exec_subprocess(cmd:str, raise_error=True):
    child = subprocess.Popen( cmd,
                              shell=True,
                              stdout=subprocess.PIPE,
                              stderr=subprocess.PIPE )
    stdout, stderr = child.communicate()
    rt = child.returncode
    if rt != 0 and raise_error:
        print("ERROR",stderr,file=sys.stderr)
        return (None,None,None)

    return stdout, stderr, rt

if __name__ == '__main__':
    main()