123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- # Copyright 2020 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """
- This script runs Chrome and automatically navigates through the given list of
- URLs the specified number of times.
- Usage: vpython3 auto-nav.py <chrome dir> <number of navigations> <url> <url> ...
- Optional flags:
- * --interval <seconds>, -i <seconds>: specify a number of seconds to wait
- between navigations, e.g., -i=5
- * --start_prompt, -s: start Chrome, then wait for the user to press Enter before
- starting auto-navigation
- * --exit-prompt, -e: after auto-navigation, wait for the user to press Enter
- before shutting down chrome.exe
- * --idlewakeups_dir: Windows only; specify the directory containing
- idlewakeups.exe to print measurements taken by IdleWakeups,
- e.g., --idlewakeups_dir=tools/win/IdleWakeups/x64/Debug
- Optional flags to chrome.exe, example:
- -- --user-data-dir=temp --disable-features=SomeFeature
- Note: must be at end of command, following options terminator "--". The options
- terminator stops command-line options from being interpreted as options for this
- script, which would cause an unrecognized-argument error.
- """
- # [VPYTHON:BEGIN]
- # python_version: "3.8"
- # wheel: <
- # name: "infra/python/wheels/selenium-py2_py3"
- # version: "version:3.14.0"
- # >
- # wheel: <
- # name: "infra/python/wheels/urllib3-py2_py3"
- # version: "version:1.24.3"
- # >
- # wheel: <
- # name: "infra/python/wheels/psutil/${vpython_platform}"
- # version: "version:5.7.2"
- # >
- # [VPYTHON:END]
- import argparse
- import os
- import subprocess
- import sys
- import time
- import urllib
- try:
- import psutil
- from selenium import webdriver
- except ImportError:
- print('Error importing required modules. Run with vpython3 instead of '
- 'python.')
- sys.exit(1)
- DEFAULT_INTERVAL = 1
- EXIT_CODE_ERROR = 1
- # Splits list |positional_args| into two lists: |urls| and |chrome_args|, where
- # arguments starting with '-' are treated as chrome args, and the rest as URLs.
- def ParsePositionalArgs(positional_args):
- urls, chrome_args = [], []
- for arg in positional_args:
- if arg.startswith('-'):
- chrome_args.append(arg)
- else:
- urls.append(arg)
- return [urls, chrome_args]
- # Returns an object containing the arguments parsed from this script's command
- # line.
- def ParseArgs():
- # Customize usage and help to include options to be passed to chrome.exe.
- usage_text = '''%(prog)s [-h] [--interval INTERVAL] [--start_prompt]
- [--exit_prompt] [--idlewakeups_dir IDLEWAKEUPS_DIR]
- chrome_dir num_navigations url [url ...]
- [-- --chrome_option ...]'''
- additional_help_text = '''optional arguments to chrome.exe, example:
- -- --enable-features=MyFeature --browser-startup-dialog
- Must be at end of command, following the options
- terminator "--"'''
- parser = argparse.ArgumentParser(
- epilog=additional_help_text,
- usage=usage_text,
- formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument(
- 'chrome_dir', help='Directory containing chrome.exe and chromedriver.exe')
- parser.add_argument('num_navigations',
- type=int,
- help='Number of times to navigate through list of URLs')
- parser.add_argument('--interval',
- '-i',
- type=int,
- help='Seconds to wait between navigations; default is 1')
- parser.add_argument('--start_prompt',
- '-s',
- action='store_true',
- help='Wait for confirmation before starting navigation')
- parser.add_argument('--exit_prompt',
- '-e',
- action='store_true',
- help='Wait for confirmation before exiting chrome.exe')
- parser.add_argument(
- '--idlewakeups_dir',
- help='Windows only; directory containing idlewakeups.exe, if using')
- parser.add_argument(
- 'url',
- nargs='+',
- help='URL(s) to navigate, separated by spaces; must include scheme, '
- 'e.g., "https://"')
- args = parser.parse_args()
- args.url, chrome_args = ParsePositionalArgs(args.url)
- if not args.url:
- parser.print_usage()
- print(os.path.basename(__file__) + ': error: missing URL argument')
- exit(EXIT_CODE_ERROR)
- for url in args.url:
- if not urllib.parse.urlparse(url).scheme:
- print(os.path.basename(__file__) +
- ': error: URL is missing required scheme (e.g., "https://"): ' + url)
- exit(EXIT_CODE_ERROR)
- return [args, chrome_args]
- # If |path| does not exist, prints a generic error plus optional |error_message|
- # and exits.
- def ExitIfNotFound(path, error_message=None):
- if not os.path.exists(path):
- print('File not found: {}.'.format(path))
- if error_message:
- print(error_message)
- exit(EXIT_CODE_ERROR)
- def main():
- # Parse arguments and check that file paths received are valid.
- args, chrome_args = ParseArgs()
- ExitIfNotFound(os.path.join(args.chrome_dir, 'chrome.exe'),
- 'Build target "chrome" to generate it first.')
- chromedriver_exe = os.path.join(args.chrome_dir, 'chromedriver.exe')
- ExitIfNotFound(chromedriver_exe,
- 'Build target "chromedriver" to generate it first.')
- if args.idlewakeups_dir:
- idlewakeups_exe = os.path.join(args.idlewakeups_dir, 'idlewakeups.exe')
- ExitIfNotFound(idlewakeups_exe)
- # Start chrome.exe. Disable chrome.exe's extensive logging to make reading
- # this script's output easier.
- chrome_options = webdriver.ChromeOptions()
- chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])
- for arg in chrome_args:
- chrome_options.add_argument(arg)
- driver = webdriver.Chrome(os.path.abspath(chromedriver_exe),
- options=chrome_options)
- if args.start_prompt:
- driver.get(args.url[0])
- input('Press Enter to begin navigation...')
- # Start IdleWakeups, if using, passing the browser process's ID as its target.
- # IdleWakeups will monitor the browser process and its children. Other running
- # chrome.exe processes (i.e., those not launched by this script) are excluded.
- if args.idlewakeups_dir:
- launched_processes = psutil.Process(
- driver.service.process.pid).children(recursive=False)
- if not launched_processes:
- print('Error getting browser process ID for IdleWakeups.')
- exit()
- # Assume the first child process created by |driver| is the browser process.
- idlewakeups = subprocess.Popen([
- idlewakeups_exe,
- str(launched_processes[0].pid), '--stop-on-exit', '--tabbed'
- ],
- stdout=subprocess.PIPE)
- # Navigate through |args.url| list |args.num_navigations| times, then close
- # chrome.exe.
- interval = args.interval if args.interval else DEFAULT_INTERVAL
- for _ in range(args.num_navigations):
- for url in args.url:
- driver.get(url)
- time.sleep(interval)
- if args.exit_prompt:
- input('Press Enter to exit...')
- driver.quit()
- # Print IdleWakeups' output, if using.
- if args.idlewakeups_dir:
- print(idlewakeups.communicate()[0])
- if __name__ == '__main__':
- sys.exit(main())
|