Advertisement
Guest User

Untitled

a guest
Aug 20th, 2017
13
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 8.62 KB | None | 0 0
  1. #! /usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3.  
  4. import os
  5. import sys
  6. import json
  7. import time
  8. import errno
  9. import base64
  10. import pychrome
  11. import threading
  12. import logging
  13. import psutil as psutil
  14. from bs4 import BeautifulSoup
  15. from argparse import ArgumentParser
  16. from subprocess import Popen, PIPE
  17.  
  18. # Cross-compatible import for urlparse
  19. if sys.version_info >= (3, 0):
  20.     from urllib.parse import urlparse
  21. if (3, 0) > sys.version_info >= (2, 5):
  22.     from urlparse import urlparse
  23.  
  24. # Log
  25. logging.basicConfig(filename='app.log',
  26.                     level=logging.DEBUG,
  27.                     format='(%(threadName)-9s) %(message)s', )
  28.  
  29. # Define CLI Arguments
  30. parser = ArgumentParser()
  31. parser.add_argument('--urls', help='e.g. --urls=https://google.com,https://facebook.com,https://ebay.com',
  32.                     type=lambda s: [str(item) for item in s.split(',')])
  33. parser.add_argument('--window-size', help='e.g. --window-size=1024,768',
  34.                     type=lambda s: [int(item) for item in s.split(',')], default='1024,768')
  35. parser.add_argument('--user-agent',
  36.                     help='e.g. --user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36"',
  37.                     type=str)
  38. parser.add_argument('--timeout', help='e.g. --timeout=60', type=int, default=60)
  39. parser.add_argument('--force-kill', help='e.g. --force-kill', action='store_true')
  40. parser.add_argument('-v', '--verbose', help='e.g. -v or -verbose', action='store_true')
  41.  
  42. # Parse Arguments
  43. args = parser.parse_args()
  44. print(args)
  45.  
  46. if args.verbose:
  47.     def info(*args):
  48.         for arg in args:
  49.             print (arg),
  50.         print
  51. else:
  52.     info = lambda *a: None
  53.  
  54.  
  55. class Handler(object):
  56.     lock = threading.Lock()
  57.  
  58.     def __init__(self, browser, tab):
  59.         self.browser = browser
  60.         self.tab = tab
  61.         self.start_frame = None
  62.         self.is_first_request = True
  63.         self.request_id = None
  64.         self.is_first_response = True
  65.         self.url = None
  66.         if args.user_agent is not None:
  67.             self.tab.Network.setUserAgentOverride(userAgent=args.user_agent)
  68.  
  69.     def frame_started_loading(self, frameId):
  70.         if not self.start_frame:
  71.             self.start_frame = frameId
  72.  
  73.     def frame_stopped_loading(self, frameId):
  74.         if self.start_frame == frameId:
  75.             self.tab.Page.stopLoading()
  76.             with self.lock:
  77.                 try:
  78.                     # Activate Tab
  79.                     self.browser.activate_tab(self.tab.id)
  80.                     info('Activated Tab: %s' % self.url)
  81.                     # Document
  82.                     document = self.tab.DOM.getDocument()
  83.                     # Full DOM
  84.                     dom = self.tab.DOM.getOuterHTML(nodeId=document['root']['nodeId'])
  85.                     # Use Beautiful Soup to Prettify
  86.                     info('Prettify HTML and write to file.')
  87.                     soup = BeautifulSoup(dom['outerHTML'], 'html.parser')
  88.                     prettyHTML = soup.prettify()
  89.                     outerHtmlFile = '%s/outer.html' % self.url
  90.                     if not os.path.exists(os.path.dirname(outerHtmlFile)):
  91.                         try:
  92.                             os.makedirs(os.path.dirname(outerHtmlFile))
  93.                         except OSError as exc:
  94.                             if exc.errno != errno.EEXIST:
  95.                                 raise
  96.                     with open(outerHtmlFile, 'wb') as stream:
  97.                         stream.write(prettyHTML.encode('utf-8'))
  98.                     # Full Page Screenshot
  99.                     info('Take full page screenshot and write binary to file.')
  100.                     self.tab.Emulation.setDeviceMetricsOverride(width=args.window_size[0], height=args.window_size[1],
  101.                                                                 deviceScaleFactor=0.0, mobile=False, fitWindow=False)
  102.                     body = self.tab.DOM.querySelector(nodeId=document['root']['nodeId'], selector='body')
  103.                     box = self.tab.DOM.getBoxModel(nodeId=body['nodeId'])
  104.                     self.tab.Emulation.setVisibleSize(width=args.window_size[0], height=box['model']['height'])
  105.                     self.tab.Emulation.forceViewport(x=0, y=0, scale=1)
  106.                     screenshot = self.tab.Page.captureScreenshot()
  107.                     screenshotFile = '%s/screenshot.png' % self.url
  108.                     if not os.path.exists(os.path.dirname(screenshotFile)):
  109.                         try:
  110.                             os.makedirs(os.path.dirname(screenshotFile))
  111.                         except OSError as exc:
  112.                             if exc.errno != errno.EEXIST:
  113.                                 raise
  114.                     with open(screenshotFile, 'wb') as stream:
  115.                         stream.write(base64.b64decode(screenshot['data']))
  116.                 finally:
  117.                     info('Stop Tab: %s.' % self.url)
  118.                     self.tab.stop()
  119.  
  120.     def request_will_be_sent(self, **kwargs):
  121.         if self.is_first_request:
  122.             self.is_first_request = False
  123.             # Set the URL we're making the request to.
  124.             self.url = urlparse(kwargs.get('request').get('url')).hostname.replace('www.', '')
  125.             info('Loading: %s' % self.url)
  126.  
  127.     def response_received(self, **kwargs):
  128.         if self.is_first_response:
  129.             self.is_first_response = False
  130.             info('Response Received: %s' % self.url)
  131.             info('Prettify JSON headers and write to file.')
  132.             headersFile = '%s/headers.json' % self.url
  133.             if not os.path.exists(os.path.dirname(headersFile)):
  134.                 try:
  135.                     os.makedirs(os.path.dirname(headersFile))
  136.                 except OSError as exc:
  137.                     if exc.errno != errno.EEXIST:
  138.                         raise
  139.             with open(headersFile, 'w') as stream:
  140.                 stream.write(json.dumps(kwargs.get('response').get('headers'), indent=2))
  141.  
  142. # Close all tabs utility
  143. def close_all_tabs(browser):
  144.     if len(browser.list_tab()) == 0:
  145.         return
  146.     for tab in browser.list_tab():
  147.         try:
  148.             tab.stop()
  149.         except pychrome.RuntimeException:
  150.             pass
  151.         browser.close_tab(tab)
  152.     time.sleep(1)
  153.     assert len(browser.list_tab()) == 0
  154.  
  155.  
  156. def main():
  157.     chromeArguments = ['/usr/bin/google-chrome', '--headless', '--hide-scrollbars', '--disable-gpu',
  158.                        '--remote-debugging-port=9222']
  159.  
  160.     # Find Chrome utility function.
  161.     def find_chrome():
  162.         for process in psutil.process_iter():
  163.             if process.name() == 'chrome' and chromeArguments == process.cmdline():
  164.                 return process
  165.         return False
  166.  
  167.     # Chrome
  168.     if find_chrome():
  169.         info('A Google Chrome process already exists with the arguments we need... we\'ll use that.')
  170.     else:
  171.         info('Starting Chrome.')
  172.         devnull = open(os.devnull, 'wb')
  173.         Popen(chromeArguments,
  174.               shell=False,
  175.               stdout=PIPE,
  176.               stderr=devnull)
  177.         # We have to block for 1s to prevent a race condition.
  178.         time.sleep(1)
  179.         info('Chrome is running... Let\'s interact with in through the chrome dev tools protocol.')
  180.     browser = pychrome.Browser()
  181.     close_all_tabs(browser)
  182.     tabs = []
  183.     for i in range(len(args.urls)):
  184.         tabs.append(browser.new_tab())
  185.     for i, tab in enumerate(tabs):
  186.         eh = Handler(browser, tab)
  187.         tab.Page.frameStartedLoading = eh.frame_started_loading
  188.         tab.Page.frameStoppedLoading = eh.frame_stopped_loading
  189.         tab.Network.requestWillBeSent = eh.request_will_be_sent
  190.         tab.Network.responseReceived = eh.response_received
  191.         tab.Network.enable()
  192.         tab.Page.stopLoading()
  193.         tab.Page.enable()
  194.         tab.Page.navigate(url=args.urls[i])
  195.     for i, tab in enumerate(tabs):
  196.         success = tab.wait(args.timeout)
  197.         if not success:
  198.             info('Timeout.')
  199.             errorFile = '%s/error.log' % urlparse(args.urls[i]).hostname.replace('www.', '')
  200.             if not os.path.exists(os.path.dirname(errorFile)):
  201.                 try:
  202.                     os.makedirs(os.path.dirname(errorFile))
  203.                 except OSError as exc:
  204.                     if exc.errno != errno.EEXIST:
  205.                         raise
  206.             with open(errorFile, 'w') as stream:
  207.                 stream.write('Timed out.')
  208.         info('Close Tab.')
  209.         browser.close_tab(tab)
  210.     if args.force_kill:
  211.         info('Force Kill Chrome.')
  212.         find_chrome().kill()
  213.     info('Complete.')
  214.  
  215. if __name__ == '__main__':
  216.     main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement