python实现刷问卷星份数(面向对象)

目录

一、前言

二、模块准备

①Sojump.py需要的模块/包

②具体问卷.py需要的模块/包

三、具体代码讲解

3.1  Sojump.py

功能1 计数器counter()

功能2 伪装selenium

更新知识 selenium的更新

01 danxuan()→单选题(随机选择)

02 fixed_danxuan()→单选题(只选择某个选项)

03 excluded_danxuan()→单选题(排除一个或一些选项)

04 range_danxuan()→单选题(在m到n范围内单选)

05 restrictive_danxuan()→单选题(在某些选项中选择)

06 textinput_danxuan()→单选题(选项中允许填空)

07 duoxuan()→多选题(随机选择)

08 fixed_duoxuan()→多选题(只选择某些选项)

09 excluded_duoxuan()→多选题(排除一个或一些选项)

10 restrictive_duoxuan()→多选题(在某些选项中多选)

11 range_duoxuan()→多选题(在m到n范围内的多选)

12 textinput_duoxuan()→多选题(选项中允许填空)

13 text()→文本题

随机所在城市的选择  random_city_selection()

限制所在城市的选择  restrictive_city_selection()

提交按钮  submit()

★全部代码展示★

3.2  具体问卷.py

★全部代码展示★

具体代码讲解

四、总结


一、前言

        笔者之前也写过python实现问卷星刷份数的代码与文章(如下)

隔壁寝室刷问卷刷疯了https://blog.csdn.net/knighthood2001/article/details/120175929?spm=1001.2014.3001.5502python自动化------问卷星刷问卷3.0版本https://blog.csdn.net/knighthood2001/article/details/120175929?spm=1001.2014.3001.5502       

        不过之前写的属于面向过程版本,它有很大的局限性,即根据不同的问卷需要按照逻辑步骤写相应的代码,代码量非常繁琐,并且很多代码都是重复的,因此如果后续查看与更改起来也非麻烦;此外,由于距离上一次写关于问卷星的时间较长,问卷星页面元素定位可能发生了更改,需要重新获取相应的元素定位,如xpath、css selector;再加上由于selenium的升级,之前定位元素的方法被弃用,需要改变之前写的代码。

        以上三个原因,使得笔者重新对之前的代码进行增删改查,最终将之前的面向过程版本写成了面向对象版本。通过将一个个的功能封装成函数,在使用时只需要调用即可。代码量大大降低,且无需重复造车轮,对于后续检查与更改来说也比较轻松。

二、模块准备

        首先创建两个python文件,Sojump的翻译就是问卷星,通过在Sojump.py文件中编写主要函数,在具体问卷.py文件中通过导入Sojump,在编写该问卷的代码,最终实现刷份数。

①Sojump.py需要的模块/包

import random
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

②具体问卷.py需要的模块/包

import random
import Sojump
from Sojump import Wenjuanxing
import schedule as schedule

三、具体代码讲解

3.1  Sojump.py

!这里的内容是重中之重!

①元素定位分析

xpath

//*[@id="divquestion1"]/ul/li[1]

        上述表示的是问卷星问卷第一题的第1个选项

//*[@id="divquestion1"]/ul/li[2]

        上述表示的是问卷星问卷第一题的第2个选项

        经观察,可以得出问卷星问题的通用xpath,如下

# 选项的xpath
base_xpaths = '//*[@id="divquestion{}"]/ul/li'

css_selector 

        大致思路同上,得到问卷星选项的通用css_selector,如下

# 选项的css_selector
base_css_selectors = '#divquestion{} > ul > li:nth-child({})'

允许填空选项的css_selector 

        有些问题的选项允许填空,其通用css_selector 如下

# 允许填空选项的css_selector
input_css_selectors = '#divquestion{} > ul > li:nth-child({}) > input.underline'

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!注意:笔者发现问卷星有两类问卷,其对应的元素定位有所不同。

具体区分如下(笔者也是在写完该文章后突然发现,大家可以自行去试试你的问卷元素定位属于哪一类,第二种由于没有相应的问卷,可能xpath和css_selector定位会发生错误,不过这不是重点,大家只需找到并更改即可)

如果大家发现无法刷问卷,可能就是元素定位出现了问题 

'''适用的可能是问卷星网页无法右键,只能通过F12或者
ctrl+shift+i快捷键进入开发者工具的问卷,并且打开开发者工具后会显示已在调试程序中暂停'''
# 选项的xpath
base_xpaths = '//*[@id="divquestion{}"]/ul/li'
# 选项的css_selector
base_css_selectors = '#divquestion{} > ul > li:nth-child({})'
# 允许填空选项的css_selector
input_css_selectors = '#divquestion{} > ul > li:nth-child({}) > input.underline'

'''适用的可能是除了上面的情况的问卷星'''
# 选项的xpath
base_xpaths = '//*[@id="div{}"]/div/div'
# 选项的css_selector
base_css_selectors = 'div{} > div.ui-controlgroup.column{} > div.ui-checkbox.checked'
# 允许填空选项的css_selector
input_css_selectors = 'tqq{}_{}'

②创建类并初始化

class Wenjuanxing(object):
    # 初始化
    def __init__(self, url):
        self.url = url

        创建一个Wenjuanxing()的类,并初始化url


接下来就开始编写函数了

功能1 计数器counter()

在类外面先定义一个count=0,然后使用下面的counter()函数,用以计数是第几次运行代码,刷了几次问卷。

# 计数器
    def counter(self):
        global count
        count += 1
        w = print("第{}次运行".format(count))
        return w

功能2 伪装selenium

        需要进行伪装的原因:笔者发现是因为问卷星的网页有反爬机制,它会检查你是不是通过selenium访问网页的。大多数情况下,检测基本原理是检测当前浏览器窗口下的window.navigator对象是否包含webdriver这个属性。因为在正常使用浏览器的情况下,这个属性是undefined,然而一旦我们使用了selenium,selenium会给window.navigator设置webdriver属性。很多网站就通过JS判断如果webdrive 属性存在,那就直接屏蔽。以下代码可以在每次页面加载之前就不会给window.navigator设置webdriver属性,从而能够通过智能检测。

详情可以看笔者写的之前的文章

隔壁寝室刷问卷刷疯了https://blog.csdn.net/knighthood2001/article/details/120175929?spm=1001.2014.3001.5502

# 伪装selenium
    def weizhuang_selenium(self):
        # 躲避智能检测
        self.option = webdriver.ChromeOptions()
        self.option.add_experimental_option('excludeSwitches', ['enable-automation'])
        self.option.add_experimental_option('useAutomationExtension', False)
        self.driver = webdriver.Chrome(options=self.option)
        self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument',
                                    {'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'})
        self.driver.get(self.url)

更新知识 selenium的更新

        chromedriver的知识我就不细说了,主要讲的是selenium更新后,元素定位的方法发生改变。

以前:

        模块

from selenium import webdriver

        定位元素方法 

# 1、通过ID进行定位
find_element_by_id('id')
# 2、通过名字进行定位
find_element_by_name('name')
# 3、通过类名进行元素定位
find_elements_by_class_name('class_name')
# 4、通过元素标签属性
find_element_by_tag_name('tag_name')
# 5、 通过页面文本信息
find_element_by_link_text('link_text')
# 6、通过模糊文本信息
find_element_by_partial_link_text('partial_link_text')
# 7、通过查找元素路径
find_element_by_xpath('xpath')
# 8、通过页面上的css元素进行定位
find_element_by_css_selector('css_selector')

更新后:

        需要在导入一个模块

from selenium import webdriver
from selenium.webdriver.common.by import By

        定位元素方法 

driver.find_element(by=By.ID, value='ID')
driver.find_element(by=By.NAME, value='NAME')
driver.find_element(by=By.CLASS_NAME, value='CLASS_NAME')
driver.find_element(by=By.TAG_NAME, value='TAG_NAME')
driver.find_element(by=By.LINK_TEXT, value='LINK_TEXT')
driver.find_element(by=By.PARTIAL_LINK_TEXT, value='PARTIAL_LINK_TEXT')
driver.find_element(by=By.XPATH, value='XPATH')
driver.find_element(by=By.CSS_SELECTOR, value='CSS_SELECTOR')

01 danxuan()→单选题(随机选择)

    # 01 单选题(随机选择)
    def danxuan(self, i):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, b)).click()

讲解:i表示题号

首先使用全局变量并通过format(i)函数给出具体的题号

a表示定位题目所对应的选项

len(a)表示题目所对应的选项个数

b表示随机生成选项个数以内的一个数

最后定位该选项并点击,完成随机选择
 

self.driver.find_element(by=By.CSS_SELECTOR,
                                 value='#divquestion{} > ul > li:nth-child({}) > label'.format(i, b)).click()

注意:在上面的代码,由于笔者在之前的文章中使用过xpath定位,不过出现了定位不到的现象,所以我使用css_selector进行定位元素。



02 fixed_danxuan()→单选题(只选择某个选项)

    # 02 单选题(只选择某个选项)
    def fixed_danxuan(self, i, b):
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, b)).click()

讲解:b表示需要选择的选项在该题目选项中的位置

03 excluded_danxuan()→单选题(排除一个或一些选项)

    # 03 单选题(排除一个或一些选项)
    def excluded_danxuan(self, i, *args):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        c = []
        # y是计算arg的个数,方便计算还剩几个选项
        y = 0
        for x in range(1, len(a) + 1):
            c.append(x)
        for arg in args:
            y += 1
            c.remove(arg)
        d = random.choice(c)
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, d)).click()

思路:是创建一个列表c,然后将选项个数添加进列表,之后根据输入的变量,在列表中逐个去除,最终在剩下的列表中随机选择一个,进行定位与点击。

04 range_danxuan()→单选题(在m到n范围内单选)

    # 04 单选题(在m到n范围内单选)
    def range_danxuan(self, i, m, n):
        x = random.randint(m, n)
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, x)).click()

思路:是用random.randint(m,n)函数返回一个m到n范围内的随机整数

05 restrictive_danxuan()→单选题(在某些选项中选择)

    # 05 单选题(在某些选项中选择)如6个选项,在1235中单选
    def restrictive_danxuan(self, i, *args):
        m = []
        for arg in args:
            m.append(arg)
        n = random.choice(m)
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, n)).click()

思路:将所输入的参数存入到列表中,然后使用random.choice()函数随机选择其中的一个数

06 textinput_danxuan()→单选题(选项中允许填空)

    # 06 单选题(选项中允许填空)
    def textinput_danxuan(self, i, c, wenzi):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, b)).click()
        if c == b:
            time.sleep(0.2)
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=input_css_selectors.format(i, c)).send_keys(wenzi)

 思路:首先选择某个选项,如果该选项和自己输入的c相同,即可再输入文字。

07 duoxuan()→多选题(随机选择)

    # 07 多选题(随机选择)
    def duoxuan(self, i):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = len(a)
        # m中存放选项
        m = []
        for x in range(1, b + 1):
            m.append(x)
        c = random.randint(1, b)
        n = random.sample(m, c)
        for o in n:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, o)).click()

思路:列表m中存放选项;c表示随机生成一个选项以内的数字,即多选题要选择选项的个数;n表示从列表m中随机选择c个个数,返回类型为列表,然后经过遍历,实现多选题的多个选项的选择。

08 fixed_duoxuan()→多选题(只选择某些选项)

    # 08 多选题(只选择某些选项)
    def fixed_duoxuan(self, i, *args):
        for arg in args:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, arg)).click()

09 excluded_duoxuan()→多选题(排除一个或一些选项)

    # 09 多选题(排除一个或一些的选项)
    def excluded_duoxuan(self, i, *args):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        # print(len(a))
        c = []
        # y是计算arg的个数,方便计算还剩几个选项
        y = 0
        for x in range(1, len(a)+1):
            c.append(x)
        for arg in args:
            y += 1
            c.remove(arg)
        # 还剩下几个选项
        z = len(a) - y
        # 多选题选项个数
        b = random.randint(1, z)
        # 多选题用sample()
        d = random.sample(c, b)
        for r in d:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, r)).click()

思路:列表c中存放选项个数,然后逐一删除输入的参数,然后在剩下的选项中用多选题的思路进行操作。 

10 restrictive_duoxuan()→多选题(在某些选项中多选)

    # 10 多选题(在某些选项中多选)
    def restrictive_duoxuan(self, i, *args):
        m = []
        for arg in args:
            m.append(arg)
        n = random.randint(1, len(m))
        o = random.sample(m, n)
        for q in o:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, q)).click()

思路:列表m中存放的内容就是所输入的参数, 然后用多选题的思路进行操作。

11 range_duoxuan()→多选题(在m到n范围内的多选)

    # 11 多选题(在m到n范围内的多选)
    def range_duoxuan(self, i, m, n):
        # 列表c为m到n的选项组,如当m=2,n=5时,c=[2,3,4,5]
        c = []
        for x in range(m, n+1):
            c.append(x)
        # 选项个数
        o = n - m + 1
        # 随机生成要填几个选项
        p = random.randint(1, o)
        d = random.sample(c, p)
        for r in d:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, r)).click()

 思路:列表c为m到n的选项组,如当m=2,n=5时,c=[2,3,4,5],然后用多选题的思路进行操作。

12 textinput_duoxuan()→多选题(选项中允许填空)

    # 12 多选题(选项中允许填空)
    def textinput_duoxuan(self, i, c, wenzi):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = len(a)
        # m中存放选项
        m = []
        for x in range(1, b + 1):
            m.append(x)
        # 随机生成要多选的选项个数
        o = random.randint(1, b)
        # 在选项中随机选取o个选项
        n = random.sample(m, o)
        for r in n:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, r)).click()
            if c == r:
                time.sleep(0.2)
                self.driver.find_element(by=By.CSS_SELECTOR,
                                         value=input_css_selectors.format(i, c)).send_keys(wenzi)

注意:该函数只适用于一个选项中需要文本输入的多选题。

思路:查看  06 单选题(选项中允许填空)和  07 多选题(随机选择)  即可理解。

13 text()→文本题

    # 13 文本题
    def text(self, i, wenzi):
        self.driver.find_element(by=By.CSS_SELECTOR, value='#q{}'.format(i)).send_keys(wenzi)

思路:通过wenzi参数填写相应的内容,实现文字题的填写。

随机所在城市的选择  random_city_selection()

 

 请选择您所在的城市的题目如上

 

        如上图所示, 问卷星还给出了海外这个选项,不过一般用不到,所以笔者就除去了它,剩下的列表并存在provinces中,之后用random函数即可随机选择城市。

        将它存到自制的列表还有一个原因就是selenium的Select提供的三种选择方式select_by_index(index)、select_by_value(value)、select_by_visible_text(visible_text),问卷星中只给出了value,因此很难随机选择。例如:如果给出index,那我们就可以使用random函数进行随机选择。

provinces = [
    '北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江',
    '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南',
    '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州',
    '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾',
    '香港', '澳门']

 

注意:

①上图中,省份与城市的选择页面是写在ifame中, 因此需要进行frame和iframe之间的切换。关键不要忘记切换回去。如果没有切换回去,即使点击确定键后,它仍在iframe中,会使得接下来元素定位失败。

②a表示省/直辖市下的城市。

    # 随机所在城市的选择
    def random_city_selection(self, i):
        frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
        frame.click()
        # 滚动到最底端
        # self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        self.driver.switch_to.frame('PDF_i_chezchenz')

        ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
        s = Select(ele)
        province = random.choice(provinces)
        s.select_by_value(province)
        a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
        # len(a)表示省下的城市有几个
        # print(len(a))
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
        self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
        # 注意切换回原来的frame
        self.driver.switch_to.parent_frame()

限制所在城市的选择  restrictive_city_selection()

    # 限制所在城市的选择
    def restrictive_city_selection(self, i, arg):
        frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
        frame.click()
        # 滚动到最底端
        # self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        self.driver.switch_to.frame('PDF_i_chezchenz')

        ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
        s = Select(ele)
        s.select_by_value(arg)
        a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
        # len(a)表示省下的城市有几个
        # print(len(a))
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
        self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
        # 注意切换回原来的frame
        self.driver.switch_to.parent_frame()

        以上代码与随机所在城市的选择的差别主要在于,添加了一个不定参数arg

通过在具体问卷.py文件中创立一个具有几个城市的列表,然后随机选择它并将其作为arg参数,实现随机选择你所需要的城市。

例如下面的代码:

        具体问卷.py

restrictive_provinces = ['云南', '贵州', '四川']
restrictive_province = random.choice(restrictive_provinces)

提交按钮  submit()

 

上图所示的就是提交按钮的流程 

按步骤写代码,如下

注意:有些步骤需要一点时间,所以加上time.sleep()

    # 提交按钮
    def submit(self):
        # time.sleep(0.5)
        btn = self.driver.find_element(by=By.CSS_SELECTOR, value='#submit_button')
        btn.click()
        # 出现点击验证码验证
        time.sleep(1)
        self.driver.find_element(by=By.XPATH, value='//*[@id="alert_box"]/div[2]/div[2]/div[2]/button').click()
        time.sleep(0.5)
        self.driver.find_element(by=By.XPATH, value='//*[@id="SM_BTN_1"]').click()
        time.sleep(4)
        # 关闭页面
        handles = self.driver.window_handles
        self.driver.switch_to.window(handles[0])
        # time.sleep(0.5)
        # # 刷新页面(可能不需要)
        # self.driver.refresh()
        # 关闭当前页面,如果只有一个页面,则也关闭浏览器
        self.driver.close()

以上就是Sojump.py的全部代码讲解


全部代码展示

# -*- coding: utf-8-*-
import random
from selenium import webdriver
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

"""
01 单选题(随机选择)
    danxuan()
02 单选题(只选择某个选项)
    fixed_danxuan()
03 单选题(排除一个或一些选项)
    excluded_danxuan()
04 单选题(在m到n范围内单选)
    range_danxuan()
05 单选题(在某些选项中选择)如6个选项,在1235中单选
    restrictive_danxuan()
06 单选题(选项中允许填空)
    textinput_danxuan() 
07 多选题(随机选择)
    duoxuan()
08 多选题(只选择某些选项)
    fixed_duoxuan()
09 多选题(排除一个或一些选项)
    excluded_duoxuan()
10 多选题(在某些选项中多选)
    restrictive_duoxuan()
11 多选题(在m到n范围内的多选)
    range_duoxuan()
12 多选题(选项中允许填空)
    text_input_duoxuan() 
13 文本题
    text()
"""
count = 0
provinces = [
    '北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江',
    '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南',
    '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州',
    '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾',
    '香港', '澳门']

'''适用的可能是问卷星网页无法右键,只能通过F12或者ctrl+shift+i快捷键进入开发者工具的问卷'''
# 选项的xpath
base_xpaths = '//*[@id="divquestion{}"]/ul/li'
# 选项的css
base_css_selectors = '#divquestion{} > ul > li:nth-child({})'
# 允许填空选项的css_selector
input_css_selectors = '#divquestion{} > ul > li:nth-child({}) > input.underline'

'''适用的可能是除了上面的情况的问卷星'''
# 选项的xpath
# base_xpaths = '//*[@id="div{}"]/div/div'
# 选项的css
# base_css_selectors = 'div{} > div.ui-controlgroup.column{} > div.ui-checkbox.checked'
# 允许填空选项的css_selector
# input_css_selectors = 'tqq{}_{}'

class Wenjuanxing(object):
    # 初始化
    def __init__(self, url):
        self.url = url

    # 计数器
    def counter(self):
        global count
        count += 1
        w = print("第{}次运行".format(count))
        return w

    # 伪装selenium
    def weizhuang_selenium(self):
        # 躲避智能检测
        self.option = webdriver.ChromeOptions()
        self.option.add_experimental_option('excludeSwitches', ['enable-automation'])
        self.option.add_experimental_option('useAutomationExtension', False)
        self.driver = webdriver.Chrome(options=self.option)
        self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument',
                                    {'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'})
        self.driver.get(self.url)

    # 01 单选题(随机选择)
    def danxuan(self, i):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, b)).click()


    # 02 单选题(只选择某个选项)
    def fixed_danxuan(self, i, b):
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, b)).click()

    # 03 单选题(排除一个或一些选项)
    def excluded_danxuan(self, i, *args):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        c = []
        # y是计算arg的个数,方便计算还剩几个选项
        y = 0
        for x in range(1, len(a) + 1):
            c.append(x)
        for arg in args:
            y += 1
            c.remove(arg)
        d = random.choice(c)
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, d)).click()

    # 04 单选题(在m到n范围内单选)
    def range_danxuan(self, i, m, n):
        x = random.randint(m, n)
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, x)).click()


    # 05 单选题(在某些选项中选择)如6个选项,在1235中单选
    def restrictive_danxuan(self, i, *args):
        m = []
        for arg in args:
            m.append(arg)
        n = random.choice(m)
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, n)).click()

    # 06 单选题(选项中允许填空)
    def textinput_danxuan(self, i, c, wenzi):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.CSS_SELECTOR,
                                 value=base_css_selectors.format(i, b)).click()
        if c == b:
            time.sleep(0.2)
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=input_css_selectors.format(i, c)).send_keys(wenzi)


    # 07 多选题(随机选择)
    def duoxuan(self, i):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = len(a)
        # m中存放选项
        m = []
        for x in range(1, b + 1):
            m.append(x)
        c = random.randint(1, b)
        n = random.sample(m, c)
        for o in n:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, o)).click()

    # 08 多选题(只选择某些选项)
    def fixed_duoxuan(self, i, *args):
        for arg in args:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, arg)).click()

    # 09 多选题(排除一个或一些的选项)
    def excluded_duoxuan(self, i, *args):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        # print(len(a))
        c = []
        # y是计算arg的个数,方便计算还剩几个选项
        y = 0
        for x in range(1, len(a)+1):
            c.append(x)
        for arg in args:
            y += 1
            c.remove(arg)
        # 还剩下几个选项
        z = len(a) - y
        # 多选题选项个数
        b = random.randint(1, z)
        # 多选题用sample()
        d = random.sample(c, b)
        for r in d:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, r)).click()

    # 10 多选题(在某些选项中多选)
    def restrictive_duoxuan(self, i, *args):
        m = []
        for arg in args:
            m.append(arg)
        n = random.randint(1, len(m))
        o = random.sample(m, n)
        for q in o:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, q)).click()


    # 11 多选题(在m到n范围内的多选)
    def range_duoxuan(self, i, m, n):
        # 列表c为m到n的选项组,如当m=2,n=5时,c=[2,3,4,5]
        c = []
        for x in range(m, n+1):
            c.append(x)
        # 选项个数
        o = n - m + 1
        # 随机生成要填几个选项
        p = random.randint(1, o)
        d = random.sample(c, p)
        for r in d:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, r)).click()


    # 12 多选题(选项中允许填空)
    def text_input_duoxuan(self, i, c, wenzi):
        global base_xpaths
        base_xpath = base_xpaths.format(i)
        a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
        b = len(a)
        # m中存放选项
        m = []
        for x in range(1, b + 1):
            m.append(x)
        # 随机生成要多选的选项个数
        o = random.randint(1, b)
        # 在选项中随机选取o个选项
        n = random.sample(m, o)
        for r in n:
            self.driver.find_element(by=By.CSS_SELECTOR,
                                     value=base_css_selectors.format(i, r)).click()
            if c == r:
                time.sleep(0.2)
                self.driver.find_element(by=By.CSS_SELECTOR,
                                         value=input_css_selectors.format(i, c)).send_keys(wenzi)

    # 13 文本题
    def text(self, i, wenzi):
        self.driver.find_element(by=By.CSS_SELECTOR, value='#q{}'.format(i)).send_keys(wenzi)

    # 随机所在城市的选择
    def random_city_selection(self, i):
        frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
        frame.click()
        # 滚动到最底端
        self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        self.driver.switch_to.frame('PDF_i_chezchenz')

        ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
        s = Select(ele)
        province = random.choice(provinces)
        s.select_by_value(province)
        a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
        # len(a)表示省下的城市有几个
        # print(len(a))
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
        self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
        # 注意切换回原来的frame
        self.driver.switch_to.parent_frame()

    # 限制所在城市的选择
    def restrictive_city_selection(self, i, arg):
        frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
        frame.click()
        # 滚动到最底端
        # self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
        self.driver.switch_to.frame('PDF_i_chezchenz')

        ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
        s = Select(ele)
        s.select_by_value(arg)
        a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
        # len(a)表示省下的城市有几个
        # print(len(a))
        b = random.randint(1, len(a))
        self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
        self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
        # 注意切换回原来的frame
        self.driver.switch_to.parent_frame()

    # 提交按钮
    def submit(self):
        # time.sleep(0.5)
        btn = self.driver.find_element(by=By.CSS_SELECTOR, value='#submit_button')
        btn.click()
        # 出现点击验证码验证
        time.sleep(1)
        self.driver.find_element(by=By.XPATH, value='//*[@id="alert_box"]/div[2]/div[2]/div[2]/button').click()
        time.sleep(0.5)
        self.driver.find_element(by=By.XPATH, value='//*[@id="SM_BTN_1"]').click()
        time.sleep(4)
        # 关闭页面
        handles = self.driver.window_handles
        self.driver.switch_to.window(handles[0])
        # time.sleep(0.5)
        # # 刷新页面(可能不需要)
        # self.driver.refresh()
        # 关闭当前页面,如果只有一个页面,则也关闭浏览器
        self.driver.close()


3.2  具体问卷.py

由于问卷涉及一些内容,不便公开,因此我就将问卷内容马赛克了(如下图),网址也不公布了。大家了解一下题目的类型即可

★全部代码展示★

# -*- coding: utf-8-*-
import random
import Sojump
from Sojump import Wenjuanxing
import schedule as schedule

restrictive_provinces = ['云南', '贵州', '四川']

problems_10_1 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']

problems_10_2 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']

if __name__ == '__main__':
    url = '问卷星网址'
    wenjuanxing = Wenjuanxing(url)
    def run():
        # 随机内容
        restrictive_province = random.choice(restrictive_provinces)
        problem_10_1 = random.choice(problems_10_1)
        problem_10_2 = random.choice(problems_10_2)
        # 计数器
        wenjuanxing.counter()
        # 伪装selenium
        wenjuanxing.weizhuang_selenium()
        # 问卷题目
        wenjuanxing.danxuan(1)
        wenjuanxing.danxuan(2)
        wenjuanxing.restrictive_danxuan(3, 2, 3, 4, 5)
        wenjuanxing.danxuan(4)
        wenjuanxing.danxuan(5)
        wenjuanxing.danxuan(6)
        wenjuanxing.duoxuan(7)
        wenjuanxing.danxuan(8)
        wenjuanxing.danxuan(9)
        # 第十题为两个选项都可以填空的单选题,因此尝试用try except
        try:
            wenjuanxing.textinput_danxuan(10, 1, problem_10_1)
            # print(problem_10_1)
            wenjuanxing.textinput_danxuan(10, 2, problem_10_2)
            # print(problem_10_2)
        except:
            pass
        wenjuanxing.duoxuan(11)
        wenjuanxing.danxuan(12)
        # 13得考虑一下
        # 第13题虽然是有五个选项的多选题,不过依据逻辑,它只能分成1 3 1 形式,
        # 即要么选择第一个选项,要么在中间三个选项之间进行多选,要么选择最后一个选项。
        a = random.randint(1, 3)
        if a == 1:
            wenjuanxing.fixed_danxuan(13, 1)
        elif a == 2:
            wenjuanxing.range_duoxuan(13, 2, 5)
        else:
            wenjuanxing.fixed_danxuan(13, 6)
        wenjuanxing.danxuan(14)
        wenjuanxing.duoxuan(15)
        wenjuanxing.duoxuan(16)
        wenjuanxing.danxuan(17)
        # 限制省份的随机选择
        # print(restrictive_province)
        wenjuanxing.restrictive_city_selection(18, restrictive_province)
        # time.sleep(3)
        # 提交问卷
        wenjuanxing.submit()
    # 每隔2秒运行
    schedule.every(2).seconds.do(run)
    # 判断条件
    while Sojump.count < 50:
        schedule.run_pending()



具体代码讲解

①每隔一段时间运行代码

import schedule as schedule
def run():
    pass
 
 
schedule.every(2).seconds.do(run)
 
while True:
    schedule.run_pending()

   该模板请牢记,上述代码表示每隔五秒运行一次,如果需要每隔一秒运行一次代码,则将其中的

schedule.every(2).seconds.do(run)改为
schedule.every(1).second.do(run)

由于count参数表示代码运行的次数,而问卷星在刷50份后得过一小时后才能继续刷,因此得加个判断(如下),防止程序一直运行。

    while Sojump.count < 50:

②编写主体代码

        因为一些题目的选项中需要填写文本,以及如果大家需要限定省份的随机随机选择。这就需要制作相应的列表,然后通过随机选取列表中的某个内容,自动填写进问卷。

restrictive_provinces = ['云南', '贵州', '四川']

problems_10_1 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']

problems_10_2 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']

这里需要注意的是: 随机选取列表中内容的代码段需要编写在run()函数中而不能直接写在上述代码下面,否则不经过run()函数,不会进行每隔一段时间运行的过程,其产生的随机内容将在每次使用都不变。这是笔者后来无意发现的,说来惭愧,这一个小失误,导致一次刷的50份的文本内容都一样,因此大家敲代码时需要多思考。

        接下来需要调用类

url = '问卷星网址'
wenjuanxing = Wenjuanxing(url)

        接下来就是编写run()函数,其中主要就是根据问卷内容、题目类型的不同而编写。

代码如下:

    def run():
        # 随机内容
        restrictive_province = random.choice(restrictive_provinces)
        problem_10_1 = random.choice(problems_10_1)
        problem_10_2 = random.choice(problems_10_2)
        # 计数器
        wenjuanxing.counter()
        # 伪装selenium
        wenjuanxing.weizhuang_selenium()
        # 问卷题目
        wenjuanxing.danxuan(1)
        wenjuanxing.danxuan(2)
        wenjuanxing.restrictive_danxuan(3, 2, 3, 4, 5)
        wenjuanxing.danxuan(4)
        wenjuanxing.danxuan(5)
        wenjuanxing.danxuan(6)
        wenjuanxing.duoxuan(7)
        wenjuanxing.danxuan(8)
        wenjuanxing.danxuan(9)
        # 第十题为两个选项都可以填空的单选题,因此尝试用try except
        try:
            wenjuanxing.textinput_danxuan(10, 1, problem_10_1)
            # print(problem_10_1)
            wenjuanxing.textinput_danxuan(10, 2, problem_10_2)
            # print(problem_10_2)
        except:
            pass
        wenjuanxing.duoxuan(11)
        wenjuanxing.danxuan(12)
        # 13得考虑一下
        # 第13题虽然是有五个选项的多选题,不过依据逻辑,它只能分成1 3 1 形式,
        # 即要么选择第一个选项,要么在中间三个选项之间进行多选,要么选择最后一个选项。
        a = random.randint(1, 3)
        if a == 1:
            wenjuanxing.fixed_danxuan(13, 1)
        elif a == 2:
            wenjuanxing.range_duoxuan(13, 2, 5)
        else:
            wenjuanxing.fixed_danxuan(13, 6)
        wenjuanxing.danxuan(14)
        wenjuanxing.duoxuan(15)
        wenjuanxing.duoxuan(16)
        wenjuanxing.danxuan(17)
        # 限制省份的随机选择
        # print(restrictive_province)
        wenjuanxing.restrictive_city_selection(18, restrictive_province)
        # time.sleep(3)
        # 提交问卷
        wenjuanxing.submit()

        着重注意一下以下两题

第10题为两个选项都可以填空的单选题,因此尝试用try except。
第13题虽然是有五个选项的多选题,不过依据逻辑,它只能分成1 3 1 形式,
即要么选择第一个选项,要么在中间三个选项之间进行多选,要么选择最后一个选项。

此外,如果要进行随机省份的选择,只需调用即可(如下)

wenjuanxing.random_city_selection(18)

剩下的其他函数在之前的内容中讲过,也比较简单。 


四、总结

        问卷星根据题目的不同、题目和选项的搭配能产生很多变化,上述只涉及到几个较常用的几个函数。后续大家如果需要实现一些其他功能的,可以自行编写,也可以与笔者多多交流,完善该模块。

        最后希望大家不要光看文章,也可以动手敲敲代码,加深理解。

        如果大家发现无法刷问卷,可能就是元素定位出现了问题,可以去看看元素定位属于上述讲的哪一类。

        如有错误之处,请批评指正!!

  • 31
    点赞
  • 119
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

knighthood2001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值