当前位置:   article > 正文

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

隔壁寝室刷问卷刷疯了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需要的模块/包

  1. import random
  2. from selenium import webdriver
  3. import time
  4. from selenium.webdriver.common.by import By
  5. from selenium.webdriver.support.select import Select

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

  1. import random
  2. import Sojump
  3. from Sojump import Wenjuanxing
  4. import schedule as schedule

三、具体代码讲解

3.1  Sojump.py

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

①元素定位分析

xpath

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

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

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

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

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

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

css_selector 

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

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

允许填空选项的css_selector 

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

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

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

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

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

  1. '''适用的可能是问卷星网页无法右键,只能通过F12或者
  2. ctrl+shift+i快捷键进入开发者工具的问卷,并且打开开发者工具后会显示已在调试程序中暂停'''
  3. # 选项的xpath
  4. base_xpaths = '//*[@id="divquestion{}"]/ul/li'
  5. # 选项的css_selector
  6. base_css_selectors = '#divquestion{} > ul > li:nth-child({})'
  7. # 允许填空选项的css_selector
  8. input_css_selectors = '#divquestion{} > ul > li:nth-child({}) > input.underline'
  9. '''适用的可能是除了上面的情况的问卷星'''
  10. # 选项的xpath
  11. base_xpaths = '//*[@id="div{}"]/div/div'
  12. # 选项的css_selector
  13. base_css_selectors = 'div{} > div.ui-controlgroup.column{} > div.ui-checkbox.checked'
  14. # 允许填空选项的css_selector
  15. input_css_selectors = 'tqq{}_{}'

②创建类并初始化

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

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


接下来就开始编写函数了

功能1 计数器counter()

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

  1. # 计数器
  2. def counter(self):
  3. global count
  4. count += 1
  5. w = print("第{}次运行".format(count))
  6. 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

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

更新知识 selenium的更新

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

以前:

        模块

from selenium import webdriver

        定位元素方法 

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

更新后:

        需要在导入一个模块

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

        定位元素方法 

  1. driver.find_element(by=By.ID, value='ID')
  2. driver.find_element(by=By.NAME, value='NAME')
  3. driver.find_element(by=By.CLASS_NAME, value='CLASS_NAME')
  4. driver.find_element(by=By.TAG_NAME, value='TAG_NAME')
  5. driver.find_element(by=By.LINK_TEXT, value='LINK_TEXT')
  6. driver.find_element(by=By.PARTIAL_LINK_TEXT, value='PARTIAL_LINK_TEXT')
  7. driver.find_element(by=By.XPATH, value='XPATH')
  8. driver.find_element(by=By.CSS_SELECTOR, value='CSS_SELECTOR')

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

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

讲解:i表示题号

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

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

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

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

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

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

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



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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  1. # 07 多选题(随机选择)
  2. def duoxuan(self, i):
  3. global base_xpaths
  4. base_xpath = base_xpaths.format(i)
  5. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  6. b = len(a)
  7. # m中存放选项
  8. m = []
  9. for x in range(1, b + 1):
  10. m.append(x)
  11. c = random.randint(1, b)
  12. n = random.sample(m, c)
  13. for o in n:
  14. self.driver.find_element(by=By.CSS_SELECTOR,
  15. value=base_css_selectors.format(i, o)).click()

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

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

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

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

  1. # 09 多选题(排除一个或一些的选项)
  2. def excluded_duoxuan(self, i, *args):
  3. global base_xpaths
  4. base_xpath = base_xpaths.format(i)
  5. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  6. # print(len(a))
  7. c = []
  8. # y是计算arg的个数,方便计算还剩几个选项
  9. y = 0
  10. for x in range(1, len(a)+1):
  11. c.append(x)
  12. for arg in args:
  13. y += 1
  14. c.remove(arg)
  15. # 还剩下几个选项
  16. z = len(a) - y
  17. # 多选题选项个数
  18. b = random.randint(1, z)
  19. # 多选题用sample()
  20. d = random.sample(c, b)
  21. for r in d:
  22. self.driver.find_element(by=By.CSS_SELECTOR,
  23. value=base_css_selectors.format(i, r)).click()

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

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

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

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

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

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

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

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

  1. # 12 多选题(选项中允许填空)
  2. def textinput_duoxuan(self, i, c, wenzi):
  3. global base_xpaths
  4. base_xpath = base_xpaths.format(i)
  5. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  6. b = len(a)
  7. # m中存放选项
  8. m = []
  9. for x in range(1, b + 1):
  10. m.append(x)
  11. # 随机生成要多选的选项个数
  12. o = random.randint(1, b)
  13. # 在选项中随机选取o个选项
  14. n = random.sample(m, o)
  15. for r in n:
  16. self.driver.find_element(by=By.CSS_SELECTOR,
  17. value=base_css_selectors.format(i, r)).click()
  18. if c == r:
  19. time.sleep(0.2)
  20. self.driver.find_element(by=By.CSS_SELECTOR,
  21. value=input_css_selectors.format(i, c)).send_keys(wenzi)

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

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

13 text()→文本题

  1. # 13 文本题
  2. def text(self, i, wenzi):
  3. 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函数进行随机选择。

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

 

注意:

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

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

  1. # 随机所在城市的选择
  2. def random_city_selection(self, i):
  3. frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
  4. frame.click()
  5. # 滚动到最底端
  6. # self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
  7. self.driver.switch_to.frame('PDF_i_chezchenz')
  8. ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
  9. s = Select(ele)
  10. province = random.choice(provinces)
  11. s.select_by_value(province)
  12. a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
  13. # len(a)表示省下的城市有几个
  14. # print(len(a))
  15. b = random.randint(1, len(a))
  16. self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
  17. self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
  18. # 注意切换回原来的frame
  19. self.driver.switch_to.parent_frame()

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

  1. # 限制所在城市的选择
  2. def restrictive_city_selection(self, i, arg):
  3. frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
  4. frame.click()
  5. # 滚动到最底端
  6. # self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
  7. self.driver.switch_to.frame('PDF_i_chezchenz')
  8. ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
  9. s = Select(ele)
  10. s.select_by_value(arg)
  11. a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
  12. # len(a)表示省下的城市有几个
  13. # print(len(a))
  14. b = random.randint(1, len(a))
  15. self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
  16. self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
  17. # 注意切换回原来的frame
  18. self.driver.switch_to.parent_frame()

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

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

例如下面的代码:

        具体问卷.py

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

提交按钮  submit()

 

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

按步骤写代码,如下

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

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

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


全部代码展示

  1. # -*- coding: utf-8-*-
  2. import random
  3. from selenium import webdriver
  4. import time
  5. from selenium.webdriver.common.by import By
  6. from selenium.webdriver.support.select import Select
  7. """
  8. 01 单选题(随机选择)
  9. danxuan()
  10. 02 单选题(只选择某个选项)
  11. fixed_danxuan()
  12. 03 单选题(排除一个或一些选项)
  13. excluded_danxuan()
  14. 04 单选题(在m到n范围内单选)
  15. range_danxuan()
  16. 05 单选题(在某些选项中选择)如6个选项,在1235中单选
  17. restrictive_danxuan()
  18. 06 单选题(选项中允许填空)
  19. textinput_danxuan()
  20. 07 多选题(随机选择)
  21. duoxuan()
  22. 08 多选题(只选择某些选项)
  23. fixed_duoxuan()
  24. 09 多选题(排除一个或一些选项)
  25. excluded_duoxuan()
  26. 10 多选题(在某些选项中多选)
  27. restrictive_duoxuan()
  28. 11 多选题(在m到n范围内的多选)
  29. range_duoxuan()
  30. 12 多选题(选项中允许填空)
  31. text_input_duoxuan()
  32. 13 文本题
  33. text()
  34. """
  35. count = 0
  36. provinces = [
  37. '北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江',
  38. '上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南',
  39. '湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州',
  40. '云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾',
  41. '香港', '澳门']
  42. '''适用的可能是问卷星网页无法右键,只能通过F12或者ctrl+shift+i快捷键进入开发者工具的问卷'''
  43. # 选项的xpath
  44. base_xpaths = '//*[@id="divquestion{}"]/ul/li'
  45. # 选项的css
  46. base_css_selectors = '#divquestion{} > ul > li:nth-child({})'
  47. # 允许填空选项的css_selector
  48. input_css_selectors = '#divquestion{} > ul > li:nth-child({}) > input.underline'
  49. '''适用的可能是除了上面的情况的问卷星'''
  50. # 选项的xpath
  51. # base_xpaths = '//*[@id="div{}"]/div/div'
  52. # 选项的css
  53. # base_css_selectors = 'div{} > div.ui-controlgroup.column{} > div.ui-checkbox.checked'
  54. # 允许填空选项的css_selector
  55. # input_css_selectors = 'tqq{}_{}'
  56. class Wenjuanxing(object):
  57. # 初始化
  58. def __init__(self, url):
  59. self.url = url
  60. # 计数器
  61. def counter(self):
  62. global count
  63. count += 1
  64. w = print("第{}次运行".format(count))
  65. return w
  66. # 伪装selenium
  67. def weizhuang_selenium(self):
  68. # 躲避智能检测
  69. self.option = webdriver.ChromeOptions()
  70. self.option.add_experimental_option('excludeSwitches', ['enable-automation'])
  71. self.option.add_experimental_option('useAutomationExtension', False)
  72. self.driver = webdriver.Chrome(options=self.option)
  73. self.driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument',
  74. {'source': 'Object.defineProperty(navigator, "webdriver", {get: () => undefined})'})
  75. self.driver.get(self.url)
  76. # 01 单选题(随机选择)
  77. def danxuan(self, i):
  78. global base_xpaths
  79. base_xpath = base_xpaths.format(i)
  80. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  81. b = random.randint(1, len(a))
  82. self.driver.find_element(by=By.CSS_SELECTOR,
  83. value=base_css_selectors.format(i, b)).click()
  84. # 02 单选题(只选择某个选项)
  85. def fixed_danxuan(self, i, b):
  86. self.driver.find_element(by=By.CSS_SELECTOR,
  87. value=base_css_selectors.format(i, b)).click()
  88. # 03 单选题(排除一个或一些选项)
  89. def excluded_danxuan(self, i, *args):
  90. global base_xpaths
  91. base_xpath = base_xpaths.format(i)
  92. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  93. c = []
  94. # y是计算arg的个数,方便计算还剩几个选项
  95. y = 0
  96. for x in range(1, len(a) + 1):
  97. c.append(x)
  98. for arg in args:
  99. y += 1
  100. c.remove(arg)
  101. d = random.choice(c)
  102. self.driver.find_element(by=By.CSS_SELECTOR,
  103. value=base_css_selectors.format(i, d)).click()
  104. # 04 单选题(在m到n范围内单选)
  105. def range_danxuan(self, i, m, n):
  106. x = random.randint(m, n)
  107. self.driver.find_element(by=By.CSS_SELECTOR,
  108. value=base_css_selectors.format(i, x)).click()
  109. # 05 单选题(在某些选项中选择)如6个选项,在1235中单选
  110. def restrictive_danxuan(self, i, *args):
  111. m = []
  112. for arg in args:
  113. m.append(arg)
  114. n = random.choice(m)
  115. self.driver.find_element(by=By.CSS_SELECTOR,
  116. value=base_css_selectors.format(i, n)).click()
  117. # 06 单选题(选项中允许填空)
  118. def textinput_danxuan(self, i, c, wenzi):
  119. global base_xpaths
  120. base_xpath = base_xpaths.format(i)
  121. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  122. b = random.randint(1, len(a))
  123. self.driver.find_element(by=By.CSS_SELECTOR,
  124. value=base_css_selectors.format(i, b)).click()
  125. if c == b:
  126. time.sleep(0.2)
  127. self.driver.find_element(by=By.CSS_SELECTOR,
  128. value=input_css_selectors.format(i, c)).send_keys(wenzi)
  129. # 07 多选题(随机选择)
  130. def duoxuan(self, i):
  131. global base_xpaths
  132. base_xpath = base_xpaths.format(i)
  133. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  134. b = len(a)
  135. # m中存放选项
  136. m = []
  137. for x in range(1, b + 1):
  138. m.append(x)
  139. c = random.randint(1, b)
  140. n = random.sample(m, c)
  141. for o in n:
  142. self.driver.find_element(by=By.CSS_SELECTOR,
  143. value=base_css_selectors.format(i, o)).click()
  144. # 08 多选题(只选择某些选项)
  145. def fixed_duoxuan(self, i, *args):
  146. for arg in args:
  147. self.driver.find_element(by=By.CSS_SELECTOR,
  148. value=base_css_selectors.format(i, arg)).click()
  149. # 09 多选题(排除一个或一些的选项)
  150. def excluded_duoxuan(self, i, *args):
  151. global base_xpaths
  152. base_xpath = base_xpaths.format(i)
  153. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  154. # print(len(a))
  155. c = []
  156. # y是计算arg的个数,方便计算还剩几个选项
  157. y = 0
  158. for x in range(1, len(a)+1):
  159. c.append(x)
  160. for arg in args:
  161. y += 1
  162. c.remove(arg)
  163. # 还剩下几个选项
  164. z = len(a) - y
  165. # 多选题选项个数
  166. b = random.randint(1, z)
  167. # 多选题用sample()
  168. d = random.sample(c, b)
  169. for r in d:
  170. self.driver.find_element(by=By.CSS_SELECTOR,
  171. value=base_css_selectors.format(i, r)).click()
  172. # 10 多选题(在某些选项中多选)
  173. def restrictive_duoxuan(self, i, *args):
  174. m = []
  175. for arg in args:
  176. m.append(arg)
  177. n = random.randint(1, len(m))
  178. o = random.sample(m, n)
  179. for q in o:
  180. self.driver.find_element(by=By.CSS_SELECTOR,
  181. value=base_css_selectors.format(i, q)).click()
  182. # 11 多选题(在m到n范围内的多选)
  183. def range_duoxuan(self, i, m, n):
  184. # 列表c为m到n的选项组,如当m=2,n=5时,c=[2,3,4,5]
  185. c = []
  186. for x in range(m, n+1):
  187. c.append(x)
  188. # 选项个数
  189. o = n - m + 1
  190. # 随机生成要填几个选项
  191. p = random.randint(1, o)
  192. d = random.sample(c, p)
  193. for r in d:
  194. self.driver.find_element(by=By.CSS_SELECTOR,
  195. value=base_css_selectors.format(i, r)).click()
  196. # 12 多选题(选项中允许填空)
  197. def text_input_duoxuan(self, i, c, wenzi):
  198. global base_xpaths
  199. base_xpath = base_xpaths.format(i)
  200. a = self.driver.find_elements(by=By.XPATH, value=base_xpath)
  201. b = len(a)
  202. # m中存放选项
  203. m = []
  204. for x in range(1, b + 1):
  205. m.append(x)
  206. # 随机生成要多选的选项个数
  207. o = random.randint(1, b)
  208. # 在选项中随机选取o个选项
  209. n = random.sample(m, o)
  210. for r in n:
  211. self.driver.find_element(by=By.CSS_SELECTOR,
  212. value=base_css_selectors.format(i, r)).click()
  213. if c == r:
  214. time.sleep(0.2)
  215. self.driver.find_element(by=By.CSS_SELECTOR,
  216. value=input_css_selectors.format(i, c)).send_keys(wenzi)
  217. # 13 文本题
  218. def text(self, i, wenzi):
  219. self.driver.find_element(by=By.CSS_SELECTOR, value='#q{}'.format(i)).send_keys(wenzi)
  220. # 随机所在城市的选择
  221. def random_city_selection(self, i):
  222. frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
  223. frame.click()
  224. # 滚动到最底端
  225. self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
  226. self.driver.switch_to.frame('PDF_i_chezchenz')
  227. ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
  228. s = Select(ele)
  229. province = random.choice(provinces)
  230. s.select_by_value(province)
  231. a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
  232. # len(a)表示省下的城市有几个
  233. # print(len(a))
  234. b = random.randint(1, len(a))
  235. self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
  236. self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
  237. # 注意切换回原来的frame
  238. self.driver.switch_to.parent_frame()
  239. # 限制所在城市的选择
  240. def restrictive_city_selection(self, i, arg):
  241. frame = self.driver.find_element(by=By.XPATH, value='//*[@id="q{}"]'.format(i))
  242. frame.click()
  243. # 滚动到最底端
  244. # self.driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
  245. self.driver.switch_to.frame('PDF_i_chezchenz')
  246. ele = self.driver.find_element(by=By.CSS_SELECTOR, value='#province')
  247. s = Select(ele)
  248. s.select_by_value(arg)
  249. a = self.driver.find_elements(by=By.XPATH, value='//*[@id="city"]/label')
  250. # len(a)表示省下的城市有几个
  251. # print(len(a))
  252. b = random.randint(1, len(a))
  253. self.driver.find_element(by=By.XPATH, value='//*[@id="city"]/label[{}]'.format(b)).click()
  254. self.driver.find_element(by=By.XPATH, value='//*[@id="form1"]/div[3]/div/input').click()
  255. # 注意切换回原来的frame
  256. self.driver.switch_to.parent_frame()
  257. # 提交按钮
  258. def submit(self):
  259. # time.sleep(0.5)
  260. btn = self.driver.find_element(by=By.CSS_SELECTOR, value='#submit_button')
  261. btn.click()
  262. # 出现点击验证码验证
  263. time.sleep(1)
  264. self.driver.find_element(by=By.XPATH, value='//*[@id="alert_box"]/div[2]/div[2]/div[2]/button').click()
  265. time.sleep(0.5)
  266. self.driver.find_element(by=By.XPATH, value='//*[@id="SM_BTN_1"]').click()
  267. time.sleep(4)
  268. # 关闭页面
  269. handles = self.driver.window_handles
  270. self.driver.switch_to.window(handles[0])
  271. # time.sleep(0.5)
  272. # # 刷新页面(可能不需要)
  273. # self.driver.refresh()
  274. # 关闭当前页面,如果只有一个页面,则也关闭浏览器
  275. self.driver.close()

3.2  具体问卷.py

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

★全部代码展示★

  1. # -*- coding: utf-8-*-
  2. import random
  3. import Sojump
  4. from Sojump import Wenjuanxing
  5. import schedule as schedule
  6. restrictive_provinces = ['云南', '贵州', '四川']
  7. problems_10_1 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']
  8. problems_10_2 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']
  9. if __name__ == '__main__':
  10. url = '问卷星网址'
  11. wenjuanxing = Wenjuanxing(url)
  12. def run():
  13. # 随机内容
  14. restrictive_province = random.choice(restrictive_provinces)
  15. problem_10_1 = random.choice(problems_10_1)
  16. problem_10_2 = random.choice(problems_10_2)
  17. # 计数器
  18. wenjuanxing.counter()
  19. # 伪装selenium
  20. wenjuanxing.weizhuang_selenium()
  21. # 问卷题目
  22. wenjuanxing.danxuan(1)
  23. wenjuanxing.danxuan(2)
  24. wenjuanxing.restrictive_danxuan(3, 2, 3, 4, 5)
  25. wenjuanxing.danxuan(4)
  26. wenjuanxing.danxuan(5)
  27. wenjuanxing.danxuan(6)
  28. wenjuanxing.duoxuan(7)
  29. wenjuanxing.danxuan(8)
  30. wenjuanxing.danxuan(9)
  31. # 第十题为两个选项都可以填空的单选题,因此尝试用try except
  32. try:
  33. wenjuanxing.textinput_danxuan(10, 1, problem_10_1)
  34. # print(problem_10_1)
  35. wenjuanxing.textinput_danxuan(10, 2, problem_10_2)
  36. # print(problem_10_2)
  37. except:
  38. pass
  39. wenjuanxing.duoxuan(11)
  40. wenjuanxing.danxuan(12)
  41. # 13得考虑一下
  42. # 第13题虽然是有五个选项的多选题,不过依据逻辑,它只能分成1 3 1 形式,
  43. # 即要么选择第一个选项,要么在中间三个选项之间进行多选,要么选择最后一个选项。
  44. a = random.randint(1, 3)
  45. if a == 1:
  46. wenjuanxing.fixed_danxuan(13, 1)
  47. elif a == 2:
  48. wenjuanxing.range_duoxuan(13, 2, 5)
  49. else:
  50. wenjuanxing.fixed_danxuan(13, 6)
  51. wenjuanxing.danxuan(14)
  52. wenjuanxing.duoxuan(15)
  53. wenjuanxing.duoxuan(16)
  54. wenjuanxing.danxuan(17)
  55. # 限制省份的随机选择
  56. # print(restrictive_province)
  57. wenjuanxing.restrictive_city_selection(18, restrictive_province)
  58. # time.sleep(3)
  59. # 提交问卷
  60. wenjuanxing.submit()
  61. # 每隔2秒运行
  62. schedule.every(2).seconds.do(run)
  63. # 判断条件
  64. while Sojump.count < 50:
  65. schedule.run_pending()

具体代码讲解

①每隔一段时间运行代码

  1. import schedule as schedule
  2. def run():
  3. pass
  4. schedule.every(2).seconds.do(run)
  5. while True:
  6. schedule.run_pending()

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

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

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

    while Sojump.count < 50:

②编写主体代码

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

  1. restrictive_provinces = ['云南', '贵州', '四川']
  2. problems_10_1 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']
  3. problems_10_2 = ['', 'xxx', 'xx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx', 'xxx']

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

        接下来需要调用类

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

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

代码如下:

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

        着重注意一下以下两题

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

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

wenjuanxing.random_city_selection(18)

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


四、总结

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

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/605907
推荐阅读
相关标签
  

闽ICP备14008679号