出于学习目的,我多填了几个问卷

个人日志 做棵大树 1个月前 (03-09) 49次浏览 0个评论
文章目录[隐藏]

为了填写问卷星,我又拿起了 Python

讲真,Python可做的 Java 也可以,不过主要在于 Java 的依赖生态不及 Python 完善,所以可能会更耗时一些,但是不代表 Java 比 Python 弱噢

背景

学校发了个表单,让填写一下

分析历程

出于好奇和学习的心态,我想试下能不能自动填写表单;首先我想的是 JavaScript,因为毕竟是网页,所以首选的还是 JS,之后我创建了个 TEST 的表单进行测试,比较每次请求的路径与参数。

首先 WJ 星 的页面里打开控制台后会进入 debug 模式,我们需要 deactivate 掉断点才可以继续操作。Firefox 操作后会造成页面卡死,伤心,我这么爱它它却让我去找 Chrome...

出于学习目的,我多填了几个问卷

错途

起初因为手速比较慢,只能拿到投票完之后的 url:https://www.wjx.cn/wjx/join/complete.aspx?activityid=109478233&joinid=108761857258&sojumpindex=1&comsign=135A7EC8CFEF9AFBE716B45BC4422FF56E5CB4F0&s=&njqj=1 ;经过多次比对,发现其中的 joinid comsign 两个字段会发生变化,我以为这个对上后会发现生成所依赖的接口或者文件,这样之后通过生成这两个字段,循环就能反复刷票了。

后来发现自己 too simple 了,这条接口根本改不了投票的结果,也就是不作数!

之后提高手速,拿到了发送请求的接口地址:https://www.wjx.cn/joinnew/processjq.ashx? 一堆参数,如下所示

出于学习目的,我多填了几个问卷

其中的核心参数是 jqparam 以及 jqsign。具体怎么生成的呢?参考一篇 52 上的分析,走了一遍。

参考文章:对问卷星参数 jqparam 的分析和探索 https://www.52pojie.cn/forum.php?mod=viewthread&tid=1361387&highlight=%CE%CA%BE%ED%D0%C7 我这里就不再赘述了。

简言之就是使用插件重定向了 WJ 星 远程访问的接口,然后本地去生成 jqparam,我试了试效果不大行。

那就手动自动吧

折腾完前边的之后发现走不过去,那就模拟点击吧~

于是我想到了 Selenium ,大家现在也都在上人工智能的课,不管听没听,至少电脑上都该有个Python的环境吧~

pip install selenium  # 下载 selenium 到本地

之后我们就有了基本,但是想要实现模拟点击,我们还需要下载浏览器驱动,Firefox 抛弃了我,所以只好下载 Chrome 了。

驱动下载地址附上:http://chromedriver.storage.googleapis.com/index.html ,选择和自己浏览器大版本匹配的下载就可,下载解压后会得到一个 chromedriver.exe 复制到自己的Python安装目录下,这样默认就可以找到,不用再传参数了。

上边就绪后,我们就来对页面进行分析,有点类似于 Beautiful Soup

出于学习目的,我多填了几个问卷

然后分析页面,我们发现,问卷主体内容都在 id="ctl00_ContentPlaceHolder1_JQ1_question" 的 div 盒子下,于是我们确定了根节点。之后我们再看第一个问题的位置

出于学习目的,我多填了几个问卷

是在根目录下的 第二个div 下的 第一个fieldset 下的 第一个div ;然后我们定位一个问题的选项,是在再往下一级的 ulli 中。于是,我们有了定位的路径。

 selection1 = '//*[@id="ctl00_ContentPlaceHolder1_JQ1_question"]/div[2]/fieldset[1]/div[1]/div[2]/ul/li'

之后我们通过 webdriver.Chrome 来定位到 li ,我们发现实际上返回的是个列表

[<selenium.webdriver.remote.webelement.WebElement (session="93b4af8577a5b18396ea581abaa417ca", element="3ddfb20d-a1ef-4811-b49d-d50f70ab992f")>, <selenium.webdriver.remote.webelement.WebElement (session="93b4af8577a5b18396ea581abaa417ca", element="7db65e3e-2779-4180-be83-669ac2db1192")>]

我们取出最后一个,然后执行 click() 操作,即可选中。

    answer_1 = driver.find_elements_by_xpath(selection1)[-1]
    answer_1.click()

其他选项同理,复选框也如此。最后我们选择提交即可。

以为到这里就结束了?我当时也这么觉得,但是我们在提交后会出现进行人机核验的弹窗,需要我们额外处理下,同时还需要处理下拖动滑块验证。

出于学习目的,我多填了几个问卷

在实践检验真理的时候,发现了问题所在 在进行智能验证时,WJ 星会识别 Selenium!,看来人家早就料到了啊。

至于说怎么识别的呢?使用 selenium 的话,浏览器控制台会多出一个属性 window.navigator.webdriver 会变为 true,而当我们正常访问时它是 undefined 的。于是我们需要隐藏掉这个属性,新版本的 Chrome (79 以后的) 需要通过 js 来进行隐藏,具体代码放在下方

driver = Chrome('./chromedriver')
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
  "source": """
    Object.defineProperty(navigator, 'webdriver', {
      get: () => undefined
    })
  """
})

经过之后,这个表单终于可以提交了,先设置个次数?

for index in range(1, 200):

完整代码如下:

import time
import random
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_experimental_option('excludeSwitches', ['enable-automation'])

def autoFinishForm():
    # 将问卷星网站放在下面
    driver.get('群里的投票地址)

    # 单选题 1
    selection1 = '//*[@id="ctl00_ContentPlaceHolder1_JQ1_question"]/div[2]/fieldset[1]/div[1]/div[2]/ul/li'
    # print("log driver.find_elements_by_xpath(selection1): ", driver.find_elements_by_xpath(selection1));
    answer_1 = driver.find_elements_by_xpath(selection1)[-1]
    answer_1.click()

    # 单选题 2
    selection2 = '//*[@id="ctl00_ContentPlaceHolder1_JQ1_question"]/div[2]/fieldset[1]/div[2]/div[2]/ul/li'
    answer_2 = driver.find_elements_by_xpath(selection2)[-1]
    answer_2.click()

    # 单选题 3
    selection3 = '//*[@id="ctl00_ContentPlaceHolder1_JQ1_question"]/div[2]/fieldset[1]/div[3]/div[2]/ul/li'
    answer_3 = driver.find_elements_by_xpath(selection3)[-1]
    answer_3.click()

    # 提交按钮
    submit = driver.find_elements_by_id('submit_button')[0]
    submit.click()

    # 提交后的验证弹窗
    alterPath = '//*[@id="alert_box"]/div[2]/div[2]/div[2]/button'
    checkButton = driver.find_elements_by_xpath(alterPath)[0]
    checkButton.click()

    # 智能验证按钮
    captchaout = '//*[@id="captchaout"]/div[1]'
    captchaoutBtn = driver.find_elements_by_xpath(captchaout)[0]
    captchaoutBtn.click()

    time.sleep(3)

    # 拖动滑块的反向操作:关掉再点 SM_POP_CLOSE_1
    # closeslipper = '//*[@id="submit_div"]/div[1]/div[1]/div[1]/div[2]/div[1]/div[1]/div[1]/span'
    # closeslipperBtn = driver.find_elements_by_xpath(closeslipper)[0]
    closeslipperBtn = driver.find_elements_by_xpath("//div[@id='SM_POP_CLOSE_1' and @class='sm-pop-close']")[0]
    closeslipperBtn.click()
    captchaoutBtn.click()

    time.sleep(3)
    driver.quit()

if __name__ == '__main__':  
    # 循环 200 次
    for index in range(1, 200):
        driver = webdriver.Chrome(options=option)
        driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                Object.defineProperty(navigator, 'webdriver', {
                get: () => undefined
                })
            """
        })
        autoFinishForm()

做棵大树 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明出于学习目的,我多填了几个问卷
喜欢 (1)
[欢迎投币]
分享 (0)
关于作者:
一个整天无所事事的,有时候忽然热血的孩子
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址