Python + selenium + chromeDrive 解析浏览器接口响应值

Python + selenium + chromedriver 解析浏览器接口响应值


本文章内容禁供学习和交流切勿用作非法用途!

本案例以自动化答题项目来讲解如何通过selenium调用chromedriver解析接口响应值

开发环境

  • Windows 10 x64
  • Python 3.8
  • Pycharm 2019.3
  • Chrome 97.0.4692.99
  • Python相关依赖
    • reqyests (用于发送HTTP请求)
    • PIL (用于处理图片)
    • selenium (用于驱动Chrome浏览器)

分析过程

1. 观察页面中资源的加载过程

目标页面的URLhttp://dati.mwr.gov.cn/dxsgltl/templates/dxsgltl/answer.jsp?answer=1

浏览器的开发者调试工具网络(NewWork)这个页面下可以查看到在加载这个页面时,页面中的的内容的加载信息,通过对响应内容的大小和名称的观察,可以看到存在一个接口**http://dati.mwr.gov.cn/dxsgltl/handle/dt/loadRandomQuestion**

不出意外的话就是这个接口的响应值完成了页面中题目信息的加载了😆

在这里插入图片描述

2. 分析接口的内容

查看接口的返回值,通过查看接口的返回值,我们很容易的发现这个接口的返回值设计也很直观👍

这不就是传说中的谜底就在谜面上

在这里插入图片描述

​ 按照这个接口中的answer对应的选项尝试着对题目进行了答题,果不其然,100分

​ 也就是说只要获取到这个接口中的响应值,然后在根据响应值的内容进行答题就可以完成满分答题

​ 自动答题的逻辑是捋清楚了,但是这个页面需要通过完成登录才可以到达

​ 所以在解决这个问题之前还要解决登录的问题

3. 实现自动登录

自动登录我们可以借助selenium驱动chromeDrive来完成

  1. 来到登录页面 URLhttp://xxxx/xxxxx/xxxxx/xxxxx/login.jsp

在这里插入图片描述
通过找到姓名身份证两个input框对应的Xpath通过selenium中的find_element_by_xpath(xpath).send_keys(key)就可以完成d对应信息的输入

但是 😮 还有一个数字验证码,解决这个验证码的方式有主要有两种

  1. 手填
  2. 通过程序解决(图像识别等…)

具体怎么解决,自己选择

  1. 从获得接口的响应的数据

    上面我们提到了,谜底就谜面上只要我们获得了接口的响应值,我们就相当于获得了题目的答案

    通过查阅了一些资料,我发现可以利用chromeDrive通过network来获取response.body

    从而获得那个接口的响应数据,进而解析出接口中的答案,具体的实现过程见下面的代码

代码编写

1. 注册chrome的驱动

因为selenium中没有提供对查询接口数据的函数,但是可以通过webdriver提供的API进行查询

使用的函数是Network.getResponseBody

  • Network.getResponseBody的参数是requestid

  • requestidwebdriver在执行每个请求过程中自动生成的唯一ID

    拿到requestid就能拿到对应的接口的返回数据

  • requestid的获取

    • 创建webdriver对象时配置相关设置,即可获取每个请求的日志信息

    • 对日志信息的检索可以找到对应的requestid

# 配置webdriver的相关参数
caps = {
    'browserName': 'chrome',
    'loggingPrefs': {
        'browser': 'ALL',
        'driver': 'ALL',
        'performance': 'ALL',
 },
    'goog:chromeOptions': {
        'perfLoggingPrefs': {
            'enableNetwork': True,
        },
        'w3c': False,
    },
}
# executable_path 可以指定驱动文件 chromedriver.exe 的路径
driver = webdriver.Chrome(desired_capabilities=caps,executable_path="chromedriver.exe")
# 设置窗口最大化
driver.maximize_window()
# 设置隐式等待(10秒)
driver.implicitly_wait(10)

2. 编写登录功能函数

1. 自动化输入姓名身份证号

通过浏览器定位到具体input输入框的xpath

在这里插入图片描述

利用selenium 中的find_element_by_xpath()函数定位到元素后根据不同的事件调用不同的函数

  • 点击.click()

  • 填写文本.send_keys(key)

访问页面,输入姓名和身份证号环节代码如下

driver.get(login_url)
print('正在执行自动化登陆...')
# 用户名
driver.find_element_by_xpath('//*[@id="userName"]').send_keys(user_name)
# 身份证号
driver.find_element_by_xpath('//*[@id="idCard"]').send_keys(id_card_num)
2. 验证码的获取

验证码的获取可通过多种方式,我所使用的方式是调用baidu图像识别接口完成验证码的识别

调用baidu图像识别的接口的方式本文不进行详细说明,有空在写😄

建议手动输入完成验证码

3. 达到答题页面

验证码输入完成后就可以来到下一个页面

直接点击开始答题按钮即可

 # 开始答题
 click_by_xpath('/html/body/div/div[1]/div/div/div[2]/form/table/tbody/tr[7]/td/div/button')

如果已经完成过了一次答题的记录,在下一个页面上会出现证书下载再答一次

4. 进入答题页面

​ 进入答题页面后需要**等待当前所有被发出的请求都得到响应**才能获取日志

	# 这里需要等待一下,等所有的接口都收到响应,才可以抓取到数据
	time.sleep(5)
	# 得到页面的所有的接口的log数据
	log_list = driver.get_log('performance')

此时得到的log_list是一个list对象
其中每一个元素的格式如下

  {
    "level": "INFO",
    "message": "{\"message\":{\"method\":\"Network.responseReceived\",\"params\":{\"frameId\":\"7F38488422C362C23121B0032A63211B\",\"hasExtraInfo\":true,\"loaderId\":\"053A0A4B77B5104685F03154CFEE68DF\",\"requestId\":\"12360.179\",\"response\":{\"connectionId\":31,\"connectionReused\":true,\"encodedDataLength\":191,\"fromDiskCache\":false,\"fromPrefetchCache\":false,\"fromServiceWorker\":false,\"headers\":{\"Connection\":\"Keep-Alive\",\"Content-Length\":\"46668\",\"Content-Type\":\"application/json;charset=UTF-8\",\"Date\":\"Tue, 22 Feb 2022 07:29:49 GMT\",\"Keep-Alive\":\"timeout=5, max=90\",\"Server\":\"iis\"},\"mimeType\":\"application/json\",\"protocol\":\"http/1.1\",\"remoteIPAddress\":\"219.142.62.58\",\"remotePort\":80,\"responseTime\":1.645515025854593e+12,\"securityState\":\"insecure\",\"status\":200,\"statusText\":\"\",\"timing\":{\"connectEnd\":-1,\"connectStart\":-1,\"dnsEnd\":-1,\"dnsStart\":-1,\"proxyEnd\":-1,\"proxyStart\":-1,\"pushEnd\":0,\"pushStart\":0,\"receiveHeadersEnd\":167.98,\"requestTime\":2379.742195,\"sendEnd\":0.369,\"sendStart\":0.201,\"sslEnd\":-1,\"sslStart\":-1,\"workerFetchStart\":-1,\"workerReady\":-1,\"workerRespondWithSettled\":-1,\"workerStart\":-1},\"url\":\"http://dati.mwr.gov.cn/dxsgltl/handle/dt/loadRandomQuestion\"},\"timestamp\":2379.910729,\"type\":\"XHR\"}},\"webview\":\"7F38488422C362C23121B0032A63211B\"}",
    "timestamp": 1645515030579
 }

其中message字段对应的是另一个字符串格式的json对象

取出message对应的值进行转义后可得到如下格式的json对象

{
  "message": {
    "method": "Network.responseReceived",
    "params": {
      "frameId": "7F38488422C362C23121B0032A63211B",
      "hasExtraInfo": true,
      "loaderId": "053A0A4B77B5104685F03154CFEE68DF",
      "requestId": "12360.179",
      "response": {
        "connectionId": 31,
        "connectionReused": true,
        "encodedDataLength": 191,
        "fromDiskCache": false,
        "fromPrefetchCache": false,
        "fromServiceWorker": false,
        "headers": {
          "Connection": "Keep-Alive",
          "Content-Length": "46668",
          "Content-Type": "application/json;charset=UTF-8",
          "Date": "Tue, 22 Feb 2022 07:29:49 GMT",
          "Keep-Alive": "timeout=5, max=90",
          "Server": "iis"
        },
        "mimeType": "application/json",
        "protocol": "http/1.1",
        "remoteIPAddress": "219.142.62.58",
        "remotePort": 80,
        "responseTime": 1.645515025854593e+12,
        "securityState": "insecure",
        "status": 200,
        "statusText": "",
        "timing": {
          "connectEnd": -1,
          "connectStart": -1,
          "dnsEnd": -1,
          "dnsStart": -1,
          "proxyEnd": -1,
          "proxyStart": -1,
          "pushEnd": 0,
          "pushStart": 0,
          "receiveHeadersEnd": 167.98,
          "requestTime": 2379.742195,
          "sendEnd": 0.369,
          "sendStart": 0.201,
          "sslEnd": -1,
          "sslStart": -1,
          "workerFetchStart": -1,
          "workerReady": -1,
          "workerRespondWithSettled": -1,
          "workerStart": -1
        },
        "url": "http://dati.mwr.gov.cn/dxsgltl/handle/dt/loadRandomQuestion"
      },
      "timestamp": 2379.910729,
      "type": "XHR"
    }
  },
  "webview": "7F38488422C362C23121B0032A63211B"
}

对象中的message.method的值存在多种类型例如Network.requestWillBeSentNetwork.responseReceived

其中只有menthd的值为Network.responseReceived的对象才是对应的是接口响应数据的对象

根据在最开始的分析,我们可以确定我们要找的是请求路径为http://dati.mwr.gov.cn/dxsgltl/handle/dt/loadRandomQuestion的接口的响应数据

结合log对象中的message字段对应的对象的结构,可以找到message.params.response.url记录的数据就是接口的请求URL

根据以上的分析对log_list数据进行过滤即可,代码如下:

request_url = 'http://xxxxx/xxxxx/xxxxx/xxxxx/loadRandomQuestion'
# 遍历列表中的对象,寻找需要解析的接口数据
for item in log_list:
    # message同时也是一个json对象
    message = item['message']
    # 解析message
    message_dict = json.loads(message)['message']
    # 得到params对象
    params_dict = message_dict['params']
    network_method = message_dict.get('method')
    # 判断params中是否存在response
    if network_method == 'Network.responseReceived':
        response_dict = params_dict.get('response')
        url_info = response_dict['url']
        # 判断url是不是我们要找的题目的url
        if request_url == url_info:
            request_id = params_dict['requestId']
            print('题目数据接口的请求ID = '+request_id)

此时我们已经得到我们需要的接口对应的requestid

5. 根据requestid从日志中获取接口响应的数据
# 传入request_id 获得响应的数据
content = driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': request_id})
# 获取body对应的数据
response_body = content['body']
print("接口数据解析成功!")

截止目前我们已经获得到了http://xxxxx/xxxxx/xxxxx/xxxxx/loadRandomQuestion的响应数据

下一步需要将返回值的进行处理,结合selenium完成自动对正确选项的勾选

3.点击正确的选项

1. 分析接口中答案和题目类型之间的关系

接口的相应数据格式如下 (数据经过简化)

{
  "message": "ok",
  "result": {
    "answer": {
    },
    "questions": [
      {
        "answer": "D",
        "createTime": "2022-01-05 15:56:57",
        "createTimeStr": "2022-01-05 15:56:57",
        "difficulty": 3,
        "id": "5dbfb524d3c34019bd61d4057b15e3dc",
        "knowledgePoint": 12,
        "knowledgePointName": "",
        "name": "()的地下水,是指与大气降水和地表水体没有密切水力联系,无法补给或者补给非常缓慢的地下水。",
        "options": [
          {
            "content": "浅层以下",
            "createTime": "2022-01-05 15:56:58",
            "id": "0eca09b3c5c748efbf6a368318f9444f",
            "question": null,
            "sortNo": "A",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          },
          {
            "content": "深层",
            "createTime": "2022-01-05 15:56:58",
            "id": "4e7cb8e3e3b4458390e65f939cb1476e",
            "question": null,
            "sortNo": "B",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          },
          {
            "content": "缓慢更新",
            "createTime": "2022-01-05 15:56:58",
            "id": "864a10198cfb4978aa8e9e177df3df33",
            "question": null,
            "sortNo": "C",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          },
          {
            "content": "难以更新",
            "createTime": "2022-01-05 15:56:58",
            "id": "5bc380f665c64936ad246df5023dc34f",
            "question": null,
            "sortNo": "D",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          }
        ],
        "quType": 1,
        "sortNo": 0,
        "status": 0,
        "updateTime": "2022-01-05 15:56:57"
      },
      {
        "answer": "A|B|C|D",
        "createTime": "2022-01-05 15:56:57",
        "createTimeStr": "2022-01-05 15:56:57",
        "difficulty": 3,
        "id": "f359a081c4704598a2f011ae9357803f",
        "knowledgePoint": 12,
        "knowledgePointName": "",
        "name": "《xxxxx》中明确,坚持节水优先,统筹()、()、()、(),优化水资源配置格局,提升配置效率。",
        "options": [
          {
            "content": "地表水与地下水",
            "createTime": "2022-01-05 15:56:58",
            "id": "f191dd0bb2e34a0fa9d0e71eabff8ea4",
            "question": null,
            "sortNo": "A",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          },
          {
            "content": "天然水与再生水",
            "createTime": "2022-01-05 15:56:58",
            "id": "5bde2b80d17f4d24890c7050a10fcd5c",
            "question": null,
            "sortNo": "B",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          },
          {
            "content": "当地水与外调水",
            "createTime": "2022-01-05 15:56:58",
            "id": "96d5e55b102840dda36cb3ddf85a1258",
            "question": null,
            "sortNo": "C",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          },
          {
            "content": "常规水与非常规水",
            "createTime": "2022-01-05 15:56:58",
            "id": "89d422ae2a2f446d83612eb3e3d5c2e2",
            "question": null,
            "sortNo": "D",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          }
        ],
        "quType": 2,
        "sortNo": 0,
        "status": 0,
        "updateTime": "2022-01-05 15:56:57"
      },
      {
        "answer": "A",
        "createTime": "2022-01-05 15:56:57",
        "createTimeStr": "2022-01-05 15:56:57",
        "difficulty": 3,
        "id": "0b64195829b34442976acdbfc5268a5e",
        "knowledgePoint": 12,
        "knowledgePointName": "",
        "name": "xxxxx》提出,开展地下水超采综合治理行动,加大中下游地下水超采漏斗治理力度,逐步实现重点区域地下水采补平衡。",
        "options": [
          {
            "content": "正确",
            "createTime": "2022-01-05 15:56:58",
            "id": "125e797508564f1b978d48c20e337240",
            "question": null,
            "sortNo": "A",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          },
          {
            "content": "错误",
            "createTime": "2022-01-05 15:56:58",
            "id": "b522fbef81254f64906027534c4e6f8c",
            "question": null,
            "sortNo": "B",
            "status": 0,
            "updateTime": "2022-01-05 15:56:58"
          }
        ],
        "quType": 3,
        "sortNo": 0,
        "status": 0,
        "updateTime": "2022-01-05 15:56:57"
      }
    ]
  },
  "returncode": 0
}

其中result.questions字段的值为包含题目中的数据列表

该对象中answer字段为正确选项的结果

options为题目中的选项

quType题目的类型

其中不同的题目类型对应的其他数据的值也不同

题目类型quTypeanswer的值options数量(选项数量)
单选1A4
多选2A | B | C | D4
判断3A2

题目中全部选项的数量 = questions中每个对象的options的长度的累加

为了后续可以更加方便的用selenium对页面中的DOM元素数据进行遍历

先构造一个二位数组对每个答案进行封装

    如果在单选中答案为A
        则结果为 [1,0,0,0]
    如果多选答案为 A B C
        则结果为 [1,1,1,0]
    如果判断题答案为 对
        则结果为 [1,0]

为了让后续遍历跟家的顺利,将二维数组转换成一维数组

数组的长度 = 页面中全部的题目的数量

如果某个选项是正确选项则对应数组中值为1反之数组中的值为0

代码如下

response_body = json.loads(response_body)
# 得到result
result_dict = response_body['result']
# 题目列表
question_list = result_dict['questions']
# 遍历题目列表得到答案
answer_result = []
for item in question_list:
    # 得到答案
    answer_str = item['answer']
    # 对答案进行拆分
    answer_list = str(answer_str).split("|")
    # 得到题目的类型
    question_type = item['quType']
    # 得到选项
    question_option = item['options']
    # 构造选项结果
    item_answer_list = []
    # 遍历选项列表
    for option_index in range(len(question_option)):
        # 跳出循环的条件
        flag = True
        # 得到选项对象
        option_item = question_option[option_index]['sortNo']
        # 遍历答案列表
        for answer_index in range(len(answer_list)):
            # 得到答案对象
            answer_item = answer_list[answer_index]
            # 判断两个元素是否一致
            if option_item == answer_item:
                item_answer_list.append(1)
                flag = False
        # 判断跳出循环的情况
        if flag:
            item_answer_list.append(0)
    answer_result.append(item_answer_list)

# 将二维数组变成一维数组
answer_result_list = []
for item_list in answer_result:
    for item in item_list:
        answer_result_list.append(item)
return answer_result_list
2. 点击正确的选项

通过上面的操作已经得到了每个每个选项是否是正确答案的结果集

得到页面上的全部的选项的对象后就可开始对数据的遍历操作

代码如下

# 得到页面上所有的选项
check_list = driver.find_elements_by_tag_name("input")
"""
此时页面上的列表中的元素的个数
和answer_result_list的个数是一样的
所以answer_result_list的下标进行遍历即可
"""
for item_index in range(len(answer_result_list)):
    # 得到每个元素的值
    answer_item = answer_list[item_index]
    # 点击选项
    if answer_item:
        check_list[item_index].click()

代码运行结束后,页面中的所有的正确答案已经被选中

后续再次定位需要点击的页面元素调用selenium的点击事件即可完成剩余操作

selenium调用chromedriver接口获得响应数据的相关引用自文章

https://blog.csdn.net/mxdzchallpp/article/details/106475193

感谢博主分享

  • 7
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Chrome Options是SeleniumChrome浏览器的一种参数配置选项。它允许我们在启动Chrome浏览器时设置各种选项,以实现不同的需求。 使用Chrome Options,我们可以完成以下任务: 1. 设置浏览器的浏览模式:我们可以通过设置"--headless"参数来启动Chrome无头模式,即在没有显示浏览器界面的情况下运行浏览器,这对于执行自动化测试很有用。 2. 设置浏览器的User-Agent:通过设置"--user-agent"参数,我们可以改变浏览器的User-Agent,以模拟不同的浏览器和设备。 3. 设置浏览器的下载路径:我们可以使用"--download.default_directory"参数来设置下载文件的默认保存路径。 4. 设置浏览器的语言:通过设置"--lang"参数,我们可以改变浏览器的语言环境,使其适应不同语言的网站。 5. 启用或禁用浏览器的扩展:通过设置"--disable-extensions"参数,我们可以禁用浏览器的扩展,或者通过"--load-extension"参数加载指定的扩展。 为了使用Chrome Options,我们首先需要导入selenium包中的Options类: ```python from selenium.webdriver.chrome.options import Options ``` 然后,我们可以创建Options对象,并设置所需的参数: ```python options = Options() options.add_argument("--headless") options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3") options.add_argument("--download.default_directory=C:/Downloads") ``` 最后,将Options对象作为参数传递给ChromeDriver的构造函数,即可在启动Chrome浏览器时应用这些参数配置: ```python driver = webdriver.Chrome(chrome_options=options) ``` 通过这种方式,我们可以充分利用Chrome Options的各种参数,对Chrome浏览器进行个性化设置,以满足我们不同场景下的需求。 ### 回答2: Python Selenium库提供了一些Chrome Options参数,可以在使用Chrome浏览器自动化测试时进行配置。下面是一些常用的Options参数及其用法: 1. --headless:设置无头模式,即在后台运行浏览器而不显示图形界面。可以提高测试效率,并节省计算资源。 2. --disable-gpu:禁用GPU加速。有些时候,GPU加速可能导致浏览器无法正常工作,禁用它可以解决一些兼容性问题。 3. --disable-extensions:禁用浏览器扩展程序。有些扩展程序可能会干扰自动化测试的进行,禁用它们可以提高测试的稳定性。 4. --disable-infobars:禁用浏览器顶部的信息栏。有些站点可能会通过弹出信息栏来干扰测试过程,禁用它可以避免干扰。 5. --start-maximized:启动时最大化浏览器窗口。默认情况下,启动时浏览器窗口是最小化的,通过设置这个参数可以让窗口最大化,方便观察测试过程。 6. --lang=xxx:设置浏览器的语言。有些站点的内容可能根据语言设置而变化,可以通过设置这个参数来模拟不同的语言环境。 以上只是常用的一些Options参数,实际上还有很多其他参数可以用来控制浏览器的行为。通过结合这些参数的使用,可以更加灵活地进行自动化测试,满足不同测试需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值