> 文档中心 > python实现刷问卷星份数(面向对象)

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实现问卷星刷份数的代码与文章(如下)

隔壁寝室刷问卷刷疯了icon-default.png?t=M3C8https://blog.csdn.net/knighthood2001/article/details/120175929?spm=1001.2014.3001.5502python自动化------问卷星刷问卷3.0版本icon-default.png?t=M3C8https://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 randomfrom selenium import webdriverimport timefrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.select import Select

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

import randomimport Sojumpfrom Sojump import Wenjuanxingimport schedule as schedule

三、具体代码讲解

3.1  Sojump.py

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

①元素定位分析

xpath

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

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

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

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

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

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

css_selector 

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

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

允许填空选项的css_selector 

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

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

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

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

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

'''适用的可能是问卷星网页无法右键,只能通过F12或者ctrl+shift+i快捷键进入开发者工具的问卷,并且打开开发者工具后会显示已在调试程序中暂停'''# 选项的xpathbase_xpaths = '//*[@id="divquestion{}"]/ul/li'# 选项的css_selectorbase_css_selectors = '#divquestion{} > ul > li:nth-child({})'# 允许填空选项的css_selectorinput_css_selectors = '#divquestion{} > ul > li:nth-child({}) > input.underline''''适用的可能是除了上面的情况的问卷星'''# 选项的xpathbase_xpaths = '//*[@id="div{}"]/div/div'# 选项的css_selectorbase_css_selectors = 'div{} > div.ui-controlgroup.column{} > div.ui-checkbox.checked'# 允许填空选项的css_selectorinput_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属性,从而能够通过智能检测。

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

隔壁寝室刷问卷刷疯了icon-default.png?t=M3C8https://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 webdriverfrom 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 randomfrom selenium import webdriverimport timefrom selenium.webdriver.common.by import Byfrom 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 = 0provinces = [    '北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江',    '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南',    '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州',    '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾',    '香港', '澳门']'''适用的可能是问卷星网页无法右键,只能通过F12或者ctrl+shift+i快捷键进入开发者工具的问卷'''# 选项的xpathbase_xpaths = '//*[@id="divquestion{}"]/ul/li'# 选项的cssbase_css_selectors = '#divquestion{} > ul > li:nth-child({})'# 允许填空选项的css_selectorinput_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 randomimport Sojumpfrom Sojump import Wenjuanxingimport schedule as schedulerestrictive_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 scheduledef 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)

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


四、总结

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

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

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

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

牙刷城