selenium&playwright获取网站Authorization鉴权实现伪装requests请求

selenium&playwright获取网站Authorization鉴权实现伪装requests请求

本文已实战为主,如果不熟悉selenium或playwright,建议补充相关知识点:

cookie、session、request、headers相关概念

selenium:get_log() 获取用户权限信息,打开指定浏览器,免登陆,伪造请求头

playwright:类方法-Page,Request,Route,Docs-Authentication,Network

本文使用的各个框架版本如下:

python-3.8.8
selenium-3.141.0
playwright-1.32.1
requests-2.27.1

其中selenium4与selenium3的操作有一些差异,这里不做研究。

吐槽一下,playwright的资料是真的很少(基础资料除外),只能自己去看官网,官网写的还是可以的,自己多试试还是可以搞出点东西来的,但真的好累,呜呜呜~

需求背景

1、登录google类web端,通过自动化手段登录会被google监控并屏蔽登录请求,严重有封号风险(在之前的文章有讲过)。

2、selenium或playwright打开指定已登录google账号的浏览器,获取用户鉴权信息。

3、伪造请求头,通过requests获取对应接口的信息,进行数据拉取。

本文实战背景以FireBase后台为列,https://console.firebase.google.com/

没有接触过的,可以用Gmail等其他系列的google应用,但重在思路和方法,详见后文一步步解析。

知识点:selenium获取已登录网站的用户鉴权信息

直接上代码

__author__ = "梦无矶小仔"
import json,time,requests
from datetime import datetime, timedelta
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

def get_headers():
    # 关键步骤 1:下面两行代码是用来设置特性,获取request的信息前提步骤。
    d = DesiredCapabilities.CHROME
    d['loggingPrefs'] = {'performance': 'ALL'}
    options = webdriver.ChromeOptions()
    options.add_experimental_option('useAutomationExtension', False)
    # # 防止打印一些无用的日志
    options.add_experimental_option("excludeSwitches", ['enable-automation', 'enable-logging'])
    options.add_argument("--disable-software-rasterizer")

    chrome_options = Options()
    chrome_options.add_experimental_option('w3c', False)
    chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
    chrome_driver = "./chromedriver.exe"  # 我是把chromedriver驱动放在项目根目录下
    driver = webdriver.Chrome(executable_path=chrome_driver, options=options, chrome_options=chrome_options)
    driver.get("https://console.firebase.google.com/")
    info = driver.get_log('performance')
    cookie_list = []
    for i in info:
        dic_info = json.loads(i["message"])  # 把json格式转成字典。
        infom = dic_info["message"]  # request 信息,在字典的 键 ["message"]['params'] 中。
        if infom['method'] == 'Network.requestWillBeSentExtraInfo' and infom["params"]["headers"].get(":authority"):
            if infom["params"]["headers"][":authority"] == "mobilesdk-pa.clients6.google.com" and \
                    infom["params"]["headers"][":method"] == 'POST':
                cookie_list.append(infom["params"]["headers"])

    authorization = cookie_list[0]["authorization"]
    cookie = cookie_list[0]["cookie"]
	
    # 伪造请求头
    headers = {
        "Host": "crashlytics-pa.clients6.google.com",
        "content-type": "application/json",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36 Edg/96.0.1054.62",
        "referer": "https://console.firebase.google.com/",
        "cookie": cookie,
        "origin": "https://console.firebase.google.com",
        "authorization": authorization
    }

    return headers

代码解析

1、好像没啥解析的,就是通过performance的log去过滤我要的接口,拿到接口中的各项信息

2、哪里看不懂给我留言吧

注意

我使用的是selenium3,如果你是selenium4,你需要使用如下方法获取:

from seleniumwire import webdriver  #pip install selenium-wire
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
browser = webdriver.Chrome()
browser.get('http://....') #打开
#获取Authorization_str
Authorization_str=''
for request in  browser.requests:  #遍历所有 请求
    # if request.method == 'POST' and \
    #    request.url == 'http://....': #找到这个请求
        if 'Authorization' in request.headers: #有这个标志
            Authorization_str = request.headers['Authorization']  #找到了结果
            break

要点:

1、装完selenium后,还得装selenium-wire

from seleniumwire import webdriver  #pip install selenium-wire
代替
from selenium import webdriver

2、仅browser 使用 seleniumwire, 其他都不变,例如 By、keys等还用selenium

知识点:playwright获取cookie

playwright官方cookie代码:BrowserContext | Playwright Python

方法一:

自动打开浏览器,手动登录后通过playwright保存cookie到本地,之后需要直接通过文件读取这个cookie。

__author__ = "梦无矶小仔"
from playwright.sync_api import sync_playwright
import json
# 先手动登录,保存Cooies到文件。
def saveCookies():
    with sync_playwright() as p:
        # 显示浏览器,每步操作等待100毫秒
        browser = p.chromium.launch(headless=False, slow_mo=100)
        context = browser.new_context()
        page = context.new_page()
        page.goto('https://cq.meituan.com/', timeout=50000)  # 设置超时时间为50s
        time.sleep(80)  # 此处手动登录,然后到个人信息页再获取cookie
        cookies = context.cookies()
        print(page.title())
        browser.close()
        f = open('cookies.txt', 'w+',,encoding="utf-8")
        json.dump(cookies, f)
        time.sleep(2)
        browser.close()
        print("cookie获取完毕")
saveCookies()#执行函数

方法二:

手动打开指定浏览器,使playwright指定改浏览器运行,获取已登录的cookie信息,保存到本地。

__author__ = "梦无矶小仔"
# 对已经打开的浏览器进行操作
import json
import subprocess
from pprint import pprint
from playwright.sync_api import Playwright,sync_playwright

playwright = sync_playwright().start()
# 连接已打开浏览器,指定端口
browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9222")
default_context = browser.contexts[0] # 注意这里不是browser.new_page()了
page = default_context.pages[0]
base_url = r"https://console.firebase.google.com/" # 我这里截去了项目网站的url进行脱敏
page.goto(base_url)
# page.wait_for_timeout(timeout=15000)
print(page.title()) #firebase标题
time.sleep(5)
cookies = default_context.cookies(urls=base_url) #指定url下的cookie值,不填则是所有的
pprint(cookies)
# 保存cookies到本地
filePath = r'cookies.txt'
with open(filePath,'w+',encoding="utf-8") as f:
    json.dump(cookies,f)

playwright.stop() 

知识点:playwright获取storage_state提取cookie

F12打开浏览器,在Aplication下可以看到本地存储的一些信息,比如cookie、session

请添加图片描述

官方教程:BrowserContext | Playwright Python

__author__ = "梦无矶小仔"
# 对已经打开的浏览器进行操作
import json
import subprocess
import time
from pprint import pprint
from playwright.sync_api import Playwright,sync_playwright

playwright = sync_playwright().start()
# 连接已打开浏览器,找好端口
browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9222")
default_context = browser.contexts[0] # 注意这里不是browser.new_page()了
page = default_context.pages[0]
base_url = r"https://console.firebase.google.com"  # 我这里截去了项目网站的url进行脱敏
page.goto(base_url)
print(page.title()) #firebase标题
filePath = r'storage_state.txt'
storage_state = default_context.storage_state(path=filePath)
pprint(storage_state)
playwright.stop()
# browser.close()

但这个方法会有个问题,这里面会把你当前浏览器所有的strong存储的内容拿出来。

如果是像我只是针对某个网站接口的cookie,这个方法就显得有点臃肿,还需要自己去整体过滤,而且本地的还存在一个及时刷新的问题(我遇到过有效期非常短的)。

秃发状况

2023.5.10这天,windows自动更新了,它更新就算了,还自动把我固定版本的chrome浏览器也给更新到了最新版本。

我原先就禁用了chrome自动更新的功能,这次windows更新竟然可以解禁,就很离谱。

这里更新一下windows禁止chrome浏览器自动更新。当前版本是 113.0.5672.93(正式版本) (64 位)

那么浏览器更新了我会遇到一个怎样的问题?

1、selenium是倚靠驱动进行浏览器操作,浏览器更新了我就得更新驱动,但我没有做自动更新驱动的功能

2、之前一直用的是固定某个版本,这样驱动一直用一个就行,现在不得不面临三个选择

  • 更新驱动,再次禁用更新,以后遇到再说(麻烦)

  • 增加自动更新驱动的功能(后续会意想不到的坑)

  • UI层面改为playwright,因为playwright无需依赖三方驱动(懒汉必备)

于是这三个我都研究了一下,接下来一一解析一下。

windows禁止chrome浏览器自动更新

1、找到C:\Users\xiaozai\AppData\Local\Google目录下的Update文件夹

请添加图片描述

2、右键属性,选择安全选项,点击编辑,把这些用户的权限全部改成拒绝

请添加图片描述

3、在安全选项下,点击高级,点击禁用继承,删除允许用户,点击确认

请添加图片描述

4、中途点击确认的时候,由于你禁止了权限,会有一堆弹窗,一直点确认就ok了

5、验证,之后你再双击Updata文件夹,发现是无权访问了

请添加图片描述

6、去chrome查看更新选项,发现已经无法更新了

请添加图片描述

selenium自动下载驱动

使用Drivers的方式

官方介绍:https://www.selenium.dev/documentation/webdriver/getting_started/install_drivers/

先安装库:

pip install webdriver-manager

webdriver-manager 支持selenium3.0、selenium4.0

具体可以看github上的说明:https://github.com/SergeyPirogov/webdriver_manager

基于 selenium3 的chrome示列

# pip install webdriver-manager
#selenium3
import time
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(ChromeDriverManager().install())
driver.get("https://www.baidu.com")
driver.maximize_window()
time.sleep(5)
driver.quit()

基于 selenium4 的chrome示列

import time
from selenium import webdriver
from selenium.webdriver.edge.service import Service
from webdriver_manager.chrome import ChromeDriverManager

service = Service(executable_path=ChromeDriverManager().install())
driver = webdriver.Ie(service=service)
driver.get("https://www.baidu.com")
driver.maximize_window()
time.sleep(5)
driver.quit()

是不是非常简单,非常nice?

官方的列子这里我就不放了,大家有兴趣自己去研究哈~

playwright无驱动操作已打开浏览器

详见我之前写的文章,这里就不赘述了,链接如下:

公众号:playwright连接已有浏览器操作 (qq.com)

CSDN:https://blog.csdn.net/qq_46158060/article/details/130429536?spm=1001.2014.3001.5501

Authorization鉴权

对于google类型的所有认证权限都含有一个Authorization,并且加密是SAPISIDHASH,这个我不会破解。

请添加图片描述

如果请求头不携带此鉴权字段,是无法访问相关接口的。

请添加图片描述

{
  "error": {
    "code": 401,
    "message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
    "status": "UNAUTHENTICATED",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.ErrorInfo",
        "reason": "CREDENTIALS_MISSING",
        "domain": "googleapis.com",
        "metadata": {
          "service": "crashlytics-pa.googleapis.com",
          "method": "google.internal.crashlytics.dashboard.v1.CrashlyticsMetricsReadService.GetCrashStatistics"
        }
      }
    ]
  }
}

通过selenium知道可以在performance获取request请求信息(前文有demo),那么playwright是否有类似的方法?

通过查阅官方文档发现,确实有,它叫事件监听。

目前我们就需要拿到请求头的这些信息,从而通过接口进行获取数据。

Authorization
Cookie
Origin
Referer
User-Agent

playwright 事件监听

官方文档:

page.on事件监听:https://playwright.dev/python/docs/api/class-page#page-event-request

request拦截接口:https://playwright.dev/python/docs/api/class-request

事件监听我们主要使用的是page.on("request",my_request),其他监听事件可以参考官网。

Request事件里面有个all_headers方法,会以字典的形式返回我们请求的请求头信息。

请添加图片描述

request拦截接口代码示列:

def my_request(request):
    print(request.all_headers())

page.on("request",my_request) # 创建拦截请求,获取请求的hearders  
# 这里推荐使用requestfinished

注意:page.on在page实例后就要创建,代表监控之后page上发生的对应事件。如果在事件发生后创建page.on方法,则无法监控该事件,只能监控创建page.on之后的操作。

如我监控了FireBase后台数据页面,示列代码如下

import json
from pprint import pprint
import requests
from playwright.sync_api import sync_playwright

def my_request(request):
    print(request.all_headers())
    # 对headers进行劫持处理

playwright = sync_playwright().start()
# 连接已打开浏览器,找好端口
browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9222")
default_context = browser.contexts[0] # 注意这里不是browser.new_page()了
page = default_context.pages[0]
page.on("requestfinished",my_request) # 创建拦截请求,获取请求的hearders
base_url = r"https://console.firebase.google.com/"
page.goto(base_url)
page.wait_for_load_state('networkidle') #等待资源加载,直到没有网络请求,否则得到的资源不完整,拿不到想要的鉴权信息

控制台输出监控到了的所有请求头信息,authorization字段赫然在列,我们可以继续改造my_request方法,拿到我们所需要的headers信息。

请添加图片描述

但这个时候还遇到个问题,这里获取到的authorization并不是我真正能够使用的,我还需要对referer字段进行过滤,但发现并没有我要找的,F12查看network发现前端是刷新出来了。打印出来的却全是https://console.firebase.google.com/

请添加图片描述

需要在请求后加上这句,表示等待资源加载,直到没有网络请求。详细可以看官网:https://playwright.dev/python/docs/api/class-page#page-wait-for-load-state

page.wait_for_load_state('networkidle')

但有时候也会有点不灵,所以我推荐使用强制等待。

page.wait_for_timeout(timeout=20000) # 这个timeout是毫秒

接下来需要对这些请求头进行过滤,我只需要拿到一个包含Authorization字段的headers字段的信息就可以结合前面拿到的cookie进行伪造了。

同时过滤:authority字段,注意,F12你看到的首字母是大写,playwright官方文档中有说明,headers返回的都是小写字段,所以我们拿的时候要以小写的方式进行提取。

改造后的my_request方法

# 全局变量
user_headers = {}
def my_request(request):
    all_headers_dict = request.all_headers()
    # 过滤请求(这里我对:path也进行了过滤,完整path我脱敏处理了)
    if all_headers_dict.get(':path') == "/metrics:getCrashFree.." and not user_headers:
        if all_headers_dict[":method"]=='POST' and all_headers_dict[":authority"]=='crashlytics-pa.clients6.google.com' :
            # 提取我需要的信息
            user_headers["cookie"] = all_headers_dict.get("cookie")
            user_headers["user-agent"] = all_headers_dict.get("user-agent")
            user_headers["authorization"] = all_headers_dict.get("authorization")

最终会将第一次获取到的headers信息记录到user_headers字典中。

接着我们就可以使用requests进行携带带有认证信息的请求头进行接口请求了。

使用route劫持

官方文档:Route | Playwright Python

用这个方法也可以获取到请求头的相关信息,它最终还是使用了request获取请求头。

我在使用的过程中发现有时候请求会被阻塞,不知道为啥,对这方面有研究的大佬请指教我一哈,万分感谢。

user_msg_list = []
def handler(route):
    headers_dict = {}
    all_headers_dict = route.request.headers
    if all_headers_dict.get('authorization') and all_headers_dict.get('cookie'):
        # if 'https://scone-pa.clients6.google.com/static/proxy' in all_headers_dict["referer"]:
            # 提取我需要的信息
            headers_dict["referer"] = all_headers_dict.get("referer")
            headers_dict["user-agent"] = all_headers_dict.get("user-agent")
            headers_dict["authorization"] = all_headers_dict.get("authorization")
            headers_dict["cookie"] = all_headers_dict.get("cookie")
            user_msg_list.append(headers_dict)
    route.continue_()

page.route(**/*,handler)
page.wait_for_load_state('networkidle')

最终代码

注意:对敏感信息已脱敏

# -*- coding: utf-8 -*-
'''
@Time : 2023/5/10 13:42
@Author : Vincent.xiaozai
@Email : Lvan826199@163.com
@File : demo06_整合请求伪造.py
'''
__author__ = "梦无矶小仔"

import json
import subprocess
import time
from datetime import datetime, timedelta
from pprint import pprint

import requests
from playwright.sync_api import Playwright,sync_playwright

# 全局变量
user_headers = {}
def my_request(request):
    all_headers_dict = request.all_headers()
    # 过滤请求
    if all_headers_dict.get(':path') == "/metrics:getCrashFreeTime..." and not user_headers:
        if all_headers_dict[":method"]=='POST' and all_headers_dict[":authority"]=='crashlytics-pa.clients6.google.com' :
            # 提取我需要的信息
            user_headers["cookie"] = all_headers_dict.get("cookie")
            user_headers["user-agent"] = all_headers_dict.get("user-agent")
            user_headers["authorization"] = all_headers_dict.get("authorization")
            user_headers["referer"] = all_headers_dict.get("referer")
            user_headers["origin"] = all_headers_dict.get("origin")

playwright = sync_playwright().start()
# 连接已打开浏览器,找好端口
browser = playwright.chromium.connect_over_cdp("http://127.0.0.1:9222")
default_context = browser.contexts[0] # 注意这里不是browser.new_page()了
page = default_context.pages[0]
page.on("requestfinished",my_request) # 创建拦截请求,获取请求的hearders
base_url = "https://console.firebase.google.com/u/0/project/..." #完整url已脱敏
page.goto(base_url) 
# 如果要保证刷新可以强制等待
page.wait_for_timeout(timeout=20000)

# 请求头伪造
headers = {
    "Host": "crashlytics-pa.clients6.google.com",
    "content-type": "application/json",
    'user-agent':user_headers["user-agent"],
    "referer": user_headers["referer"],
    'cookie':user_headers["cookie"],
    "origin": user_headers["origin"],
    'authorization': user_headers["authorization"]
}
print("---------------用户cookie及Authorization--------------------------")
print(f"伪造的请求头:{headers}")
print("---------------用户cookie及Authorization--------------------------")

## 执行request请求获取数据
crashAndUsersUrl = "https://crashlytics-pa.clients6.google.com/v1/projects" # 完整url已脱敏
crashAndUsersNum = get_all_crashAndUser(day=2,headers=headers,url=crashAndUsersUrl,the_latest="None",version_bt_list="None",platform='Android',eventType=["FATAL"])
print("----------接口信息打印-------------------")
print(crashAndUsersNum)

这里的get_all_crashAndUser就是我的业务代码,里面是对接口的请求进行了处理,这里就不放出来了。

可以看到最终我们拿到了这个接口的信息。

请添加图片描述

在之后的操作中,就可以一直使用requests进行接口请求了,如果cookie有使用有效期,那么每隔一段时间用playwright进行重新获取,重新伪造请求头就可以了。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值