【日常学习】2025-8-13
今天第一天,先读取一个在项目根目录的Readme,这里介绍了一个自研小组件工具的使用,HTML driver。文档有他的用法,我主要还是记录一下今天学到的名词和复习一下提到的基础。
1. 什么是 WebDriver?
Selenium WebDriver 是用于浏览器自动化的工具,它能模拟真实用户操作(如点击、输入、跳转),支持动态网页(依赖 JavaScript 渲染的内容),本质是通过驱动程序(如 chromedriver)控制真实浏览器(Chrome、Firefox 等)。
优势:
- 能处理动态内容(JS 加载、AJAX 请求等);
- 支持模拟用户交互(表单提交、弹窗处理等);
- 可执行浏览器环境中的 JavaScript。
劣势:
- 启动浏览器耗时,资源消耗高(内存、CPU);
- 速度慢(尤其批量处理时);
- 依赖浏览器和驱动程序,部署复杂。
2. 为什么用 “HTML 解析工具” 替代 WebDriver?
如果你的需求是提取静态 HTML 内容(无需交互或动态渲染),使用 HTML 解析工具(如 BeautifulSoup、lxml、pyquery 等)会更合适,此时可以 “替代” WebDriver 的部分功能,原因如下:
(1)效率更高,速度更快
WebDriver 需要启动浏览器、加载页面(包括 CSS、JS、图片等),而 HTML 解析工具直接处理 HTML 文本,无需加载额外资源,速度可能提升 10 倍以上。
例如:爬取一个纯静态网页的文本内容,用 BeautifulSoup 解析 HTML 字符串,比用 WebDriver 打开浏览器加载页面快得多。
(2)资源消耗低
WebDriver 控制浏览器时,会占用大量内存(尤其是同时处理多个页面时);而 HTML 解析工具仅在内存中处理字符串,资源消耗极小,适合批量任务(如爬虫爬取百万级页面)。
(3)避免浏览器依赖
WebDriver 需要对应版本的浏览器和驱动程序(如 chromedriver 必须与 Chrome 版本匹配),部署时需额外配置;而 HTML 解析工具仅需安装 Python 库,跨环境兼容性更强。
(4)简化逻辑
如果无需模拟点击、输入等交互,仅需提取标签内容(如<div>
、<a>
中的文本),用 HTML 解析工具的 API(如find_all()
)比 WebDriver 的find_element()
更简洁。
相对 URL(Relative URL) 是指相对于当前文档(或基准 URL)的位置来表示资源地址的字符串,它不包含完整的协议(如http://
、https://
)和域名,仅通过路径关系指向目标资源。
3.相对URL
相对 URL 的核心是依赖一个 “基准 URL”(通常是当前页面的 URL)来确定完整地址,适用于同一网站内的资源引用,能简化链接的书写和维护。
与绝对 URL 的区别
类型 | 特点 | 示例 |
---|---|---|
绝对 URL | 包含完整协议、域名和路径,可直接定位资源,不依赖基准 URL。 | https://www.example.com/images/logo.png |
相对 URL | 不包含协议和域名,依赖当前页面的 URL 解析完整地址,仅适用于同一网站。 | images/logo.png 或 ../about.html |
相对 URL 的常见形式
- 网页开发:在 HTML 中引用同一网站的图片、CSS、JS 文件或内部页面时使用,例如
<img src="images/photo.jpg">
。 - 文件系统:本地文件管理中,用相对路径表示文件位置(如
../docs/report.pdf
)。 - API 请求:前端向同一域名的后端接口发送请求时,简化 URL 书写(如
fetch('/api/data')
)。
优势
- 简洁性:无需重复书写协议和域名,减少代码冗余。
- 可移植性:当网站域名变更时,相对 URL 无需逐个修改,降低维护成本。
例如,若网站从 http://old.com
迁移到 http://new.com
,页面中所有相对 URL(如 news.html
)会自动适配新域名,而绝对 URL 则需要批量替换。
4.outerHTML
和 innerHTML
在 HTML DOM(文档对象模型)中,outerHTML
和 innerHTML
是用于操作元素内容的属性,用于获取或设置元素的 HTML 内容,但它们的作用范围不同:
1. innerHTML
- 定义:返回或设置当前元素内部的 HTML 内容(包括所有子元素及其文本,但不包含元素自身的标签)。
- 作用范围:仅针对元素的 “内部”,不包含元素本身的起始标签和结束标签。
示例:
假设有如下 HTML 结构:
<div class="container"><p>Hello <span>World</span></p>
</div>
获取
innerHTML
:
对div.container
元素使用element.innerHTML
,会返回:<p>Hello <span>World</span></p>
设置
innerHTML
:
如果执行element.innerHTML = "<h1>New Content</h1>"
,则div
元素的内容会被替换为:<div class="container"><h1>New Content</h1> </div>
2. outerHTML
- 定义:返回或设置当前元素自身及内部的完整 HTML 内容(包括元素自身的标签、属性和所有子元素)。
- 作用范围:包含元素本身的标签及所有内部内容,相当于 “整个元素的 HTML”。
示例:
使用上面相同的 HTML 结构:
<div class="container"><p>Hello <span>World</span></p>
</div>
获取
outerHTML
:
对div.container
元素使用element.outerHTML
,会返回:<div class="container"><p>Hello <span>World</span></p> </div>
设置
outerHTML
:
如果执行element.outerHTML = "<section><p>Replaced</p></section>"
,则整个div
元素会被替换为:<section><p>Replaced</p> </section>
核心区别
属性 | 包含自身标签? | 操作范围 | 类比理解 |
---|---|---|---|
innerHTML | 不包含 | 仅元素内部的子内容 | 相当于 “盒子里的东西” |
outerHTML | 包含 | 元素自身 + 内部的所有内容 | 相当于 “盒子连同里面的东西” |
应用场景
innerHTML
:常用于修改元素内部的内容(如动态更新列表、替换文本块)。outerHTML
:常用于替换整个元素(如将一个div
换成section
,同时保留或修改内部结构)。
注意:两者在设置内容时都会解析 HTML 字符串,可能存在 XSS 安全风险(如果内容来自不可信来源),使用时需谨慎。
5. XSS(Cross-Site Scripting,跨站脚本攻击)
是一种常见的 Web 安全漏洞,指攻击者通过在网页中注入恶意脚本(通常是 JavaScript),当用户访问该网页时,恶意脚本会在用户的浏览器中执行,从而窃取用户信息、劫持用户会话、篡改页面内容等。
XSS 的核心原理
Web 应用在处理用户输入(如表单提交、URL 参数、评论等)时,如果未对输入内容进行严格过滤或转义,直接将用户输入的内容嵌入到页面 HTML 中,就可能导致攻击者注入的恶意脚本被浏览器执行。
例如:
若网站直接将用户输入的评论内容插入到页面中,攻击者输入 <script>alert('XSS')</script>
,如果网站未处理,这段代码会被当作正常脚本执行,弹出警告框 —— 这就是最基础的 XSS 攻击。
XSS 的主要类型
存储型 XSS(Persistent XSS)
- 恶意脚本被存储在服务器数据库中(如评论、用户资料),所有访问包含该脚本页面的用户都会受到攻击。
- 例子:攻击者在论坛发布含恶意脚本的评论,所有查看该评论的用户都会触发攻击。
反射型 XSS(Reflected XSS)
- 恶意脚本通过 URL 参数、表单提交等方式传递给服务器,服务器未处理就直接 “反射” 回页面,仅对当前访问者生效(一次性攻击)。
- 例子:攻击者构造含恶意脚本的 URL(如
http://example.com/search?query=<script>...</script>
),诱骗用户点击,用户访问后脚本执行。
DOM 型 XSS(DOM-based XSS)
- 恶意脚本不经过服务器,直接通过前端 JavaScript 操作 DOM 时注入(如使用
innerHTML
、document.write
等方法处理不可信输入)。 - 例子:前端代码直接将 URL 参数的值通过
innerHTML
插入到页面,攻击者可构造参数注入脚本。
- 恶意脚本不经过服务器,直接通过前端 JavaScript 操作 DOM 时注入(如使用
如何防范 XSS 攻击?
输入验证与过滤
- 对用户输入的内容进行严格校验,只允许符合规则的字符(如限制长度、过滤
<script>
等危险标签)。 - 使用成熟的库(如 Python 的
bleach
、JavaScript 的DOMPurify
)过滤 HTML 内容。
- 对用户输入的内容进行严格校验,只允许符合规则的字符(如限制长度、过滤
输出转义
- 在将用户输入的内容嵌入到 HTML 页面时,对特殊字符(如
<
、>
、&
、"
、'
)进行转义:<
转义为<
>
转义为>
&
转义为&
- 根据输出场景选择转义规则(如 HTML 属性、JavaScript 代码、URL 中需使用不同的转义方式)。
- 在将用户输入的内容嵌入到 HTML 页面时,对特殊字符(如
使用安全的 API
- 避免使用
innerHTML
、document.write
等直接解析 HTML 的方法,优先使用textContent
(仅渲染文本,不解析 HTML)。 - 动态创建元素时,使用
createElement
、setAttribute
等方法,而非拼接 HTML 字符串。
- 避免使用
设置安全相关的 HTTP 头
- Content-Security-Policy(CSP):限制页面可加载的脚本来源(如只允许本站域名的脚本),阻止外部恶意脚本执行。
- X-XSS-Protection:启用浏览器内置的 XSS 过滤器(部分浏览器默认开启)。
其他措施
- 对 Cookie 设置
HttpOnly
属性(防止 JavaScript 读取 Cookie)和Secure
属性(仅通过 HTTPS 传输)。 - 定期进行安全测试(如使用工具扫描漏洞、渗透测试)。
- 对 Cookie 设置
XSS 攻击的本质是 “信任了不可信的输入”,只要 Web 应用严格处理用户输入和输出,就能大幅降低风险。它是 Web 安全领域的基础漏洞之一,开发者需始终保持警惕。
6. 静态方法
静态方法的特点
不依赖实例或类
静态方法没有默认参数(如实例方法的self
或类方法的cls
),因此无法直接访问类的属性(如cls.attribute
)或实例的属性(如self.attribute
)。通过
@staticmethod
装饰器定义
必须使用@staticmethod
装饰器声明,否则会被视为普通的实例方法。调用方式灵活
可以通过类名直接调用,也可以通过实例对象调用(但不推荐后者,因为静态方法与实例无关)。
class MathUtils:# 定义静态方法:计算两个数的和@staticmethoddef add(a, b):return a + b# 定义静态方法:计算两个数的积@staticmethoddef multiply(a, b):return a * b# 通过类名调用静态方法(推荐)
print(MathUtils.add(2, 3)) # 输出:5
print(MathUtils.multiply(2, 3)) # 输出:6# 通过实例调用静态方法(允许但不推荐)
math_instance = MathUtils()
print(math_instance.add(4, 5)) # 输出:9
与实例方法、类方法的区别
方法类型 | 装饰器 | 默认参数 | 能否访问实例属性 | 能否访问类属性 | 调用方式 |
---|---|---|---|---|---|
实例方法 | 无 | self (实例) | 能 | 能(self.class.xxx ) | 只能通过实例调用 |
类方法 | @classmethod | cls (类) | 不能 | 能 | 类名或实例均可调用 |
静态方法 | @staticmethod | 无 | 不能 | 不能(除非显式传入) | 类名或实例均可调用 |
适用场景
静态方法适合封装与类相关但不依赖类状态的工具函数,主要作用是:
代码组织:将逻辑上属于类的工具函数放在类内部,避免全局函数散落,提高代码可读性。
例如,上面的MathUtils
类将加法、乘法等数学工具函数封装为静态方法,逻辑更清晰。避免不必要的参数传递:如果一个函数与类相关,但不需要访问类或实例的任何属性,用静态方法比实例方法更简洁(无需接收
self
参数)。兼容其他语言的习惯:类似 Java、C++ 中的静态方法,提供更符合跨语言开发者直觉的接口。
注意事项
- 静态方法不能直接修改类或实例的状态(除非显式传入类或实例作为参数)。
- 不要滥用静态方法:如果一个方法需要访问类属性,应使用类方法;如果需要访问实例属性,应使用实例方法。
总之,静态方法是类中独立的 “工具函数”,主要用于逻辑归类,不参与类的状态管理。
7.便捷函数
在编程中,便捷函数(Convenience Function) 是指为了简化常用操作、减少重复代码而设计的辅助性函数。它们通常封装了复杂或高频使用的逻辑,让开发者能够用更简洁的方式完成任务,无需重复编写相同的代码片段。
核心特点
简化操作:将多个步骤或复杂逻辑浓缩为一个函数,降低使用门槛。
例如,在处理文件时,一个读取并解析 JSON 文件的便捷函数,可能同时包含 “打开文件→读取内容→解析 JSON→关闭文件” 的完整流程,用户只需调用一次函数即可。高频场景优化:针对开发中频繁出现的需求(如数据转换、格式校验、资源获取等)设计,避免重复劳动。
例如,在 Web 开发中,一个 “发送 JSON 格式响应” 的便捷函数,可能封装了设置响应头、序列化数据、返回响应等步骤。不引入新功能:便捷函数通常不实现全新的核心逻辑,而是对已有功能的 “包装” 或 “组合”,目的是提升开发效率。
示例
假设在处理字符串时,经常需要 “去除首尾空格并转为小写”,可以封装一个便捷函数:
# 原始操作(每次都要写两行)
s = " Hello World "
s_clean = s.strip().lower() # 输出:"hello world"# 封装为便捷函数
def clean_string(s):"""去除首尾空格并转为小写(便捷函数)"""return s.strip().lower()# 简化后的使用
s = " Hello World "
s_clean = clean_string(s) # 同样输出:"hello world"
再比如,Python 标准库中的 json.load()
就是一个便捷函数,它封装了 “打开文件→读取内容→解析 JSON” 的流程,替代了手动编写这些步骤的麻烦:
import json# 不用便捷函数(多步操作)
with open("data.json", "r") as f:content = f.read()data = json.loads(content)# 用便捷函数(一步操作)
data = json.load(open("data.json", "r")) # 内部已封装上述逻辑
作用与价值
- 提高开发效率:减少重复代码,让开发者专注于核心业务逻辑。
- 降低出错概率:将常用逻辑封装为经过测试的函数,避免重复编写时可能出现的疏漏(如忘记关闭文件、漏写异常处理等)。
- 统一代码风格:团队中使用统一的便捷函数,可保证同类操作的实现一致,提升代码可维护性。
注意事项
- 便捷函数应保持 “单一职责”,避免过度封装导致逻辑复杂。
- 命名应清晰易懂,让使用者能直观理解其功能(如
clean_string
、send_json_response
)。
总之,便捷函数是 “为简化而生” 的工具,是提升代码效率和质量的常用手段。