Last active
June 3, 2021 12:49
-
-
Save tai-fukaya/874c08bde4f2f24fd34f62d55f6e5b24 to your computer and use it in GitHub Desktop.
lighthouseの結果を自動で取得する(雑)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import csv | |
import datetime | |
import json | |
import os | |
import re | |
import subprocess | |
import time | |
# python execute-lighthouse-test.py target(file, url) --output_dir=<path> --count=<count> | |
parser = argparse.ArgumentParser() | |
parser.add_argument("target") | |
parser.add_argument("--output_dir", type=str, default=None) | |
parser.add_argument("--count", type=int, default=5) | |
args = parser.parse_args() | |
count = args.count | |
output_dir = args.output_dir | |
SETTINGS = [ | |
{'id': 'RequestedUrl', 'query': '.requestedUrl', 'default': ''}, | |
{'id': 'performance', 'query': '.categories.performance.score', 'default': 0}, | |
{'id': 'accessibility', 'query': '.categories.accessibility.score', 'default': 0}, | |
{'id': 'bestPractices', 'query': '.categories.best-practices.score', 'default': 0}, | |
{'id': 'seo', 'query': '.categories.seo.score', 'default': 0}, | |
{'id': 'pwa', 'query': '.categories.pwa.score', 'default': 0}, | |
# performance | |
{'id': 'FirstContentfulPaint', 'query': '.audits.first-contentful-paint.numericValue', 'default': 0}, | |
{'id': 'FirstMeaningfulPaint', 'query': '.audits.first-meaningful-paint.numericValue', 'default': 0}, | |
{'id': 'LargestContentfulPaint', 'query': '.audits.largest-contentful-paint.numericValue', 'default': 0}, | |
{'id': 'Interactive', 'query': '.audits.interactive.numericValue', 'default': 0}, | |
{'id': 'SpeedIndex', 'query': '.audits.speed-index.numericValue', 'default': 0}, | |
{'id': 'TotalBlockingTime', 'query': '.audits.total-blocking-time.numericValue', 'default': 0}, | |
{'id': 'MaxPotentialFID', 'query': '.audits.max-potential-fid.numericValue', 'default': 0}, | |
{'id': 'CumulativeLayoutShift', 'query': '.audits.cumulative-layout-shift.numericValue', 'default': 0}, | |
# performance details | |
{'id': 'CriticalRequestChains', 'query': '.audits.critical-request-chains.displayValue', 'default': ''}, # FCP, LCP | |
{'id': 'LongTasks', 'query': '.audits.long-tasks.displayValue', 'default': ''}, # TBT | |
{'id': 'LayoutShiftElements', 'query': '.audits.layout-shift-elements.displayValue', 'default': ''}, # CLS | |
{'id': 'ServerResponseTime', 'query': '.audits.server-response-time.numericValue', 'default': 0}, # FCP, LCP | |
{'id': 'UnusedCssRules', 'query': '.audits.unused-css-rules.displayValue', 'default': ''}, # FCP, LCP | |
{'id': 'UnusedJavascript', 'query': '.audits.unused-javascript.displayValue', 'default': ''}, # LCP | |
{'id': 'TotalByteWeight', 'query': '.audits.total-byte-weight.numericValue', 'default': 0}, # LCP | |
{'id': 'BootupTimeScore', 'query': '.audits.bootup-time.score', 'default': 0}, # TBT | |
{'id': 'BootupTime', 'query': '.audits.bootup-time.numericValue', 'default': 0}, # TBT | |
{'id': 'MainthreadWorkBreakdownScore', 'query': '.audits.mainthread-work-breakdown.score', 'default': 0}, # TBT | |
{'id': 'MainthreadWorkBreakdown', 'query': '.audits.mainthread-work-breakdown.numericValue', 'default': 0}, # TBT | |
{'id': 'DomSizeScore', 'query': '.audits.dom-size.score', 'default': 0}, # TBT | |
{'id': 'DomSize', 'query': '.audits.dom-size.numericValue', 'default': 0}, # TBT | |
{'id': 'ThirdPartyFacades', 'query': '.audits.third-party-facades.displayValue', 'default': ''}, # TBT | |
{'id': 'ThirdPartySummary', 'query': '.audits.third-party-summary.score', 'default': 0}, # TBT score | |
{'id': 'UnsizedImages', 'query': '.audits.unsized-images.score', 'default': 0}, # CLS score | |
{'id': 'FontDisplay', 'query': '.audits.font-display.score', 'default': 0}, # FCP, LCP score | |
{'id': 'UsesTextCompression', 'query': '.audits.uses-text-compression.score', 'default': 0} # FCP, LCP score | |
# {'id': 'RenderBlockingResources', 'query': '.audits.render-blocking-resources.score', 'default': 0}, # FCP, LCP score | |
# {'id': 'Redirects', 'query': '.audits.redirects.score', 'default': 0}, # FCP, LCP score | |
# {'id': 'UsesRelPreconnect', 'query': '.audits.uses-rel-preconnect.score', 'default': 0}, # FCP, LCP score | |
# {'id': 'UsesRelPreload', 'query': '.audits.uses-rel-preload.score', 'default': 0}, # FCP, LCP score | |
# {'id': 'UnminifiedJavascript', 'query': '.audits.unminified-javascript.score', 'default': 0}, # FCP, LCP score | |
# {'id': 'UnminifiedCss', 'query': '.audits.unminified-css.score', 'default': 0}, # FCP, LCP score | |
# {'id': 'LargestContentfulPaintElement', 'query': '.audits.largest-contentful-paint-element.displayValue', 'default': ''}, # LCP | |
# {'id': 'PreloadLcpImage', 'query': '.audits.preload-lcp-image.score', 'default': 0}, # LCP score | |
# {'id': 'EfficientAnimatedContent', 'query': '.audits.efficient-animated-content.score', 'default': 0}, # LCP score | |
# {'id': 'DuplicatedJavascript', 'query': '.audits.duplicated-javascript.score', 'default': 0}, # TBT score | |
# {'id': 'LegacyJavascript', 'query': '.audits.legacy-javascript.score', 'default': 0} # TBT score | |
] | |
results = [] | |
def makedirs(path): | |
if not os.path.isdir(path): | |
os.makedirs(path) | |
def exec_lighthouse(url): | |
# --preset=desktop | |
commands = ['lighthouse', url, '--quiet', '--chrome-flags="--headless"', '--output=json'] | |
return subprocess.check_output(commands) | |
def save_result(path, output): | |
with open(path, 'w') as f: | |
f.write(output) | |
def get_json_value(obj, query_arr, default): | |
o = obj.get(query_arr[0]) | |
if len(query_arr) > 1: | |
return get_json_value(o or {}, query_arr[1:], default) | |
else: | |
return o or default | |
def parse(output): | |
parsed = json.loads(output) | |
result = {} | |
for s in SETTINGS: | |
# TODO Array[] | |
query_arr = s.get('query', '.').split('.')[1:] | |
value = get_json_value(parsed, query_arr, s.get('default')) | |
if type(value) is int or type(value) is float: | |
value = str(value) | |
elif type(value) is unicode: | |
value = value.encode('utf_8') | |
result[s.get('id')] = value | |
results.append(result) | |
return result | |
if output_dir is not None: | |
makedirs(output_dir) | |
# read target url | |
lines = [] | |
if args.target.find("://") >= 0: | |
lines.append(args.target) | |
else: | |
with open(args.target, 'r') as f: | |
lines = f.read().split("\n") | |
print(','.join([ x.get('id') for x in SETTINGS if 'id' in x])) | |
for url in lines: | |
for i in range(count): | |
try: | |
output = exec_lighthouse(url) | |
if output_dir is not None: | |
now = datetime.datetime.now().strftime('%Y%m%d%H%M%S') | |
key = re.sub('[^a-zA-Z0-9]+', '-', url) + '_' + now | |
output_file_path = output_dir + '/' + key + '.json' | |
save_result(output_file_path, output) | |
parsed = parse(output) | |
print(','.join([parsed.get(x.get('id')) for x in SETTINGS if 'id' in x])) | |
except subprocess.CalledProcessError as e: | |
pass | |
time.sleep(.1) | |
if output_dir is not None: | |
output_result_path = output_dir + '/' + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '.tsv' | |
with open(output_result_path, 'w') as f: | |
writer = csv.DictWriter(f, [ x.get('id') for x in SETTINGS if 'id' in x], delimiter='\t') | |
writer.writeheader() | |
writer.writerows(results) |
We can make this file beautiful and searchable if this error is corrected: No commas found in this CSV file in line 0.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
http://example.com/ | |
http://example.com/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment