Programmatic Google Signin/Signout User1 then Signin User2 (Python)

只谈情不闲聊 提交于 2019-12-12 21:09:52

问题


I'm working on a DIY project to retrieve Google location history for multiple members of my household. With direction from StackOverflow member @t.m.adam, I came to realize that the signon process is quite involved (understatement). I am not using OAuth or 2FA at this point. Perhaps I'll graduate, but baby steps first.

I have the signin and cookie retrieval part, and the KML retrieval part working great. I was able to adapt the code in this project (google-login - github.com/tubaman/google-login) which uses selenium to sign in. I adapted the code in this project (Maps-Location-History - https://github.com/alexattia/Maps-Location-History) to retrieve and parse the KML data.

I want to sign in user1, retrieve KML, sign out user1, sign in user2, retrieve user2 KML, sign out user2. Just requesting "https://www.google.com/maps/timeline?authuser=0&logout&hl=en" doesn't do it. It appears that signing out may be as involved as signing in!

Upon closer inspection of the response to the logout request... "Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'report-sample' 'nonce-' 'unsafe-inline' 'strict-dynamic' https: http: 'unsafe-eval'". Note that 'unsafe-inline' is ignored if either a hash or nonce value is present in the source list. It also shows messages referencing P3P policy p3p:CP="This is not a P3P policy! See g.co/p3phelp (https://support.google.com/accounts/answer/151657) for more info."

So, I guess I need to recreate the click stream of an interactive logout using Selenium. I just don't know where to start.

Here's the code of the sign-in and KML retrieval. Now I need some guidance on performing a successful sign-out.

import requests
from selenium import webdriver
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.chrome.options import Options  
from selenium.webdriver.common.keys import Keys
from http.cookiejar import (_warn_unhandled_exception, FileCookieJar, LoadError,
                       Cookie, MISSING_FILENAME_TEXT, MozillaCookieJar)

import sys
import re
import netrc
import logging
import argparse
import os  

import time
from datetime import datetime
import calendar
import numpy as np
import pandas as pd

from bs4 import BeautifulSoup
import process_location


def setup_webdriver():
    chrome_options = Options()
    chrome_options.add_argument("--headless")
    chrome_options.add_argument("--disable-gpu")
    chrome_options.add_argument("--window-size=1024,768")
    chrome_options.binary_location = '/usr/bin/chromium-browser'
    driver = webdriver.Chrome(executable_path="/usr/bin/chromedriver", chrome_options=chrome_options)
    return driver

def copy_cookies_to_session(driver, session):
    """Copy cookies from selenium webdriver to requests.Session"""
    cookies = driver.get_cookies()
    for cookie in cookies:
        session.cookies.set(
            cookie['name'],
            cookie['value'],
            domain=cookie['domain'],
            path=cookie['path']
        )

class Session(requests.Session):
    """A Google session"""

    def __init__(self, *args, **kwargs):
        super(Session, self).__init__(*args, **kwargs)
        self.url = "https://console.developers.google.com"

    def find_sign_in(self, driver):
        conditions = [
            EC.presence_of_element_located((By.PARTIAL_LINK_TEXT, "Sign in")),
            EC.presence_of_element_located((By.PARTIAL_LINK_TEXT, "SIGN IN")),
        ]
        for condition in conditions:
            try:
                sign_in = WebDriverWait(driver, 5).until(condition)
            except:
                pass
            else:
                break
        return sign_in

    def login(self, username, password, url=None):
        if url is not None:
            self.url = url
        driver = setup_webdriver()
        try:
            driver.get(self.url)
            driver.find_element_by_id("Email").clear()
            driver.find_element_by_id("Email").send_keys(username)
            try:
                driver.find_element_by_id("Passwd").clear()
                driver.find_element_by_id("Passwd").send_keys(password)
            except:
                driver.find_element_by_id("next").click()
                condition = EC.visibility_of_element_located((By.ID, "Passwd"))
                passwd = WebDriverWait(driver, 5).until(condition)
                passwd.clear()
                passwd.send_keys(password)
            driver.find_element_by_id("signIn").click()
            copy_cookies_to_session(driver, self)
            assert self.is_logged_in()
        except:
            driver.save_screenshot('/tmp/googlelogin_problem.png')
            raise
        finally:
            driver.quit()

    def is_logged_in(self):
        response = self.get(self.url, allow_redirects=False)
        if response.status_code == 302:
            login_url = "https://accounts.google.com/ServiceLogin"
            return not response.headers['location'].startswith(login_url)
        else:
            return response.status_code == 200

    def save(self, filename):
        cj = MozillaCookieJar(filename)
        for cookie in self.cookies:
            cj.set_cookie(cookie)
        cj.save(ignore_discard=True, ignore_expires=True)

    def load(self, filename):
        cj = MozillaCookieJar(filename)
        cj.load(ignore_discard=True, ignore_expires=True)
        for cookie in cj:
            self.cookies.set_cookie(cookie)

def get_session(user, cookies_path, url, session):
    username, _, password = netrc.netrc().authenticators(user)
    username = '%s@gmail.com' % username
    logger.debug("trying to load saved session")
    try:
        session.load(cookies_path)
    except IOError:
        logger.debug("error loading saved session")
        logger.debug("logging in")
        session.login(username, password, url=url)
        session.save(cookies_path)
    else:
        logger.debug("loaded saved session")
        logger.debug("are we still logged in?")
        if not session.is_logged_in():
            logger.debug("no, so logging in")
            session.login(username, password, url=url)
            logger.debug("saving session")
            session.save(cookies_path)
        else:
            logger.debug("yup, we're still logged in")
    return session

def get_kml(user, folder):
    logger.debug('{} get_kml'.format(user))

    #cookies_path = '%s%s_cookies.txt' % folder, user
    cookies_path = '{}{}_cookies.txt'.format(folder, user)
    url = 'https://www.google.com/maps/timeline'
    cls = Session()
    cls = get_session(user, cookies_path, url, cls)
    #cookies = dict(cookie=cookie_content)

    url = 'https://www.google.com/maps/timeline/kml?authuser=0&pb=!1m8!1m3!1i2018!2i3!3i2!2m3!1i2018!2i3!3i2'
    r = cls.get(url)
    if r.status_code == 200:
        logger.debug('{} current location:'.format(user))
        logger.debug(r.text)
    else:
        logger.debug('{} get_kml error - status_code= {}'.format(user, r.status_code))

    url = 'https://www.google.com/maps/timeline?authuser=0&logout&hl=en'
    r = cls.get(url)
    logger.debug('{} get_kml logout - status_code= {}'.format(user, r.status_code))

cookies_folder = '/home/pi/Projects/GeoFence/GoogleMaps/LocationHistory/LocationHistoryData/'
log_file = os.path.splitext(os.path.realpath(__file__))[0] + '.log'

logger = logging.getLogger(__name__)
logging.basicConfig(filename=log_file, level=logging.DEBUG)
#logging.basicConfig(filename=log_file, level=logging.WARN)

get_kml('user1', cookies_folder)
get_kml('user2', cookies_folder)
sys.exit()

来源:https://stackoverflow.com/questions/49635179/programmatic-google-signin-signout-user1-then-signin-user2-python

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!