【日常】爬虫技巧进阶:textarea的value修改与提交问题(以智谱清言为例)

news2024/12/23 0:00:50

序言

记录一个近期困扰了一些时间的问题。

我很喜欢在爬虫中遇到问题,因为这意味着在这个看似简单的事情里还是有很多值得去探索的新东西。其实本身爬虫也是随着前后端技术的不断更新在进步的。


文章目录

  • 序言
  • Preliminary
  • 1 问题缘起
    • 1.1 Selenium长文本输入阻塞
    • 1.2 ChromeDriver无法输入非BMP字符(如Emoji字符)
  • 2 Javascript修改textarea文本内容不生效
  • 3 笨方法
  • 4 正解
  • 附录:智谱清言(ChatGLM)Selenium测试脚本
  • 后记


Preliminary

本文假定你已经拥有基于ChromeDriver的Selenium脚本开发经验以及基础的前端知识。

1 问题缘起

1.1 Selenium长文本输入阻塞

长文本输入阻塞的问题是Selenium老大难问题,其原因是Selenium的send_keys()函数在输入字符串时,会将字符串分解为每个字符进行处理,具体可见源码(keys_to_typing函数for循环里最后一个else分支):

def send_keys(self, *keys_to_send):
    """
    Sends keys to current focused element.

    :Args:
     - keys_to_send: The keys to send.  Modifier keys constants can be found in the
       'Keys' class.
    """
    typing = keys_to_typing(keys_to_send)
    if self._driver.w3c:
        for key in typing:
            self.key_down(key)
            self.key_up(key)
    else:
        self._actions.append(lambda: self._driver.execute(
            Command.SEND_KEYS_TO_ACTIVE_ELEMENT, {'value': typing}))
    return self
    
def keys_to_typing(value):
    """Processes the values that will be typed in the element."""
    typing = []
    for val in value:
        if isinstance(val, Keys):
            typing.append(val)
        elif isinstance(val, int):
            val = str(val)
            for i in range(len(val)):
                typing.append(val[i])
        else:
            for i in range(len(val)):
                typing.append(val[i])
    return typing

这显然是挺笨重的操作,因为我们正常将长文本复制到<textarea>文本框中并不需要花费很长时间,但是用driver.find_element(By.xxx, xxx).send_keys(...)逐字输入就很容易使得页面阻塞,这里有个隐藏的问题是输入换行符\n有时会触发文本框内容的提交,这个问题相对容易解决,因为<textarea>是闭合标签,输入的文本内容本质上是在<textarea>{文本内容}</textarea>中,因此将send_keys(...)中的换行符\n替换成<br>即可避免。

题外话,源码为什么要这样写一定会有它的道理。仔细想想也不难理解,keys_to_typing函数的参数value并不总是字符串,也可能是键盘的某个键位,比如常用的我们通过send_keys(Key.CONTROL, 'v')实现向输入框中粘贴文本,因此需要分解成各个字符处理。


图1 智谱清言官网👇👇👇
在这里插入图片描述


本文以智谱清言(ChatGLM)官网的<textarea>标签的文本框输入为例(你也可以在任何一个带有<textarea>文本框的网站进行测试),直接向里面粘贴长文本是非常容易的,但是通过浏览器驱动输入长文本则很容易造成阻塞。

下面的代码是基于Chrome浏览器驱动的测试脚本:

  • 需要配置用户数据路径(initialize_chrome_driver函数中的chrome_user_data_path变量),以跳过智谱清言的登录。

  • 目前访问智谱清言官网还有一个问题,就是会在页面加载上卡很久(有一层Loading…的mask层使得页面元素完全无法交互,应该是在验证登录),此时页面标签和元素已经加载出来,因此不能通过简单的element.is_display来确定页面是否可用。这个状态下可以用Selenium向文本框输入文字,但是无法点击提交按钮,可能可以通过selenium.webdriver.support.expected_condition.element_to_be_clickable函数判断元素是否可交互来确定页面是否完全加载。

    这个问题不展开讨论,一般来说很少彻底卡死(报错信息是element not interactable)。

# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn

import os
import time
import logging

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.action_chains import ActionChains


def initialize_chrome_driver(headless, timeout):
	chrome_user_data_path = r"C:\Users\caoyang\AppData\Local\Google\Chrome\User Data"
	chrome_options = webdriver.ChromeOptions()		
	chrome_options.add_argument(f"user-data-dir={chrome_user_data_path}")	# Import user data
	if headless:
		chrome_options.add_argument("--headless")
	driver = webdriver.Chrome(chrome_options=chrome_options)
	driver.set_page_load_timeout(timeout)
	if not headless:
		driver.maximize_window()
	return driver

def check_element_by_xpath(driver, xpath, timeout):
	WebDriverWait(driver, timeout).until(lambda _driver: _driver.find_element_by_xpath(xpath).is_displayed())

# Regular-used XPaths
layout_xpaths = {"input-box"			: "//textarea[@class=\"scroll-display-none\"]",	# XPath of the input box for human
				 "send-button-1"		: "//img[@class=\"enter_icon\"]",				# XPath of the button to send text of input box
				 "send-button-2"		: "//div[@class=\"enter\"]",					# XPath of the button to send text of input box
				 "chat-area"			: "//div[@id=\"session-container\"]",			# XPath of the chat area which contains all the talks (consist of several chat boxes)
				 "human-box"			: "//div[@class=\"pr\"]",						# XPath of human chat box
				 "ai-box"				: "//div[@class=\"answer-content-wrap\"]",		# XPath of AI chat box
				 "ai-box-text"			: "//div[@class=\"markdown-body\"]",			# XPath of the text contained in AI chat box
				 "create-new-button"	: "//div[@class=\"new-session-button\"]",		# XPath of create new talk
				 "like-or-dislike-area"	: "//div[@class=\"interact-operate\"]",			# XPath of div tag contains like and dislike icons
				 "delete-session"		: "//span[@class=\"btn delete\"]",				# XPath of button to delete old talk
				 }

# Initialize
driver = initialize_chrome_driver(headless=False, timeout=60)
driver.get("https://chatglm.cn/main/detail")

check_element_by_xpath(driver, xpath=layout_xpaths["input-box"], timeout=60)		# Check if input box is rendered
check_element_by_xpath(driver, xpath=layout_xpaths["send-button-1"], timeout=60)	# Check if send button is rendered
check_element_by_xpath(driver, xpath=layout_xpaths["send-button-2"], timeout=60)	# Check if send button is rendered

# Delete old talk
try:
	driver.find_element_by_xpath(self.layout_xpaths["delete-session"]).click()
	logging.info("Delete old talk ...")
except:
	logging.info("No old talk found ...")

# Request
logging.info("Prompt ...")
prompt = """这是一段很长的文本!"""	# e.g. prompt = 'x' * 8000

## Method 1: Use `element.send_keys`
driver.find_element_by_xpath(layout_xpaths["input-box"]).send_keys(prompt)	# Input the given prompt

在上述代码中,prompt字段过长容易导致send_keys(prompt)造成阻塞,以至于影响后续的代码逻辑。


1.2 ChromeDriver无法输入非BMP字符(如Emoji字符)

ChromeDriver无法输入非BMP字符(据说Geckodriver是可行的),具体报错信息为:

selenium.common.exceptions.WebDriverException: Message: unknown error: ChromeDriver only supports characters in the BMP

这个问题就比上面的长文本阻塞要麻烦多了,前者只是需要等待一段时间就能输入完,但这个问题是真的绕不过去。

好在我们有万能的Javascript😋:

# Request
logging.info("Prompt ...")
prompt = '😋'

# ## Method 1: Use `element.send_keys`
# driver.find_element_by_xpath(layout_xpaths["input-box"]).send_keys(prompt)	# Input the given prompt

## Method 2: Use Javascript with one auguments (Fail)
js = """var txt = arguments[0]; document.getElementsByTagName("textarea")[0].value = txt;"""		
driver.execute_script(js, prompt)
logging.info("  - Use Javascript to input ...")

注意,这里Selenium不能直接用JQuery的语法使用$("textarea")来定位元素,因为页面上只有一个<textarea>标签。


图2 使用Javascript输入Emoji👇👇👇(大功告成,我们成功地把😋输入到文本框中啦!)
在这里插入图片描述
问题结束了吗?


2 Javascript修改textarea文本内容不生效

然而,问题才刚刚开始,在图2中,只要你点击提交按钮,就会发现页面提示发送内容不能为空!,甚至只要将鼠标移动到文本框中,😋就会消失。

这个问题确实很让人苦恼,笔者搜索很多关于Javascript修改textarea内容不生效的文章,众说纷纭,但是没有一个起作用的。事实上不止是修改<textarea>标签的value,包括innerTextinnerHTMLtextContent都是不起作用的:


图3 测试textarea的value, innerText, innerHTML属性的修改👇👇👇
在这里插入图片描述
控制台的输出信息会发现,单纯修改value,确实可以看到修改的文本出现在界面上,但是实际HTML中并不会显示,点击提交按钮($(".enter_icon").click())也无法发送内容。

如果修改innerTextinnerHTML,HTML中确实出现了值,修改的文本也出现在界面上,但是仍然无法提交,且点击提交按钮后,界面上不再显示文本,但HTML中依然可以看到Hello

某些回答指出,需要进行dispatchEvent,从最后的正解来看,这种回答说对了一半,如下面的代码所示,通过加入elm.dispatchEvent(new Event('change'));,似乎很有道理,但是实际上依然无法进行提交。

# Request
logging.info("Prompt ...")
prompt = 'Hello!'

# ## Method 1: Use `element.send_keys`
# driver.find_element_by_xpath(layout_xpaths["input-box"]).send_keys(prompt)	# Input the given prompt

# ## Method 2: Use Javascript with one auguments (Fail)
# js = """var txt = arguments[0]; document.getElementsByTagName("textarea")[0].value = txt;"""		
# driver.execute_script(js, prompt)
# logging.info("  - Use Javascript to input ...")

# Method 3: Use Javascript with event dispatch (Fail)
js = """var elm = arguments[0], txt = arguments[1]; elm.value += txt; elm.dispatchEvent(new Event('change'));"""
element = driver.find_element_by_xpath(self.layout_xpaths["input-box"])
driver.execute_script(js, element, prompt)
logging.info("  - Use Javascript to input ...")

真是糟透了,难道真的没有人遇到跟我一样的问题吗?


3 笨方法

在控制台测了一遍又一遍后,被这离奇消失的<textarea>文本折磨得实在是无能为力,我决定还是用最笨得方法来解决:

  1. 先把需要输入的文本复制到剪贴板上(使用Pyperclip包,使用pip install pyperclip进行安装)
  2. 使用send_keys方法在<textarea>文本框中进行粘贴文本

效果拔群!事实证明,能抓到猫的就是好老鼠,笨方法一下子就解决了这个该死的问题!

# Request
logging.info("Prompt ...")
prompt = 'Hello!'

# ## Method 1: Use `element.send_keys`
# driver.find_element_by_xpath(layout_xpaths["input-box"]).send_keys(prompt)	# Input the given prompt

# ## Method 2: Use Javascript with one auguments (Fail)
# js = """var txt = arguments[0]; document.getElementsByTagName("textarea")[0].value = txt;"""		
# driver.execute_script(js, prompt)
# logging.info("  - Use Javascript to input ...")

# # Method 3: Use Javascript with event dispatch (Fail)
# js = """var elm = arguments[0], txt = arguments[1]; elm.value += txt; elm.dispatchEvent(new Event('change'));"""
# element = driver.find_element_by_xpath(self.layout_xpaths["input-box"])
# driver.execute_script(js, element, prompt)
# logging.info("  - Use Javascript to input ...")

# Method 4: Use keyboard operation (Success)
import pyperclip
pyperclip.copy(prompt)
time.sleep(1)
driver.find_element_by_xpath(self.layout_xpaths["input-box"]).send_keys(Keys.CONTROL, 'v')
logging.info("  - Use keyboard to input ...")

4 正解

但是我们总归还是要解决这个问题的,不能因为走了歪门邪道就自鸣得意。其实很容易能想到,之所以无法提交Javascript修改的<textarea>文本框内容,肯定是提交按钮没有绑定到修改的内容,仍然是文本框原本的默认值,这当然需要定义事件进行发送。这里我也搜索到一些人发现无法使用Selenium清空<textarea>文本框的内容,但是他们最后还是向用send_keys(Keys.BACK_SPACE)的键盘操作方法进行妥协。

最后是在StackFlow上找到了这个问题的正解:https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-change-or-input-event-in-react-js

For React 16 and React >=15.6

  • Setter .value= is not working as we wanted because React library overrides input value setter but we can call the function directly on the input as context.
var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeInputValueSetter.call(input, 'react 16 value');
var ev2 = new Event('input', { bubbles: true});
input.dispatchEvent(ev2);
  • For textarea element you should use prototype of HTMLTextAreaElement class.

这是涉及React框架的知识,可是我再也不会回去学习前端了。

最后的正解代码应该是:

# Request
logging.info("Prompt ...")
prompt = 'Hello!'

# ## Method 1: Use `element.send_keys`
# driver.find_element_by_xpath(layout_xpaths["input-box"]).send_keys(prompt)	# Input the given prompt

# ## Method 2: Use Javascript with one auguments (Fail)
# js = """var txt = arguments[0]; document.getElementsByTagName("textarea")[0].value = txt;"""		
# driver.execute_script(js, prompt)
# logging.info("  - Use Javascript to input ...")

# # Method 3: Use Javascript with event dispatch (Fail)
# js = """var elm = arguments[0], txt = arguments[1]; elm.value += txt; elm.dispatchEvent(new Event('change'));"""
# element = driver.find_element_by_xpath(self.layout_xpaths["input-box"])
# driver.execute_script(js, element, prompt)
# logging.info("  - Use Javascript to input ...")

# # Method 4: Use keyboard operation (Success)
# import pyperclip
# pyperclip.copy(prompt)
# time.sleep(1)
# driver.find_element_by_xpath(self.layout_xpaths["input-box"]).send_keys(Keys.CONTROL, 'v')
# logging.info("  - Use keyboard to input ...")

# Method 5: Use Javascript with DispatchEvent (Success)
js = """var txt = arguments[0];
const textarea = $("textarea");
var nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
nativeTextAreaValueSetter.call(textarea, txt);
const event = new Event("input", {bubbles: true});
textarea.dispatchEvent(event);"""
driver.execute_script(js, prompt)
logging.info("  - Use Javascript to input ...")

附录:智谱清言(ChatGLM)Selenium测试脚本

感谢您阅读到这里,作为本文的结束,附上完整的代码:

# -*- coding: utf-8 -*-
# @author: caoyang
# @email: caoyang@163.sufe.edu.cn

import os
import re
import time
import logging
import requests

from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions

class BaseCrawler:
	tag_regex = re.compile(r"<[^>]+>|\n|\t")
	global_timeout = 60
	global_interval = 300
	chrome_user_data_path = r"C:\Users\caoyang\AppData\Local\Google\Chrome\User Data"
	# Convert request headers copied from Firefox to dictionary
	@classmethod
	def headers_to_dict(cls, headers: str) -> dict:
		lines = headers.splitlines()
		headers_dict = {}
		for line in lines:
			key, value = line.strip().split(':', 1)
			headers_dict[key.strip()] = value.strip()
		return headers_dict

	# Easy use of WebDriverWait
	@classmethod
	def check_element_by_xpath(cls, driver, xpath, timeout=30):
		WebDriverWait(driver, timeout).until(lambda _driver: _driver.find_element_by_xpath(xpath).is_displayed())
	
	# @param method: e.g. GET, POST
	def easy_requests(self, method, url, **kwargs):
		while True:
			try:
				response = requests.request(method, url, **kwargs)
				break
			except Exception as e:
				logging.warning(f"Error {method} {url}, exception information: {e}")
				logging.warning(f"Wait for {self.global_interval} seconds ...")
				time.sleep(self.global_interval)
		return response

	# Initialize driver
	def initialize_driver(self, browser="chrome", headless=True, timeout=60, **kwargs):
		browser = browser.lower()
		assert browser in ["chrome", "firefox"], f"Unknown browser name: {browser}"
		return eval(f"self._initialize_{browser}_driver")(headless, timeout, **kwargs)
	
	# Initialize Google Chrome driver
	def _initialize_chrome_driver(self, headless, timeout, **kwargs):
		chrome_options = webdriver.ChromeOptions()		
		chrome_options.add_argument(f"user-data-dir={self.chrome_user_data_path}")	# Import user data
		if headless:
			chrome_options.add_argument("--headless")
		driver = webdriver.Chrome(chrome_options=chrome_options)
		driver.set_page_load_timeout(timeout)
		if not headless:
			driver.maximize_window()
		return driver

	# Initialize Mozilla Firefox driver
	def _initialize_firefox_driver(self, headless, timeout, **kwargs):
		options = webdriver.FirefoxOptions()
		if headless:
			options.add_argument("--headless")
		driver = webdriver.Firefox(options=options)
		driver.set_page_load_timeout(timeout)
		if not headless:
			driver.maximize_window()
		return driver

	# Get cookies by driver
	def get_cookies(self, url, driver=None, browser="chrome"):
		quit_flag = False
		if driver is None:
			# If there is no driver passed
			quit_flag = True
			driver = self.initialize_driver(browser=browser, headless=True, timeout=30)
		driver.get(url)
		cookies = driver.get_cookies()
		def _cookie_to_string(_cookies):
			_string = str()
			for _cookie in _cookies:
				_name = _cookie["name"]
				_value = _cookie["value"].replace(' ', '%20') # %20 refers to space char in HTML
				_string += f"{_name}={_value};"
			return _string.strip()
		if quit_flag:
			driver.quit()
		return _cookie_to_string(cookies)


class ChatGLMCrawler(BaseCrawler):
	urls = {"home": "https://chatglm.cn/main/detail",	# Home URL
			}
	layout_xpaths = {"input-box"			: "//textarea[@class=\"scroll-display-none\"]",					# XPath of the input box for human
					 # "input-box"			: "//div[@class=\"input-box-inner\"]",							# XPath of the input box for human (div tag cannot be interacted)
					 "send-button-1"		: "//img[@class=\"enter_icon\"]",								# XPath of the button to send text of input box
					 "send-button-2"		: "//div[@class=\"enter\"]",									# XPath of the button to send text of input box
					 "chat-area"			: "//div[@id=\"session-container\"]",							# XPath of the chat area which contains all the talks (consist of several chat boxes)
					 "human-box"			: "//div[@class=\"pr\"]",										# XPath of human chat box
					 "ai-box"				: "//div[@class=\"answer-content-wrap\"]",						# XPath of AI chat box
					 "ai-box-text"			: "//div[@class=\"markdown-body\"]",							# XPath of the text contained in AI chat box
					 "create-new-button"	: "//div[@class=\"new-session-button\"]",						# XPath of create new talk
					 "like-or-dislike-area"	: "//div[@class=\"interact-operate\"]",							# XPath of div tag contains like and dislike icons
					 "delete-session"		: "//span[@class=\"btn delete\"]",								# XPath of button to delete old talk
					 }
	forbidden_strings = []
	def __init__(self):
		super(ChatGLMCrawler, self).__init__()

	# @param driver		: WebDriver object
	# @param prompt		: The question you would like to ask AI
	# @param model_name	: One of the key in `model_card_xpath`, e.g. "chatgpt3.5(16k)"
	def request(self, driver, prompt, first_trial=True):
		prompt = prompt.replace('\n', "\\n")
		if first_trial:
			driver.get(self.urls["home"])
		self.check_element_by_xpath(driver, xpath=self.layout_xpaths["input-box"], timeout=60)		# Check if input box is rendered
		self.check_element_by_xpath(driver, xpath=self.layout_xpaths["send-button-1"], timeout=60)	# Check if send button is rendered
		self.check_element_by_xpath(driver, xpath=self.layout_xpaths["send-button-2"], timeout=60)	# Check if send button is rendered
		# Delete old talk
		try:
			driver.find_element_by_xpath(self.layout_xpaths["delete-session"]).click()
			logging.info("Delete old talk ...")
		except:
			logging.info("No old talk found ...")
		# Request
		logging.info("Prompt ...")
		try:
			## Method 1: Use `element.send_keys`
			driver.find_element_by_xpath(self.layout_xpaths["input-box"]).send_keys(prompt)	# Input the given prompt
			logging.info("  - ok!")
		except:
			# ## Method 2: Use Javascript with one auguments (Fail)
			# js = """var txt = arguments[0]; document.getElementsByTagName("textarea")[0].value = txt;"""		
			# driver.execute_script(js, prompt)
			# logging.info("  - Use Javascript to input ...")

			# # Method 3: Use Javascript with event dispatch (Fail)
			# js = """var elm = arguments[0], txt = arguments[1]; elm.value += txt; elm.dispatchEvent(new Event('change'));"""
			# element = driver.find_element_by_xpath(self.layout_xpaths["input-box"])
			# driver.execute_script(js, element, prompt)
			# logging.info("  - Use Javascript to input ...")

			# # Method 4: Use keyboard operation (Success)
			# import pyperclip
			# pyperclip.copy(prompt)
			# time.sleep(1)
			# driver.find_element_by_xpath(self.layout_xpaths["input-box"]).send_keys(Keys.CONTROL, 'v')
			# logging.info("  - Use keyboard to input ...")

			# Method 5: Use Javascript with DispatchEvent (Success)
			js = """var txt = arguments[0];
			const textarea = $("textarea");
			var nativeTextAreaValueSetter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, "value").set;
			nativeTextAreaValueSetter.call(textarea, txt);
			const event = new Event("input", {bubbles: true});
			textarea.dispatchEvent(event);"""
			driver.execute_script(js, prompt)
			logging.info("  - Use Javascript to input ...")

		while True:
			# The button is dynamic and sometimes fail to click on
			try:
				driver.find_element_by_xpath(self.layout_xpaths["send-button-1"]).click()		# Click on the button to send the prompt
				logging.info("Use send button 1 ...")
				break
			except:
				try:
					driver.find_element_by_xpath(self.layout_xpaths["send-button-2"]).click()	# Click on the button to send the prompt
					logging.info("Use send button 2 ...")
					break
				except:
					logging.info("Use send button error ...")
					raise Exception("Use send button error ...")
		# Wait for response
		self.check_element_by_xpath(driver, xpath=self.layout_xpaths["chat-area"], timeout=30)	# Check if chat area is rendered
		self.check_element_by_xpath(driver, xpath=self.layout_xpaths["human-box"], timeout=30)	# Check if human chat box is rendered
		self.check_element_by_xpath(driver, xpath=self.layout_xpaths["ai-box"], timeout=30)		# Check if AI chat box is rendered
		finish_flag = True	# Indicating if AI generation is finished
		while finish_flag:
			try:
				# If like or dislike appear, then stop
				driver.find_element_by_xpath(self.layout_xpaths["like-or-dislike-area"])
				finish_flag = False
			except:
				ai_box_text = driver.find_element_by_xpath(self.layout_xpaths["ai-box-text"])						# Find AI response text element
				# ai_box_text = driver.find_element_by_xpath(self.layout_xpaths["ai-box"])							# Find AI response text element
				ai_box_text_inner_html = ai_box_text.get_attribute("innerHTML")										# Get inner HTML of the element
				response = self.tag_regex.sub(str(), ai_box_text_inner_html).strip("\n\t ").replace('\n', '\\n')	# Process response text
				forbidden_flags = [forbidden_string in response for forbidden_string in self.forbidden_strings]
				if sum(forbidden_flags) > 0:
					# It indicates that a forbidden string occurs
					finish_flag = False
		# Extract AI response text
		ai_box_text = driver.find_element_by_xpath(self.layout_xpaths["ai-box-text"])	# Find AI response text element
		ai_box_text_inner_html = ai_box_text.get_attribute("innerHTML")					# Get inner HTML of the element
		response = self.tag_regex.sub(str(), ai_box_text_inner_html).strip("\n\t ")		# Process response text
		return response

	# @param data_path: EXCEL file of job descriptions
	# @param save_path: file path for storing AI response
	# @param model_name: defined in model_card_xpaths
	def demo(self, model_name="chatgpt3.5(16k)"):
		driver = self.initialize_driver(browser="chrome", headless=False, timeout=60)
		driver.implicitly_wait(15) # 
		prompt = "给我讲述一下《基督山伯爵》的故事,500字左右。"
		response = self.request(driver, prompt)
		with open(f"d:/answer-chatglm.txt", 'w', encoding="utf8") as f:
			f.write(response)
		time.sleep(5)
		driver.quit()

if __name__ == "__main__":
	crawler = ChatGLMCrawler()
	crawler.demo()

图4 脚本测试截图👇👇👇
在这里插入图片描述


后记

昨天S消防演习,人在18楼,走到10楼就堵得下不去了,宝玺姐根本就没有下楼,一直呆在工位上,我回来后说如果S真的失火,应该会有直升机来营救,我们应该往上面天台跑而不是往楼下逃生。宝玺姐非常认真地跟我说,S如果失火,我们肯定都是死路一条,有一种高知女性特有的决绝感。我不知道SXY是否也是如此,其实我现在离TA很近,可是又离TA很远。

在这次高校百英里接力赛,摸到10km路跑40分钟的大门之后,我坚定了一个信念,人可以办成任何事情,人也不要办成每一件事情。十年前,甚至五年、三年前我都不敢想象有一天能把10km跑进40分钟,但现在确确实实做到了,即便是在这样奔波的生活中。未来我也一定有机会全马破三、完成铁三越野。

现在我觉得,人生是一个等待和追求的过程,迷茫的时候可以静静地等待,明确的时候应当无畏地追求,一切安顿时就该好好休息。我看到不同的人、不同的事、不同的生活、不同的态度,我觉得平等的交流是最难能可贵的事,让每个认知水平不同的人,即便是在一个相对小的群体里,都能够发声,也敢于发声,让每一种声音都有它的容身之处。

我们总是有一种莫名的主观的客观视角,总会觉得有一些自己认为理所当然的事情,别人都应当如此,然而并不其然。当我看到35岁的吉米和乔总吃饭时还在讨论《咒术回战》的五条悟、《进击的巨人》的地鸣,听到儿子都已经10多岁的慧悦姐称自己在工作中很闷骚时,觉得很不可思议。跟宝玺姐一起工作的三个月里,也看到她身上很多看似矛盾、但细想又很合理的事情。人类就是这样多态的实例化对象,也注定是多变的非常量实体。

每次写到这里,都会想起很多遗憾。岁数越长,越觉得人生是很奇特的历程,你永远不知道盒子里下一块巧克力是什么颜色,你甚至无法知晓盒子里到底是不是一块巧克力,或许是根棒棒糖也说不定,就像曾经错过的人和事,在将来的某个节点是否会再次相遇?

罢了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1221827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue项目本地开发完成后部署到服务器后报404

vue项目本地开发完成后部署到服务器后报404是什么原因呢&#xff1f; 一、如何部署 前后端分离开发模式下&#xff0c;前后端是独立布署的&#xff0c;前端只需要将最后的构建物上传至目标服务器的web容器指定的静态目录下即可 我们知道vue项目在构建后&#xff0c;是生成一系…

开源网安解决方案荣获四川数实融合创新实践优秀案例

​11月16日&#xff0c;2023天府数字经济峰会在成都圆满举行。本次峰会由四川省发展和改革委员会、中共四川省委网络安全和信息化委员会办公室、四川省经济和信息化厅等部门联合指导&#xff0c;聚焦数字经济与实体经济深度融合、数字赋能经济社会转型发展等话题展开交流研讨。…

航天联志Aisino-AISINO26081R服务器通过调BIOS用U盘重新做系统(windows系统通用)

产品名称:航天联志Aisino系列服务器 产品型号:AISINO26081R CPU架构&#xff1a;Intel 的CPU&#xff0c;所以支持Windows Server all 和Linux系统&#xff08;重装完系统可以用某60驱动管家更新所有硬件驱动&#xff09; 操作系统&#xff1a;本次我安装的服务器系统为Serv…

对话芯动科技 | 助力云游戏 4K级服务器显卡的探索与创新

2021年芯动科技推出了基于IMG BXT GPU IP的风华1号显卡。单块风华1号显卡可在台式机和云游戏中实现4K级别的性能&#xff0c;渲染能力达到5 TFLOPS&#xff0c;如果在服务器中同时运行两块显卡&#xff0c;性能还可翻倍。该显卡是为不断扩大的安卓云游戏市场量身定制的&#xf…

时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM)

时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM) 目录 时序预测 | Python实现ConvLSTM卷积长短期记忆神经网络股票价格预测(Conv1D-LSTM)预测效果基本介绍程序设计参考资料预测效果 基本介绍 时序预测 | Python实现ConvLSTM卷积长短期记忆神…

智能指针面试题

智能指针被问到的概率还是很大的&#xff0c;特别是Shared_ptr&#xff0c;最好会手撕&#xff0c;亲身经历&#xff01; 基本概念 1. RAll RAII&#xff08;Resource Acquisition Is Initialization&#xff09;是一种利用对象生命周期来控制程序资源&#xff08;如内存、文…

解决Requests中使用httpbin服务器问题:自定义URL的实现与验证

问题背景 在使用Python的Requests模块进行单元测试时&#xff0c;可能会遇到无法使用本地运行的httpbin服务器进行测试的问题。这是因为测试脚本允许通过环境变量HTTPBIN_URL指定用于测试的本地httpbin实例&#xff0c;但在某些测试用例中&#xff0c;URL是硬编码为httpbin.or…

100套Axure RP大数据可视化大屏模板及通用组件库

106套Axure RP大数据可视化大屏模板包括了多种实用美观的可视化组件库及行业模板库&#xff0c;行业模板涵盖&#xff1a;金融、教育、医疗、政府、交通、制造等多个行业提供设计参考。 随着大数据的发展&#xff0c;可视化大屏在各行各业得到越来越广泛的应用。可视化大屏不再…

基于共生生物算法优化概率神经网络PNN的分类预测 - 附代码

基于共生生物算法优化概率神经网络PNN的分类预测 - 附代码 文章目录 基于共生生物算法优化概率神经网络PNN的分类预测 - 附代码1.PNN网络概述2.变压器故障诊街系统相关背景2.1 模型建立 3.基于共生生物优化的PNN网络5.测试结果6.参考文献7.Matlab代码 摘要&#xff1a;针对PNN神…

02-1解析xpath

我是在edge浏览器中安装的xpath&#xff0c;需要安装的朋友可以参考下面这篇博客最新版edge浏览器中安装xpath插件 一、xpathd的使用 安装lxml pip install lxml ‐i https://pypi.douban.com/simple导入lxml.etree from lxml import etreeetree.parse() 解析本地文件 htm…

11月最新版付费进群源码自动定位+开源

感觉这个和前几天发布的付费进群差不多。 但有部分地方不一样&#xff0c;也是有什么分销分站后台&#xff0c;看见就头大。 没测试具体功能&#xff0c;可以搭建出来&#xff0c;D盾也未检测到加密文件 更多源码请到www.baicxx.com

OpenCV技术应用(4)— 如何改变图像的透明度

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本节课就手把手教你如何改变图像的透明度&#xff0c;希望大家学习之后能够有所收获~&#xff01;&#x1f308; 目录 &#x1f680;1.技术介绍 &#x1f680;2.实现代码 &#x1f680;1.技术介绍 改变图像透明度的实…

(论文阅读40-45)图像描述1

40.文献阅读笔记&#xff08;m-RNN&#xff09; 简介 题目 Explain Images with Multimodal Recurrent Neural Networks 作者 Junhua Mao, Wei Xu, Yi Yang, Jiang Wang, Alan L. Yuille, arXiv:1410.1090 原文链接 http://arxiv.org/pdf/1410.1090.pdf 关键词 m-RNN、…

金融业务系统: Service Mesh用于安全微服务集成

随着云计算的不断演进&#xff0c;微服务架构变得日益复杂。为了有效地管理这种复杂性&#xff0c;人们开始采用服务网格。在本文中&#xff0c;我们将解释什么是Service Mesh&#xff0c;为什么它对现代云架构至关重要&#xff0c;以及它是如何解决开发人员今天面临的一些最紧…

PCL_点云分割_基于法线微分分割

一、概述 PCL_点云分割_基于法线微分分割_点云法向量微分-CSDN博客 利用不同的半径&#xff08;大的半径、小半径&#xff09;来计算同一个点的法向量差值P。判断P的范围&#xff0c;从而进行分割。 看图理解&#xff1a; 二、计算流程 1、计算P点小半径的法向量Ns 2、计…

Python3语法总结-基本数据类型①

Python3语法总结-基本数据类型① Python3语法总结一.注释和基本数据类型标识符与关键字注释变量标准数据类型数字(Number)布尔类型(bool) 未完待续... Python3语法总结 一.注释和基本数据类型 标识符与关键字 标识符是指程序中定义的一个名字&#xff0c;如变量名&#xff0…

NewStarCTF2023 Reverse Week3---Let‘s Go Wp

分析 程序打开后结合题目可以发现是 GO语言。 在GO语言中&#xff0c;main_init 要先于 main 函数运行。 在这里对一个iv做了处理。 用插件Signsrch发现AES加密 知道是AES后&#xff0c;就需要找密文&#xff0c;key和iv了。 密文应该就是前面的十六进制字符串。 key和i…

python趣味编程-5分钟实现一个F1 赛车公路游戏(含源码、步骤讲解)

Python 中的 F1 赛车公路游戏及其源代码 F1 Race Road Game是用Python编程语言开发的,它是一个桌面应用程序。 这款 Python 语言的 F1 赛道游戏可以免费下载开源代码,它是为想要学习 Python 的初学者创建的。 该项目系统使用了 Pygame 和 Random 函数。 Pygame 是一组跨平…

Oracle 存储过程数据插入临时表慢以及SQL语句查询慢

/*parallel*/ 解释: 一般表数据量比较大&#xff08;超过100万&#xff09;时&#xff0c;可以使用parallel强制启动并行度来提升查询速度 用法&#xff1a;/*parallel(table_short_name,cash_number)*/ 可以加到insert、delete、update、select的后面来使用 比如&#xff…

获取用户详细信息

pojo.user&#xff1a;JsonIgnore注解作用忽略密码属性&#xff0c;返回给用户的信息不能有敏感属性密码 package com.lin.springboot01.pojo;import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data;import java.time.LocalDateTime;Data public class Use…