目录
一、前言
二、模块准备
①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)
剩下的其他函数在之前的内容中讲过,也比较简单。?
四、总结
????????问卷星根据题目的不同、题目和选项的搭配能产生很多变化,上述只涉及到几个较常用的几个函数。后续大家如果需要实现一些其他功能的,可以自行编写,也可以与笔者多多交流,完善该模块。
????????最后希望大家不要光看文章,也可以动手敲敲代码,加深理解。
????????如果大家发现无法刷问卷,可能就是元素定位出现了问题,可以去看看元素定位属于上述讲的哪一类。
? ? ? ? 如有错误之处,请批评指正!!
|