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

序言

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

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


文章目录

  • 序言
  • 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 selfdef 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.cnimport os
import time
import loggingfrom 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 ActionChainsdef 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 dataif 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 driverdef 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.cnimport os
import re
import time
import logging
import requestsfrom bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditionsclass BaseCrawler:tag_regex = re.compile(r"<[^>]+>|\n|\t")global_timeout = 60global_interval = 300chrome_user_data_path = r"C:\Users\caoyang\AppData\Local\Google\Chrome\User Data"# Convert request headers copied from Firefox to dictionary@classmethoddef 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@classmethoddef 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, POSTdef easy_requests(self, method, url, **kwargs):while True:try:response = requests.request(method, url, **kwargs)breakexcept 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 driverdef 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 driverdef _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 dataif 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 driverdef _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 driverdef get_cookies(self, url, driver=None, browser="chrome"):quit_flag = Falseif driver is None:# If there is no driver passedquit_flag = Truedriver = 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 renderedself.check_element_by_xpath(driver, xpath=self.layout_xpaths["send-button-1"], timeout=60)	# Check if send button is renderedself.check_element_by_xpath(driver, xpath=self.layout_xpaths["send-button-2"], timeout=60)	# Check if send button is rendered# Delete old talktry:driver.find_element_by_xpath(self.layout_xpaths["delete-session"]).click()logging.info("Delete old talk ...")except:logging.info("No old talk found ...")# Requestlogging.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 promptlogging.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 ontry:driver.find_element_by_xpath(self.layout_xpaths["send-button-1"]).click()		# Click on the button to send the promptlogging.info("Use send button 1 ...")breakexcept:try:driver.find_element_by_xpath(self.layout_xpaths["send-button-2"]).click()	# Click on the button to send the promptlogging.info("Use send button 2 ...")breakexcept:logging.info("Use send button error ...")raise Exception("Use send button error ...")# Wait for responseself.check_element_by_xpath(driver, xpath=self.layout_xpaths["chat-area"], timeout=30)	# Check if chat area is renderedself.check_element_by_xpath(driver, xpath=self.layout_xpaths["human-box"], timeout=30)	# Check if human chat box is renderedself.check_element_by_xpath(driver, xpath=self.layout_xpaths["ai-box"], timeout=30)		# Check if AI chat box is renderedfinish_flag = True	# Indicating if AI generation is finishedwhile finish_flag:try:# If like or dislike appear, then stopdriver.find_element_by_xpath(self.layout_xpaths["like-or-dislike-area"])finish_flag = Falseexcept: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 elementai_box_text_inner_html = ai_box_text.get_attribute("innerHTML")										# Get inner HTML of the elementresponse = self.tag_regex.sub(str(), ai_box_text_inner_html).strip("\n\t ").replace('\n', '\\n')	# Process response textforbidden_flags = [forbidden_string in response for forbidden_string in self.forbidden_strings]if sum(forbidden_flags) > 0:# It indicates that a forbidden string occursfinish_flag = False# Extract AI response textai_box_text = driver.find_element_by_xpath(self.layout_xpaths["ai-box-text"])	# Find AI response text elementai_box_text_inner_html = ai_box_text.get_attribute("innerHTML")					# Get inner HTML of the elementresponse = self.tag_regex.sub(str(), ai_box_text_inner_html).strip("\n\t ")		# Process response textreturn 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_xpathsdef 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.mzph.cn/news/150997.shtml

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

相关文章

激光雕刻机的雕刻操作

今天小编带大家来学习一下在实践当中学习到的基础操作&#xff0c;激光雕刻机的雕刻玻璃操作&#xff0c;欢迎评论指正。 目录 什么是激光雕刻机 怎么操作激光雕刻机操作 打开电源 打开电脑&#xff0c;选择需要的软件 在CAD上进行绘图&#xff0c;绘图完成进行保存 在RD…

Ubuntu 20.04 调整交换分区大小

Ubuntu 调整交换分区大小 一、系统情况二、去除旧的交换分区文件三、配置并启用交换分区四、查看swap文件大小 一、系统情况 Ubuntu &#xff1a;Ubuntu 20.04.6 LTS 交换分区位置&#xff1a; cat /proc/swaps二、去除旧的交换分区文件 去掉旧的交换分区有两个步骤&#x…

广州一母婴店因设置0元购导致关店

我是卢松松&#xff0c;点点上面的头像&#xff0c;欢迎关注我哦&#xff01; 广州的一家母婴用品网店Minitutu因双十一优惠券设置错误&#xff0c;导致所有商品变成0元购买&#xff0c;引发消费者疯狂抢购&#xff0c;15万多单订单中有800多万元的损失。店家无奈之下只能暂停营…

css animation 动画如何保留动画结束后的状态 animation-fill-mode: forwards

css animation 动画如何保留动画结束后的状态 animation-fill-mode: forwards 一、问题描述 在做一个弹窗动画提示的时候遇到了一个问题&#xff1a; 在动画结束的时候&#xff0c;移除元素时会有闪一下的问题&#xff0c;像这样&#xff1a; 我的动画结尾是这样的&#xff…

LaTex生成引文(参考文献)时出现乱序,想把引文按顺序显示的解决方法

LaTex生成pdf时文献应用会乱序&#xff1a; 引用bib格式的参考文献时&#xff0c;会这么写&#xff1a; \bibliographystyle{plain} \bibliography{%filename%.bib} 而plain的意思是用作者的姓名排序&#xff0c;而不是按照引用顺序&#xff0c; 解决方案&#xff1a; 所以…

redis高级案列case

案列一 双写一致性 案例二 双锁策略 package com.redis.redis01.service;import com.redis.redis01.bean.RedisBs; import com.redis.redis01.mapper.RedisBsMapper; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; imp…

流体的压力

压力是流体力学中很重要的物理量&#xff0c;国际标准单位为 Pa&#xff08;帕斯卡&#xff09;&#xff0c;其他常用单位包括 MPa&#xff08;兆帕&#xff09;、atm&#xff08;标准大气压&#xff09;、Torr&#xff08;托&#xff09; 等。 在流体内部&#xff0c;压力是标…

数据结构树与二叉树的实现

目录 一、普通树的存储结构 1、双亲表示法 2.孩子表示法 二、二叉树 1.二叉树的顺序存储&#xff08;必须是完全二叉树&#xff0c;否则很浪费空间&#xff09; 1&#xff09;结构体 2.二叉树的链式存储 1&#xff09;结构体 2&#xff09;操作 1.创建一颗二叉树 2.创…

Linux内核mmap内存映射详解及例子实现

mmap在linux哪里&#xff1f; 什么是mmap&#xff1f; 上图说了&#xff0c;mmap是操作这些设备的一种方法&#xff0c;所谓操作设备&#xff0c;比如IO端口&#xff08;点亮一个LED&#xff09;、LCD控制器、磁盘控制器&#xff0c;实际上就是往设备的物理地址读写数据。 但…

【Zabbix监控二】之zabbix自定义监控内容案例(自动发现、自动注册)

一、自定义监控内容 案例&#xff1a;自定义监控客户端服务器登录的人数 需求&#xff1a;限制登录人数不超过3个人&#xff0c;超过5个人就发出报警 1、在客户端创建自定义key 明确需要执行的linux命令 创建zabbix监控项配置文件&#xff0c;用于自定义Key #在zabbix的配…

SMART PLC数值积分器功能块(矩形+梯形积分法完整源代码)

PLC的数值积分器算法也可以参考下面文章链接: PLC算法系列之数值积分器(Integrator)-CSDN博客文章浏览阅读1.5k次,点赞3次,收藏3次。数值积分和微分在工程上的重要意义不用多说,闭环控制的PID控制器就是积分和微分信号的应用。流量累加也会用到。有关积分运算在流量累加上…

算法-二叉树-简单-二叉树的遍历

记录一下算法题的学习6 首先我们要回忆一下怎么样遍历一个树&#xff1a; 三种遍历概念 先序遍历&#xff1a;先访问根节点&#xff0c;再访问左子树&#xff0c;最后访问右子树。 后序遍历&#xff1a;先左子树&#xff0c;再右子树&#xff0c;最后根节点。 中序遍历&…

kafka原理看这一篇就够了

为何使用消息队列 异步。接口方式实现多个系统协作&#xff0c;如图A系统作为用户请求接收方&#xff0c;需要调用多个系统的接口&#xff0c;这些接口还有可能是在A系统里同步调用&#xff0c;所以最后的接口耗时是多个系统接口耗时的总和&#xff1b;mq方式则可以异步发送消…

解决ubuntu23.10 wifi不能使用的问题

解决ubuntu23.10 wifi不能使用的问题 今天升级到了ubuntu23.10之后&#xff0c;wifi不能使用。 参考此视频解决了问题&#xff1a; https://www.youtube.com/watch?appdesktop&vn92O8rNKVb0 sudo lshw -class networkcd /etc/pm/sleep.dlssudo touch configsudo gedit co…

Java制作俄罗斯方块

Java俄罗斯方块小游戏 import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.List; imp…

C#,怎么修改(VS)Visual Studio 2022支持的C#版本

一些文字来自于 Microsoft . &#xff08;只需要读下面的红色文字即可&#xff01;&#xff09; 1 C# 语言版本控制 最新的 C# 编译器根据项目的一个或多个目标框架确定默认语言版本。 Visual Studio 不提供用于更改值的 UI&#xff0c;但可以通过编辑 .csproj 文件来更改值。…

1688阿里巴巴官方开放平台API接口获取商品详情、商品规格信息列表、价格、宝贝详情数据调用示例说明

商品详情API接口在电商平台和购物应用中的作用非常重要。它提供了获取商品详细信息的能力&#xff0c;帮助用户了解和选择合适的商品&#xff0c;同时也支持开发者进行竞品分析、市场研究和推广营销等工作&#xff0c;以提高用户体验和促进销售增长。 1688.item_get-获得1688商…

单链表的实现(Single Linked List)---直接拿下!

单链表的实现&#xff08;Single Linked List&#xff09;—直接拿下&#xff01; 文章目录 单链表的实现&#xff08;Single Linked List&#xff09;---直接拿下&#xff01;一、单链表的模型二、代码实现&#xff0c;接口函数实现①初始化②打印链表③创建一个结点④尾插⑤尾…

Unity 场景烘培 ——unity Post-Processing后处理1(四)

提示&#xff1a;文章有错误的地方&#xff0c;还望诸位大神不吝指教&#xff01; 文章目录 前言一、Post-Processing是什么&#xff1f;二、安装使用Post-Processing1.安装Post-Processing2.使用Post-Processing&#xff08;1&#xff09;.添加Post-process Volume&#xff08…

Flutter 3.16 中带来的更新

Flutter 3.16 中带来的更新 目 录 1. 概述2. 框架更新2.1 Material 3 成为新默认2.2 支持 Material 3 动画2.3 TextScaler2.4 SelectionArea 更新2.5 MatrixTransition 动画2.6 滚动更新2.7 在编辑菜单中添加附加选项2.8 PaintPattern 添加到 flutter_test 3. 引擎更新&#xf…