项目链接:https://github.com/VGDXHan/Automatic-Questionnaire_Script_With_DeepSeek

1 方法 1:基于逻辑判断

  • 优点:可以得到满分答案
  • 局限:只适用单选题且题目不从题库随机抽取(否则状态空间很大,很难收敛)

1.1 网页交互部分

通过 playwright 与网页交互、获取网页上的数据

安装

1
2
3
4
conda config --add channels conda-forge
conda config --add channels microsoft
conda install playwright
playwright install

同步模式与异步模式的区别

  • 同步模式:依赖多线程(资源开销大),基于线程阻塞,每一步操作等待完成后再继续执行
  • 异步模式:单线程高效处理多任务(协程切换成本低),基于协程,非阻塞执行

同步模式使用方法

1
from playwright.sync_api import sync_playwright
1
2
3
4
5
6
7
8
9
def open_questionaire(self, url: str = 'http://www.baidu.com'):
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto(url)
print("成功打开问卷: {}".format(page.title()))
...
...
browser.close()

headless=False 意味着展示浏览器操作,调试完毕后可以将其设置为 true 提高运行效率

提取页面元素

核心是使用 locator 方法

该方法针对 page 对象使用,传入 selector 参数

可在网页上根据如下方法获取 selector:

  1. 按 F12
  2. 上方选择栏中 Elements
  3. 找到需要的元素,右键>Copy>Copy Selector
  4. 以 str 形式粘贴到 locator 的参数中

image-20250309173711561

1
2
3
4
5
6
7
def extract_qa(self, page):
question_locator = page.locator('#div1 > div.field-label > div.topichtml')
question = question_locator.inner_text()
answer_locator = page.locator('#div1 > div.ui-controlgroup.column1')
answer = answer_locator.all_inner_texts()

return question, answer[0].split('\n')

对 page. locator 返回的对象使用 inner_text 或 all_inner_texts 方法获得 selector 对应元素的文本内容

点击选项

image-20250309173730712

对 page. locator 返回的对象调用 click 方法进行点击操作

1
page.locator("#div1 > div.ui-controlgroup.column1 > div:nth-child(1)").click()

按钮验证

1
2
3
4
if page.locator(self.valid_button_selector).count() > 0:
print("进行智能验证")
page.locator(self.valid_button_selector).click()
print("验证成功")

valid_button_selector 存储着验证按钮对应的 selector 值,对 page. locator 返回的对象调用 count 方法返回 selector 对应元素的数量,如果网页中这个元素的数量大于 0(即该页面存在该元素),则定位到该按钮进行点击

1.2 求解部分

判断逻辑如图:

image-20250309173944271

2 方法 2:基于 deepseek

  • 优点:适用所有题型
  • 局限:无法获得满分

网页交互部分与方法 1 一致,不赘述

求解部分使用 deepseek 实现

Deepseek api 的使用方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
client = OpenAI(api_key=self.params.api_key, base_url=self.params.base_url)

try:
content = "要给deepseek发送的文本"
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": "用于设置系统角色的文本"},
{"role": "user", "content": content},
],
stream=False
)
answer = response.choices[0].message.content # 得到答案
except json.JSONDecodeError as e:
print("服务器繁忙")
exit(1)
except Exception as e:
print("其他错误")
exit(1)

通过提示词设置让 Deepseek 生成指定格式的答案,在再后面的程序进行解析与选择即可

我给的提示词是:

1
2
3
4
5
6
7
我上面向你提供的是一个问卷的提取文本,不同题目间用换行符进行了间隔,你必须严格按照下列要求作答\
作答规则:\
1. 题目无论单选多选还是判断,都以数字形式作答,数字为选项的序号(如一题的答案选项为BCDA,则B对应序号1,A对应序号4)\
2. 只有题干中出现多选题字样的题目(如“王某夫妇收到陌生人电话,称有一款理财产品回报率高,便去银行汇款,银行工作人员怕其上当阻止,王某夫妇应该怎么办?【多选题】”)才按照多选题处理,多个序号间用空格隔开\
3. 判断题答题规则与单选题一致,也按照序号回答,跟对错字样无关\
4. 不同题目的答案之间要用\n隔开\
5. 答案行数应该与题目数量一致"

读取答案部分代码如下,由于 Deepseek 生成的答案有随机性,所以需要对其答案的正确性进行简单判断(这里是判断答案数量对不对)

1
2
3
4
5
6
7
with open(self.params.ANSWERS_PATH, 'r') as f:
raw_answers = f.readlines() # 每一行是一个题目

if len(raw_answers) != self.params.n_questions :# 检查答案数量是否等于题目数量
print("题目数量为{},而答案数量为{}".format(self.params.n_questions, len(raw_answers)))
print("答案有误")
exit(1)