【日常】爬虫技巧进阶: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.hqwc.cn/news/189309.html

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

相关文章

CSS特效013:背景色彩不停流动效果

CSS常用示例100专栏目录 本专栏记录的是经常使用的CSS示例与技巧&#xff0c;主要包含CSS布局&#xff0c;CSS特效&#xff0c;CSS花边信息三部分内容。其中CSS布局主要是列出一些常用的CSS布局信息点&#xff0c;CSS特效主要是一些动画示例&#xff0c;CSS花边是描述了一些CSS…

uniapp App 端 版本更新检测

function checkVersion() { var req { //升级检测数据 appid: plus.runtime.appid, version: plus.runtime.version }; const timestamp Date.parse(new Date()); config.server.query_news uni.reque…

Ubuntu18.04安装Moveit框架

简介 Moveit是一个由一系列移动操作的功能包组成的集成化开发平台,提供友好的GUI,是目前ROS社区中使用度排名前三的功能包,Moveit包含以下三大核心功能,并集成了大量的优秀算法接口: 运动学:KDL,Trac-IK,IKFast...路径规划:OMPL,CHMOP,SBPL..碰撞检测:FCL,PCD... 一、更新功…

新品首发 | HP1011:高性能双相交错 PFC 数字控制器

随着PFC技术的发展&#xff0c;不断有新型PFC拓扑结构提出&#xff0c;如单相PFC、交错并联 PFC、传统无桥PFC、图腾柱无桥 PFC等。交错Boost PFC系统不仅具有并联系统的所有优点&#xff0c;还能减少输入电流纹波&#xff0c;降低开关管的电流应力。在中大功率场所通常采用工作…

复杂数据统计与R语言程序设计实验二

1、创建一个对象&#xff0c;并进行数据类型的转换、判别等操作&#xff0c;步骤如下。 ①使用命令清空工作空间&#xff0c;创建一个对象x&#xff0c;内含元素为序列&#xff1a;1&#xff0c;3&#xff0c;5&#xff0c;6&#xff0c;8。 ②判断对象x是否为数值型数据。 ③…

防抖-节流-深拷贝-事件总线

一、防抖与节流 1.认识防抖与节流函数 防抖和节流的概念其实最早并不是出现在软件工程中&#xff0c;防抖是出现在电子元件中&#xff0c;节流出现在流体流动中 而JavaScript是事件驱动的&#xff0c;大量的操作会触发事件&#xff0c;加入到事件队列中处理。而对于某些频繁…

C语言变量与常量

跟着肯哥&#xff08;不是我&#xff09;学C语言的变量和常量、跨文件访问、栈空间 栈空间还不清楚&#xff0c;期待明天的课程内容 C变量 变量&#xff08;Variable&#xff09;是用于存储和表示数据值的名称。 主要包括四个环节&#xff1a;定义、初始化、声明、使用 在我刚…

代码随想录算法训练营第三十八天【动态规划part01】 | 动态规划理论基础、509. 斐波那契数、70. 爬楼梯、746. 使用最小花费爬楼梯

动态规划理论基础 什么是动态规划 动态规划 (Dynamic Programming, DP)&#xff0c;是求解决策过程最优化的过程。 如果某一问题有很多重叠子问题&#xff0c;使用动态规划是最有效的。 所以动态规划中每一个状态一定是由上一个状态推导出来的&#xff0c;这一点就区分于贪…

metinfo 5.0.4 文件包含漏洞复现

metinfo 5.0.4 文件包含漏洞 漏洞环境 metinfo cms 版本 5.0.4 代码审计 在metinfo下的about/index.php代码中发现动态调用 上面没有赋值但是是有具体值的说明在上一个文件包含赋值了 查看这个文件的源代码 可以看到这里做了初始化但是是在fmodule不等于7的时候那假设等…

有关编译器的科普

Clang和GCC的主要区别如下所示&#xff1a; Clang比GCC编译用的时间更短&#xff0c;包括预处理、语法分析、解析、语义分析、抽象语法树生成的时间。Clang比GCC的内存占用更小。Clang生成的中间产物比GCC更小。Clang的错误提示比GCC更加友好。Clang有静态分析&#xff0c;GCC…

批量替换WordPress文章内图片链接

在WordPress使用过程中&#xff0c;如果中途更换了域名&#xff0c;原先文章内的图片使用的是原来的域名&#xff0c;就会造成文章页里面的图片链接无法显示。如果从后台文章挨个修改就比较麻烦。可以通过数据库进行批量替换即可。 使用 PHPMyadmin 打开 数据库&#xff0c;登…

【DevOps】Git 图文详解(二):Git 安装及配置

Git 图文详解&#xff08;二&#xff09;&#xff1a;Git 安装及配置 1.Git 的配置文件2.配置 - 初始化用户3.配置 - 忽略.gitignore Git 官网&#xff1a;https://www.git-scm.com/ 下载安装包进行安装。Git 的使用有两种方式&#xff1a; 命令行&#xff1a;Git 的命令通过系…