------------------------------------------------------------------------------------------------------------------------
谷歌浏览器的开发者工具(DevTools)中,搜索功能允许你查找 HTML、CSS、文本内容等内容,具体操作步骤如下:
1. 打开开发者工具
快捷键:
Windows/Linux: Ctrl + Shift + I
Mac: Cmd + Option + I
或者右键页面 -> 检查 (Inspect)。
2. 搜索元素或内容
在 Elements 面板中搜索
进入 Elements 面板:
默认打开的是 Elements 面板(或手动切换到 Elements 标签)。
使用搜索框:
按下 Ctrl + F(Mac: Cmd + F)调出搜索框。
在搜索框中输入:
文本内容:直接输入目标文字。
CSS 选择器:如 .classname、#id。
XPath 表达式:如 //div[@class='example']。
浏览器会高亮显示匹配的元素。
------------------------------------------------------------------------------------------------------------------------
xpath 相关笔记:
XPath(XML Path Language)是一种用于在 XML 和 HTML 文档中定位节点的语言。以下是常用的 XPath 语法和表达式的介绍:
1. 节点选择
表达式 描述
/ 从根节点选择。
// 从文档的任意位置选择节点,而不考虑其位置。
. 当前节点。
.. 当前节点的父节点。
@ 选取属性。
2. 基本语法
表达式 描述
nodename 选取指定名称的所有节点。
* 匹配任何元素节点。
@* 匹配任何属性节点。
text() 选取当前节点中的文本。
@attribute 选取指定的属性值。
/a/b 选取根节点下的 <a> 中的 <b> 节点。
//b 选取文档中所有 <b> 节点。
3. 谓词(条件过滤)
谓词使用方括号 [] 包裹,用于筛选节点。
表达式 描述
//div[@id='example'] 选取所有 id="example" 的 <div> 节点。
//div[@class='item'][1] 选取第一个 class="item" 的 <div> 节点。
//ul/li[last()] 选取最后一个 <li> 节点。
//li[position()<3] 选取前两个 <li> 节点。
//*[@*='value'] 选取具有某个属性且值为 'value' 的所有节点。
4. 运算符
运算符 描述
+ 加法运算(适用于数值)。
- 减法运算。
* 乘法运算,或匹配所有节点。
div 除法运算(XPath 中不使用 / 表示除法)。
= 等于。
!= 不等于。
< 小于。
> 大于。
<= 小于等于。
>= 大于等于。
5. 函数
XPath 提供了一些常用函数,用于高级查询。
字符串函数
函数 描述
contains(string1, string2) 判断 string1 是否包含 string2。
starts-with(string1, string2) 判断 string1 是否以 string2 开头。
string-length(string) 返回字符串的长度。
normalize-space(string) 移除字符串前后的空白并合并多余的空格。
节点函数
函数 描述
count(node-set) 返回节点集的数量。
name(node) 返回节点名称。
local-name(node) 返回节点的本地名称(不带命名空间)。
数值函数
函数 描述
sum(node-set) 返回节点集中所有值的总和。
floor(number) 返回小于或等于指定数字的最大整数。
ceiling(number) 返回大于或等于指定数字的最小整数。
round(number) 返回最接近指定数字的整数。
6. 实例
选择节点
//div[@id='header']
匹配所有 id="header" 的 <div> 节点。
//a[text()='Home']
匹配文本为 Home 的所有链接。
//input[@type='checkbox' and @checked='checked']
匹配所有选中的复选框。
结合函数
//p[contains(@class, 'highlight')]
匹配 class 属性包含 highlight 的所有 <p> 节点。
//div[starts-with(@id, 'user')]
匹配 id 属性以 user 开头的 <div> 节点。
//li[position() mod 2 = 0]
选取偶数位置的 <li> 节点。
这套语法适用于调试和定位 HTML/XML 的节点。在实际开发中,结合工具(如浏览器 DevTools 或测试框架)可以更加高效地使用 XPath。
------------------------------------------------------------------------------------------------------------------------
lxml 是一个功能强大且高效的 Python 库,用于解析和处理 HTML 和 XML 文档。它基于 C 库 libxml2 和 libxslt,提供了丰富的功能,同时兼具高性能和易用性。以下是关于 lxml 的核心知识和使用方法。
1. 安装
bash
复制代码
pip install lxml
2. 基础模块
lxml 提供两个主要模块:
lxml.etree:用于处理 XML 和 HTML 文档。
lxml.html:专门用于 HTML 的处理。
3. 基本用法
3.1 解析 XML/HTML
从字符串解析
python
复制代码
from lxml import etree
# XML 解析
xml_str = "<root><child>data</child></root>"
tree = etree.fromstring(xml_str)
print(tree.tag) # 输出: root
# HTML 解析
from lxml import html
html_str = "<html><body><p>Hello, World!</p></body></html>"
tree = html.fromstring(html_str)
print(tree.xpath('//p/text()')) # 输出: ['Hello, World!']
从文件解析
python
复制代码
# 解析 XML 文件
tree = etree.parse("example.xml")
root = tree.getroot()
print(root.tag)
# 解析 HTML 文件
tree = html.parse("example.html")
3.2 XPath 查询
lxml 支持强大的 XPath 查询,用于快速提取数据。
python
复制代码
from lxml import etree
xml = """
<root>
<item id="1">First</item>
<item id="2">Second</item>
</root>
"""
tree = etree.fromstring(xml)
# 查询所有 item 节点
items = tree.xpath("//item")
print([item.text for item in items]) # 输出: ['First', 'Second']
# 按条件查询
second_item = tree.xpath("//item[@id='2']/text()")
print(second_item) # 输出: ['Second']
3.3 CSS 选择器(需要 lxml.html)
lxml.html 提供了 CSS 选择器的支持:
python
复制代码
from lxml import html
html_str = """
<html>
<body>
<div class="content">Hello</div>
<div class="footer">World</div>
</body>
</html>
"""
tree = html.fromstring(html_str)
# 使用 CSS 选择器
content = tree.cssselect(".content")
print(content[0].text) # 输出: Hello
3.4 修改和构建文档
构建新文档
python
复制代码
from lxml import etree
root = etree.Element("root")
child = etree.SubElement(root, "child")
child.text = "Hello, World!"
# 转换为字符串
print(etree.tostring(root, pretty_print=True).decode())
修改已有文档
python
复制代码
tree = etree.fromstring("<root><item>Old</item></root>")
item = tree.find("item")
item.text = "New"
print(etree.tostring(tree).decode()) # 输出: <root><item>New</item></root>
4. 高级功能
4.1 HTML 清理
lxml.html.clean 模块可以清理不安全的 HTML(如去除脚本)。
python
复制代码
from lxml.html.clean import clean_html
dirty_html = "<div><script>alert('Hello');</script>Safe content</div>"
cleaned = clean_html(dirty_html)
print(cleaned) # 输出: <div>Safe content</div>
4.2 XSLT 转换
使用 lxml 可以轻松进行 XSLT 转换。
python
复制代码
xslt_str = """
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<hello>
<xsl:value-of select="/root/item"/>
</hello>
</xsl:template>
</xsl:stylesheet>
"""
xml_str = "<root><item>Hello, XSLT!</item></root>"
xslt_root = etree.XML(xslt_str)
transform = etree.XSLT(xslt_root)
xml_root = etree.XML(xml_str)
result = transform(xml_root)
print(str(result)) # 输出: <hello>Hello, XSLT!</hello>
4.3 错误处理
lxml 提供详细的错误报告。
python
复制代码
try:
etree.fromstring("<root><unclosed></root>")
except etree.XMLSyntaxError as e:
print(f"XML Syntax Error: {e}")
5. 性能优化
字节解析:用字节而不是字符串输入以提高速度。
python
复制代码
tree = etree.fromstring(b"<root><child>data</child></root>")
iterparse:处理大文件时使用流式解析。
python
复制代码
context = etree.iterparse("large.xml", events=("start", "end"))
for event, elem in context:
print(event, elem.tag)
6. 应用场景
HTML 数据抓取:结合 XPath 或 CSS 选择器快速提取网页内容。
XML 配置处理:解析和生成复杂的 XML 配置文件。
安全处理 HTML:清理不安全的内容。
XSLT 转换:对 XML 数据进行复杂的重构。
lxml 是一个功能全面且高效的工具,非常适合处理结构化数据的应用场景。
------------------------------------------------------------------------------------------------------------------------
etree.HTML() 是 Python lxml 库中的一个函数,用于解析 HTML 文档,并将其转换为一个可操作的树结构对象(Element Tree)。通过这个对象,可以使用 XPath 等方法快速定位和提取 HTML 中的内容。
基本用法
python
复制代码
from lxml import etree
html_content = """
<html>
<body>
<h1>Title</h1>
<p class="description">This is a paragraph.</p>
<a href="https://example.com">Link</a>
</body>
</html>
"""
# 使用 etree.HTML() 解析 HTML 内容
tree = etree.HTML(html_content)
# 使用 XPath 提取内容
title = tree.xpath("//h1/text()")
paragraph = tree.xpath("//p[@class='description']/text()")
link = tree.xpath("//a/@href")
print("Title:", title[0])
print("Paragraph:", paragraph[0])
print("Link:", link[0])
输出结果:
vbnet
复制代码
Title: Title
Paragraph: This is a paragraph.
Link: https://example.com
工作原理
etree.HTML() 会自动处理 HTML 文档的常见格式问题,例如未闭合的标签。
它会返回一个根节点对象(Element),该对象表示整个 HTML 树。
可结合 XPath 表达式操作解析后的树结构,快速提取数据。
与 etree.parse() 的区别
方法 适用场景
etree.HTML() 主要用于解析 HTML 文本字符串,可以快速处理 HTML 数据(例如网络爬虫的 HTML 内容)。
etree.parse() 主要用于解析文件,适合处理本地的 HTML 或 XML 文件。
示例:
etree.parse():
python
复制代码
from lxml import etree
tree = etree.parse("example.html")
root = tree.getroot()
print(etree.tostring(root, pretty_print=True).decode())
etree.HTML():
python
复制代码
from lxml import etree
with open("example.html", "r", encoding="utf-8") as file:
html_content = file.read()
tree = etree.HTML(html_content)
print(etree.tostring(tree, pretty_print=True).decode())
常见操作
1. 处理不规范 HTML
HTML 页面中常见格式错误(如未闭合的标签、嵌套错误)会导致解析困难,etree.HTML() 会尝试修复这些问题:
python
复制代码
html = "<html><body><p>Unclosed paragraph<body></html>"
tree = etree.HTML(html)
print(etree.tostring(tree, pretty_print=True).decode())
输出:
html
复制代码
<html>
<body>
<p>Unclosed paragraph</p>
<body></body>
</body>
</html>
2. 提取多层嵌套内容
python
复制代码
html = """
<html>
<body>
<div class="container">
<h2>Main Title</h2>
<p>Paragraph with <b>bold</b> text.</p>
</div>
</body>
</html>
"""
tree = etree.HTML(html)
# 提取 h2 文本
h2_text = tree.xpath("//h2/text()")[0]
print("H2 Text:", h2_text)
# 提取段落中的所有文本内容(包括嵌套的 <b> 标签)
paragraph_text = tree.xpath("//p//text()")
print("Paragraph Text:", "".join(paragraph_text))
输出:
vbnet
复制代码
H2 Text: Main Title
Paragraph Text: Paragraph with bold text.
注意事项
HTML 格式修复:
etree.HTML() 会尝试修复不规范的 HTML,但修复后的结构可能与原始文档有细微差异。
如果需要完全保留原始结构,建议使用更宽松的解析库如 BeautifulSoup。
编码问题:
如果输入是二进制内容(如 requests.get(url).content),确保将其解码为字符串或正确指定编码。
性能:
etree.HTML() 性能较高,适合处理中小型 HTML 文档。如果需要处理大型文档或流式解析,可使用 iterparse。
总结
etree.HTML() 是一个非常实用的工具,特别适合快速解析 HTML 数据,并结合 XPath 提取目标内容。在网络爬虫、网页分析和数据清洗场景中经常使用。
------------------------------------------------------------------------------------------------------------------------
使用cookie+seesion鉴权的网站模拟登录的流程
1.传递账号密码,进行登录
2.登录之后保存cookie(返回时响应头的set-cookie字段中)
3.请求其他页面时,携带cookie
------------------------------------------------------------------------------------------------------------------------
selenium 等待机制
1.隐式等待 #全局
driver.implicitly_wait(10)#最多等待10秒,如果元素加载出来就立即继续执行
2.显示等待 #条件
1)第一步:创建一个等待对象
wait = WebDriverWait(driver,10)
2)第二步:定位元素【located】
3)第三步:设置等待条件
conditions=EC.visibility_of_element_located(located)
4)第四步:通过等待计时器对象去找
wait.until(conditions)
常见的等待条件
presence_of_element_located:元素存在【加载到DOM容器中】
visibility_of_element_located:元素可见*
element_to_be_clickable:元素可点击*
new_window_is_opened:等待新窗口出现
frame_to_be_avaliable_and_switch_to_it:加载并切换到frame中
------------------------------------------------------------------------------------------------------------------------
在 Selenium 中,可以使用 `expected_conditions.new_window_is_opened` 来等待新窗口的出现。以下是一个示例,展示如何等待一个新的窗口打开:
### 示例代码
```python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 初始化浏览器驱动(以 Chrome 为例)
driver = webdriver.Chrome()
try:
# 打开目标网页
driver.get("https://example.com")
# 点击触发新窗口打开的按钮
trigger_button = driver.find_element(By.ID, "openNewWindowButton")
trigger_button.click()
# 获取当前所有窗口的句柄(用于比较)
initial_windows = driver.window_handles
# 显示等待新窗口打开
wait = WebDriverWait(driver, 10)
wait.until(EC.new_window_is_opened(initial_windows))
# 获取所有窗口的句柄(包括新打开的窗口)
all_windows = driver.window_handles
# 切换到新窗口
new_window = [window for window in all_windows if window not in initial_windows][0]
driver.switch_to.window(new_window)
# 执行新窗口中的操作
print("当前窗口标题:", driver.title)
finally:
# 关闭浏览器
driver.quit()
```
---
### 代码详解
1. **`new_window_is_opened`**:
- 参数:`current_handles` 是当前已知窗口的句柄列表。
- 作用:等待新窗口打开(窗口句柄数量比 `current_handles` 多)。
2. **步骤说明**:
- `driver.window_handles`:获取当前打开的所有窗口句柄。
- 比较 `initial_windows` 和 `all_windows`,以找到新增的窗口句柄。
3. **切换到新窗口**:
- 使用 `driver.switch_to.window(window_handle)` 切换到目标窗口。
4. **超时处理**:
- 如果在设定时间内没有检测到新窗口,会抛出 `TimeoutException`。
---
### 常见场景
- 点击一个链接或按钮,触发新的浏览器窗口或标签页。
- 操作多个窗口或标签页时,使用句柄识别并切换。
如果有更复杂的场景需要处理,例如多个新窗口打开,可以进一步扩展代码逻辑!
------------------------------------------------------------------------------------------------------------------------
**窗口句柄**(Window Handle)是 Selenium 用来标识浏览器窗口或标签页的唯一标识符。每个浏览器窗口或标签页在 Selenium 中都有一个独一无二的句柄值,用来帮助脚本识别和切换窗口。
---
### 关键点
1. **句柄的本质**:
- 是一个字符串,由 Selenium 自动生成。
- 用来标识打开的浏览器窗口。
2. **获取句柄的方法**:
- `driver.current_window_handle`:获取当前窗口的句柄。
- `driver.window_handles`:获取所有已打开窗口的句柄列表。
3. **切换窗口**:
使用 `driver.switch_to.window(window_handle)` 来切换到指定窗口。
---
### 示例
假设有一个主窗口,点击按钮后打开了一个新窗口:
```python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
try:
# 打开主页面
driver.get("https://example.com")
# 获取主窗口句柄
main_window = driver.current_window_handle
print("主窗口句柄:", main_window)
# 点击按钮打开新窗口
driver.find_element(By.ID, "openNewWindowButton").click()
# 等待新窗口打开
wait = WebDriverWait(driver, 10)
wait.until(EC.new_window_is_opened([main_window]))
# 获取所有窗口句柄
all_handles = driver.window_handles
print("所有窗口句柄:", all_handles)
# 切换到新窗口
for handle in all_handles:
if handle != main_window:
driver.switch_to.window(handle)
print("切换到新窗口,标题:", driver.title)
break
# 返回主窗口
driver.switch_to.window(main_window)
print("返回主窗口,标题:", driver.title)
finally:
driver.quit()
```
---
### 常见用法
1. **获取当前窗口**:
```python
current_handle = driver.current_window_handle
print("当前窗口句柄:", current_handle)
```
2. **获取所有窗口**:
```python
handles = driver.window_handles
print("所有窗口句柄:", handles)
```
3. **切换窗口**:
```python
driver.switch_to.window(target_handle)
```
---
### 注意事项
- **句柄与窗口的关系**:
- 每个窗口的句柄是唯一的,直到窗口关闭。
- 当窗口关闭后,其句柄也随之失效。
- **句柄顺序**:
- `driver.window_handles` 返回的句柄列表顺序与窗口打开的顺序一致。
- **关闭窗口后切换**:
- 如果切换到一个已关闭的窗口,可能抛出错误。
- 建议在关闭窗口后,立即切换到有效的窗口。
如果还需要深入了解窗口操作或句柄的特殊场景,可以进一步讨论!
------------------------------------------------------------------------------------------------------------------------
`wait.until(EC.new_window_is_opened([main_window]))` 是 Selenium 显示等待中的一个具体使用方法,用于等待一个新的窗口或标签页打开。
---
### **代码解读**
1. **`wait.until()`**:
- `wait` 是一个显示等待的对象,通常由 `WebDriverWait(driver, timeout)` 创建。
- `wait.until(condition)` 的作用是:
- 持续检查 `condition` 是否为真。
- 如果在设定时间(`timeout`)内条件满足,代码继续执行。
- 如果超时后条件仍未满足,抛出 `TimeoutException`。
2. **`EC.new_window_is_opened([main_window])`**:
- `EC` 是 `expected_conditions` 的缩写,包含各种条件类。
- `new_window_is_opened` 是一个预定义的条件,用于检测是否有新窗口打开。
- 参数 `[main_window]` 是当前已知窗口的句柄列表。
- 它告诉 Selenium:**“这是我现在知道的窗口句柄列表,请监测是否有新的窗口句柄被添加。”**
- 条件会持续检查 `driver.window_handles`,即当前所有窗口的句柄。
- 如果检测到窗口句柄数量比 `[main_window]` 多,表示新窗口打开,条件满足。
---
### **具体流程**
以下代码说明了实际执行过程:
```python
# 1. 获取当前窗口句柄
main_window = driver.current_window_handle
# 2. 点击按钮,可能会打开一个新窗口
driver.find_element(By.ID, "openNewWindowButton").click()
# 3. 使用显示等待,直到新窗口打开
wait = WebDriverWait(driver, 10)
wait.until(EC.new_window_is_opened([main_window]))
# 4. 检查所有窗口句柄
all_handles = driver.window_handles
print("所有窗口句柄:", all_handles)
# 5. 找到新窗口句柄并切换
for handle in all_handles:
if handle != main_window:
driver.switch_to.window(handle)
print("新窗口标题:", driver.title)
break
```
---
### **总结**
- **`[main_window]`** 是现有窗口的句柄列表,用于与新窗口句柄列表对比。
- **`new_window_is_opened`** 检测窗口句柄是否增加,表明新窗口已打开。
- **显示等待的作用** 是避免新窗口打开速度慢导致的脚本失败。
如果你仍不清楚哪个部分,可以告诉我,我会进一步解释!
------------------------------------------------------------------------------------------------------------------------
在 Selenium 中,如果需要**等待页面刷新**,可以通过显示等待来检测某些页面刷新后的特定变化,例如:
1. 页面上的特定元素发生变化。
2. URL 地址发生变化。
3. DOM 结构中某些内容更新。
下面是几种常见的方法来等待页面刷新:
---
### **方法 1:等待页面 URL 改变**
页面刷新后 URL 通常会发生变化,可以用以下方法等待:
```python
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 获取当前页面的 URL
current_url = driver.current_url
# 等待页面 URL 改变
wait = WebDriverWait(driver, 10)
wait.until(EC.url_changes(current_url))
print("页面已刷新,当前 URL:", driver.current_url)
```
---
### **方法 2:等待某个元素的文本或属性改变**
可以选择页面中一个会发生变化的元素来检测刷新。例如,等待某个元素的文本变化:
```python
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 获取目标元素
element = driver.find_element(By.ID, "targetElement")
# 保存初始的文本内容
initial_text = element.text
# 等待目标元素的文本改变
wait = WebDriverWait(driver, 10)
wait.until(lambda driver: element.text != initial_text)
print("页面已刷新,元素文本已更新为:", element.text)
```
---
### **方法 3:等待 DOM 结构更新**
如果页面刷新后 DOM 会重新加载,可以通过等待某个特定元素重新加载来判断页面已刷新。
```python
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 定位目标元素的 ID
element_id = "uniqueElementId"
# 等待目标元素消失,然后重新出现
wait = WebDriverWait(driver, 10)
wait.until(EC.staleness_of(driver.find_element(By.ID, element_id))) # 等待元素变为"过时"
wait.until(EC.presence_of_element_located((By.ID, element_id))) # 等待元素重新加载
print("页面已刷新,元素已重新加载")
```
---
### **方法 4:等待整个页面加载完成**
有时需要等待页面加载完成,这可以通过 JavaScript 的 `document.readyState` 来判断。
```python
from selenium.webdriver.support.ui import WebDriverWait
# 等待页面加载完成
wait = WebDriverWait(driver, 10)
wait.until(lambda driver: driver.execute_script("return document.readyState") == "complete")
print("页面已刷新并加载完成")
```
---
### **总结**
根据页面刷新后的特定行为选择合适的等待策略:
1. 如果 URL 改变,用 `EC.url_changes`。
2. 如果元素文本或属性变化,用自定义 lambda 或 `expected_conditions`。
3. 如果页面重新加载 DOM,用 `EC.staleness_of` 和 `EC.presence_of_element_located`。
4. 如果需要等整个页面加载完成,用 `document.readyState`。
如果知道具体页面的刷新特性,可以告诉我,我会推荐更适合的方案!
------------------------------------------------------------------------------------------------------------------------
`wait.until(EC.url_changes(current_url))` 是 Selenium 中用于等待网页 URL 发生变化的显示等待方法。下面是具体的解释和分步解析。
---
### **1. 语句含义**
- `wait` 是 `WebDriverWait` 对象,用于设置显示等待。
- `EC.url_changes(current_url)` 是一个条件,它会不断检查当前页面的 URL 是否与提供的 `current_url` 不同。
- `wait.until(EC.url_changes(current_url))` 的作用是:**等待直到网页的 URL 发生变化或者超时为止**。
---
### **2. 关键点拆解**
#### **`current_url`**
这是刷新前页面的 URL:
```python
current_url = driver.current_url
```
通过 `driver.current_url` 获取当前页面的 URL,用作基准。
#### **`EC.url_changes(url)`**
`expected_conditions.url_changes(url)` 是一个预定义的条件,用来检测页面的 URL 是否与 `url` 不同:
- 它会不断检查 `driver.current_url` 的值。
- 如果 URL 发生变化,则条件满足,显示等待结束。
- 如果超时时间内 URL 没有变化,会抛出 `TimeoutException`。
#### **`wait.until(condition)`**
`wait.until(condition)` 的作用是:
- 持续检查 `condition`。
- 如果 `condition` 在超时前满足,程序继续执行。
- 如果超时后仍不满足,抛出异常。
---
### **3. 适用场景**
该方法适用于页面操作后 URL 会发生变化的情况,例如:
1. 点击按钮后页面跳转到新地址。
2. 提交表单后,页面重定向到结果页面。
3. JavaScript 触发 URL 的更新。
---
### **4. 示例代码**
#### **场景:点击按钮后等待页面跳转**
```python
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
try:
# 打开初始页面
driver.get("https://example.com")
# 获取初始 URL
current_url = driver.current_url
print("初始 URL:", current_url)
# 点击触发跳转的按钮
driver.find_element(By.ID, "redirectButton").click()
# 等待 URL 发生变化
wait = WebDriverWait(driver, 10)
wait.until(EC.url_changes(current_url))
# 打印新的 URL
print("新的 URL:", driver.current_url)
finally:
driver.quit()
```
---
### **5. 工作原理**
1. `driver.get("https://example.com")`:打开页面。
2. `current_url = driver.current_url`:记录当前页面的 URL。
3. `driver.find_element(By.ID, "redirectButton").click()`:触发页面跳转。
4. `wait.until(EC.url_changes(current_url))`:
- 每隔一段时间检查当前页面的 URL 是否与 `current_url` 不同。
- 如果 URL 改变,条件满足,等待结束。
- 如果超时 URL 没有变化,抛出 `TimeoutException`。
---
### **6. 注意事项**
- 如果页面跳转很快,等待可能立即结束。
- 如果页面刷新后 URL 不发生变化,此方法不适用。
- 超时时间(如 `10 秒`)需根据实际情况调整。
如果你还有其他疑问或需要更具体的示例,欢迎随时问!
------------------------------------------------------------------------------------------------------------------------
`wait.until(EC.staleness_of(driver.find_element(By.ID, element_id)))` 和 `wait.until(EC.presence_of_element_located((By.ID, element_id)))` 是 Selenium 中用来处理页面刷新或动态内容加载的两个显示等待条件。
---
### **1. `EC.staleness_of(element)`**
#### **含义**
- **作用**:等待一个元素变为“过时”(stale),即:
- 元素在 DOM(文档对象模型)中被移除。
- 元素在页面刷新后失效。
- 如果元素过时,条件满足,等待结束。
#### **使用场景**
- 页面刷新、元素被替换、内容动态更新等情况下,等待旧的元素失效。
#### **示例**
```python
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 定位目标元素
element = driver.find_element(By.ID, "targetElement")
# 等待元素变为过时(页面刷新或被移除)
wait = WebDriverWait(driver, 10)
wait.until(EC.staleness_of(element))
print("元素已变为过时")
```
#### **原理**
- 每个元素对象都与其所在的 DOM 绑定。
- 当页面刷新或元素被移除,原来的元素对象失效。
- 检测元素是否“过时”,如果是,返回 `True`。
---
### **2. `EC.presence_of_element_located(locator)`**
#### **含义**
- **作用**:等待一个元素出现在 DOM 中(但不一定可见)。
- 如果元素出现在 DOM 中,条件满足,等待结束。
#### **使用场景**
- 页面加载后,检查元素是否重新加载或出现。
#### **示例**
```python
# 等待目标元素重新加载
wait = WebDriverWait(driver, 10)
wait.until(EC.presence_of_element_located((By.ID, "targetElement")))
print("元素已重新加载")
```
#### **原理**
- `presence_of_element_located` 会定期检查 DOM 中是否存在指定的元素。
- 它不关心元素是否可见或交互,只需存在即可。
---
### **3. 结合使用场景**
在页面刷新或元素更新的场景中,可以组合这两个条件:
1. **等待旧元素过时**,确认页面或部分内容开始刷新。
2. **等待新元素加载**,确认刷新完成或新内容出现。
#### **完整示例:等待页面刷新并重新加载元素**
```python
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 假设页面中有一个 ID 为 "uniqueElementId" 的元素
element_id = "uniqueElementId"
# 定位目标元素
element = driver.find_element(By.ID, element_id)
# 等待元素变为过时
wait = WebDriverWait(driver, 10)
wait.until(EC.staleness_of(element))
print("元素已过时,页面可能正在刷新")
# 等待元素重新加载
wait.until(EC.presence_of_element_located((By.ID, element_id)))
print("元素已重新加载")
```
---
### **4. 注意事项**
- **元素过时检查**:
- 页面刷新或 DOM 结构更新时,原元素会失效。
- 如果目标元素在刷新后不会移除(仅内容改变),`staleness_of` 不适用。
- **元素重新加载检查**:
- `presence_of_element_located` 只确认元素存在于 DOM 中,不关心是否可见或可交互。如果需要检查可见性,使用 `EC.visibility_of_element_located`。
- **错误处理**:
- 如果超时,Selenium 会抛出 `TimeoutException`。
通过这种方式,可以确保操作的元素在页面动态加载或刷新后是最新的有效元素。如果还有疑问,请随时询问!
------------------------------------------------------------------------------------------------------------------------
`driver.switch_to.frame()` 是 Selenium 用于切换到 **HTML 中嵌套的 `<iframe>` 或 `<frame>`** 的方法。嵌套的 `iframe` 是独立的文档,不能直接访问主页面的内容。因此,在操作 `iframe` 内的元素前,必须先切换到该 `iframe`。
---
### **1. 使用方法**
#### **语法**
```python
driver.switch_to.frame(frame_reference)
```
#### **参数**
- `frame_reference` 可以是以下之一:
1. **索引号(index)**:
- 从 0 开始,表示第几个 `iframe`。
2. **名称或 ID(name or id)**:
- 使用 `iframe` 的 `name` 或 `id` 属性。
3. **WebElement**:
- 使用 `find_element` 定位到目标 `iframe` 元素。
---
### **2. 切换回主页面**
- 如果已经切换到某个 `iframe`,但需要切换回主页面:
```python
driver.switch_to.default_content()
```
- 如果当前 `iframe` 嵌套在另一个 `iframe` 内,可以切换回父级:
```python
driver.switch_to.parent_frame()
```
---
### **3. 示例代码**
#### **场景 1:使用索引切换到第一个 `iframe`**
```python
from selenium import webdriver
driver = webdriver.Chrome()
driver.get("https://example.com")
# 切换到第一个 iframe
driver.switch_to.frame(0)
# 操作 iframe 中的元素
iframe_element = driver.find_element(By.ID, "iframeElementID")
iframe_element.click()
# 切换回主页面
driver.switch_to.default_content()
driver.quit()
```
---
#### **场景 2:使用 `id` 或 `name` 切换**
假设页面中有一个 `iframe`:
```html
<iframe id="myIframe" name="iframeName"></iframe>
```
可以通过 `id` 或 `name` 切换:
```python
driver.switch_to.frame("myIframe") # 或 driver.switch_to.frame("iframeName")
```
---
#### **场景 3:嵌套 `iframe`**
如果 `iframe` 嵌套在另一个 `iframe` 内,需要逐级切换:
```html
<iframe id="outerIframe">
<iframe id="innerIframe"></iframe>
</iframe>
```
切换方法:
```python
# 切换到外部 iframe
driver.switch_to.frame("outerIframe")
# 再切换到内部 iframe
driver.switch_to.frame("innerIframe")
# 操作内部 iframe 的元素
element = driver.find_element(By.ID, "innerElement")
element.click()
# 切回到外部 iframe
driver.switch_to.parent_frame()
# 切回到主页面
driver.switch_to.default_content()
```
---
#### **场景 4:定位 `iframe` 并切换**
如果无法通过 `index` 或 `id` 切换,可以通过 `find_element` 定位:
```python
iframe = driver.find_element(By.TAG_NAME, "iframe")
driver.switch_to.frame(iframe)
```
---
### **4. 注意事项**
1. **必须先切换**:
- `iframe` 中的内容与主页面隔离,无法直接操作。
- 在操作 `iframe` 内的元素前,必须切换到对应的 `iframe`。
2. **嵌套的 `iframe`**:
- 如果 `iframe` 嵌套,必须逐级切换,不能直接跳到最深层的 `iframe`。
3. **切回主页面**:
- 如果不切回主页面,后续无法操作主页面上的元素。
4. **报错处理**:
- 如果 `frame_reference` 无效或 `iframe` 不存在,Selenium 会抛出 `NoSuchFrameException`。
通过以上方法,可以很好地操作嵌套在 `iframe` 中的内容。如果还有其他疑问,欢迎随时询问!
------------------------------------------------------------------------------------------------------------------------
要模拟滚动页面,你可以使用 Selenium 和 JavaScript 执行一些脚本来控制页面的滚动。以下是一些示例代码,展示如何使用 JavaScript 进行页面滚动:
### 使用 Selenium 结合 JavaScript 执行页面滚动:
```python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 启动浏览器
driver = webdriver.Chrome() # 你可以选择其他浏览器,如Firefox
# 打开网页
driver.get("https://example.com")
# 等待页面完全加载
wait = WebDriverWait(driver, 10)
# JS 滚动页面
script = "window.scrollBy(0, 1000);" # 水平滚动:scrollBy(x, y)
driver.execute_script(script)
# 等待滚动完成
wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="some_id"]')))
# 示例:滚动到页面底部
script_bottom = "window.scrollTo(0, document.body.scrollHeight);"
driver.execute_script(script_bottom)
# 示例:滚动到特定元素
element = wait.until(EC.presence_of_element_located((By.ID, 'specific_id')))
driver.execute_script("arguments[0].scrollIntoView();", element)
# 关闭浏览器
driver.quit()
```
### 解释代码:
1. **启动浏览器**:使用 `webdriver.Chrome()` 启动 Chrome 浏览器,也可以换成 Firefox 等其他浏览器。
2. **打开网页**:使用 `driver.get("URL")` 打开目标网页。
3. **滚动页面**:使用 `execute_script()` 执行 JavaScript 代码,`window.scrollBy(x, y)` 或 `window.scrollTo(x, y)` 来模拟滚动。
4. **等待滚动完成**:使用 `WebDriverWait` 确保滚动后页面内容加载完成。
### 示例中的滚动选项:
- `window.scrollBy(0, 1000)`:向下滚动 1000 像素。
- `window.scrollTo(0, document.body.scrollHeight)`:滚动到页面底部。
如果有任何问题或需要更复杂的操作,请告诉我!
------------------------------------------------------------------------------------------------------------------------
是的,您提供的配置可以帮助消除浏览器中的一些自动化提示(例如“正在接受自动化程序控制”)。这些提示通常是为了让用户知道当前浏览器正在由自动化程序(如 Selenium)控制,而这些配置则旨在隐藏这些提示,以便让爬虫行为更像正常用户。
### 配置解释:
```python
opt = webdriver.ChromeOptions()
# 禁用 "Chrome正在被自动化软件控制" 的提示栏
opt.add_argument('--disable-infobars')
# 移除 "chrome正在被自动化程序控制" 的提示
opt.add_experimental_option('excludeSwitches', ["enable-automation"])
# 禁用自动化扩展
opt.add_experimental_option('useAutomationExtension', False)
```
### 配置功能:
1. **`--disable-infobars`**:
- 这个参数可以禁止浏览器顶部出现“Chrome正在被自动化软件控制”的信息栏。
- 这是一个常见的提示,在一些自动化场景中,禁用它可以让浏览器看起来更像是普通的用户浏览。
2. **`excludeSwitches: ["enable-automation"]`**:
- 这个选项会禁用 `enable-automation` 开关,它通常在控制台中显示“Chrome正在被自动化程序控制”。
- 这个配置有助于隐藏来自自动化的标识,避免被网站检测到是自动化程序。
3. **`useAutomationExtension: False`**:
- 这个选项可以禁用 Chrome 的自动化扩展,它是 Selenium 驱动浏览器时默认启用的。
- 禁用该扩展可以进一步减少浏览器暴露出“自动化”的痕迹。
### 注意:
尽管这些配置可以消除一些自动化提示,但一些网站(特别是对反爬机制非常严格的网站)仍然可能会通过浏览器指纹、HTTP 请求头、JavaScript 监控等方式检测到是通过自动化工具控制的。为了解决这种问题,可能还需要结合其他反爬策略,如:
- 通过代理池避免IP封禁。
- 使用更复杂的模拟技术(例如随机延迟、模拟人类行为等)。
- 使用验证码识别服务等。
### 完整示例代码:
```python
from selenium import webdriver
opt = webdriver.ChromeOptions()
opt.add_argument('--disable-infobars')
opt.add_experimental_option('excludeSwitches', ["enable-automation"])
opt.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options=opt)
driver.get("https://example.com")
# 做一些操作
print(driver.title)
# 退出浏览器
driver.quit()
```
这个配置有助于减少 Selenium 操作时的检测痕迹,但并不是百分之百避免被反爬机制检测,特别是当目标网站有较为复杂的反自动化机制时,可能需要进一步加强反爬策略。
------------------------------------------------------------------------------------------------------------------------
在使用 Selenium 进行爬虫时,许多网站会通过不同的反爬机制来检测和阻止自动化工具。以下是一些常见的 **Selenium 反爬手段**,以及如何避免被检测到:
### 1. **浏览器指纹**(Browser Fingerprint)
- **问题**:Selenium 控制的浏览器会暴露一些特征,尤其是在浏览器的 `navigator` 属性、`userAgent` 等方面,反爬系统通过这些特征来识别自动化浏览器。
- **解决方法**:
- 通过修改或删除 `navigator.webdriver` 属性,使浏览器看起来不由自动化工具控制。
- 修改 `navigator.plugins`, `navigator.languages` 等属性。
- 修改 `navigator.userAgent`,避免检测到 `HeadlessChrome`。
- 禁用 `console.log`, `console.info`, `console.error` 等,避免打印自动化相关日志。
### 2. **WebDriver 控制痕迹**
- **问题**:浏览器对象中会暴露 `window.navigator.webdriver` 等属性,直接暴露了该浏览器是被 WebDriver 控制的。
- **解决方法**:
- 使用 JavaScript 脚本删除或重定义这些自动化特征,如:
```javascript
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
```
- 修改 `window.navigator.webdriver` 属性来隐藏浏览器的自动化控制痕迹。
- 禁用开发者模式下显示的提示信息(例如 `Chrome正在被自动化程序控制`)。
### 3. **`headless` 模式**
- **问题**:`headless` 模式下运行的浏览器通常容易被检测,尤其是它暴露的特征和界面不同于正常用户的操作。
- **解决方法**:
- 禁用 `headless` 模式,尽可能让浏览器呈现完整界面。通过设置浏览器选项:
```python
options.add_argument('--headless')
```
### 4. **浏览器事件监测**(如鼠标点击、滚动行为等)
- **问题**:反爬系统可能会监测用户的互动行为,如鼠标移动、点击和滚动,来判断是否是机器人。
- **解决方法**:
- 模拟正常用户行为,如鼠标的随机移动、点击、滚动等。
- 使用 `ActionChains` 来模拟鼠标的随机移动:
```python
actions.move_by_offset(random.randint(50, 200), random.randint(50, 200)).perform()
```
- 模拟滚动:
```python
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
```
### 5. **浏览器环境变量检测**
- **问题**:反爬系统会通过分析浏览器环境变量来判断是否为自动化程序。例如,Selenium 启动的浏览器会暴露一些特殊的环境变量。
- **解决方法**:
- 使用 `--disable-blink-features=AutomationControlled` 启动参数来禁用这些特征。
- 在 `ChromeOptions` 中设置 `excludeSwitches`,避免自动化标记:
```python
options.add_experimental_option('excludeSwitches', ['enable-automation'])
```
### 6. **页面加载时间和行为模式**
- **问题**:反爬机制可能会检查页面加载时间和行为模式。如果页面加载异常快,可能被判断为自动化爬虫。
- **解决方法**:
- 模拟用户等待时间,使得页面加载和交互更自然。
- 控制爬取速率,避免短时间内过多请求。
### 7. **验证码与行为验证**
- **问题**:许多网站使用验证码(如滑动验证码、人机验证等)来防止自动化访问。
- **解决方法**:
- 利用 **OCR** 技术(如 Tesseract)来自动识别验证码。
- 使用图像识别技术解决验证码问题。
- 通过人工干预或者集成第三方验证码破解服务来解决验证码问题。
### 8. **IP 限制与代理池**
- **问题**:反爬系统通过检测大量请求的 IP 地址来识别是否存在异常流量。
- **解决方法**:
- 使用 **代理池** 来更换 IP,避免过多的请求来自同一 IP 地址。
- 设置合理的请求间隔和随机化的请求头,模拟真实用户的行为。
### 9. **动态内容加载检测**
- **问题**:有些网站通过 `AJAX` 或其他动态内容加载技术来加载页面数据,而这类数据可能不会在页面源代码中直接呈现。
- **解决方法**:
- 通过 Selenium 等工具模拟浏览器请求数据,等待页面完全加载(包括异步加载的内容)。
- 使用浏览器的开发者工具(Network 标签)分析动态请求,模拟获取这些数据。
### 10. **检测过多的请求频率**
- **问题**:过高频率的请求容易被检测到。
- **解决方法**:
- 通过设置合理的请求间隔(`time.sleep()`)来模拟正常用户浏览网站的行为。
- 使用随机间隔来控制请求的时间,使其看起来更像是人工操作。
### 11. **避免触发反爬机制的行为**
- **问题**:某些网站可能会通过页面元素的异常行为(如滚动、点击等)触发反爬机制。
- **解决方法**:
- 调整模拟的鼠标行为(例如鼠标的移动和点击等)并保持一致性。
- 尽量模拟用户的正常使用行为,如滚动、点击、输入等。
### 综合防爬策略
1. **配置代理**:使用代理池,定期更换 IP 地址。
2. **模拟人类行为**:随机化鼠标和键盘事件的行为,包括点击、滚动等。
3. **动态控制请求频率**:使用随机时间间隔来防止过快访问。
4. **头部伪装**:伪装浏览器 `User-Agent` 和 `Referer` 等信息,避免被识别为爬虫。
5. **禁用 `headless` 模式**:避免使用 `headless`,并在启动时隐藏 Selenium 自动化痕迹。
6. **页面加载控制**:等待 JavaScript 完全渲染完成,模拟真实用户访问。
通过组合这些技术,可以有效地减轻被反爬机制检测和封禁的风险。
------------------------------------------------------------------------------------------------------------------------
好的,以下是一个 **通用的 JavaScript 脚本**,可以帮助你在使用 Selenium 进行爬虫时,隐藏一些常见的自动化痕迹,从而避免被反爬机制检测:
### 通用 JS 脚本:隐藏 Selenium 自动化特征
```javascript
// 1. 隐藏 WebDriver 特征
try {
Object.defineProperty(navigator, 'webdriver', { get: () => undefined }); // 防止暴露 webdriver 属性
} catch (e) {
console.warn("无法重定义 'navigator.webdriver'");
}
// 2. 修改 navigator.plugins 和 navigator.languages,避免被识别为自动化工具
try {
Object.defineProperty(navigator, 'plugins', { get: () => [] }); // 禁用插件检测
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); // 设置语言为英文
} catch (e) {
console.warn("无法修改 'navigator.plugins' 或 'navigator.languages'");
}
// 3. 修改 navigator.userAgent,避免识别为 Headless 浏览器
const userAgent = navigator.userAgent;
if (userAgent.includes('HeadlessChrome')) {
try {
Object.defineProperty(navigator, 'userAgent', {
get: () => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
});
} catch (e) {
console.warn("无法修改 'navigator.userAgent'");
}
}
// 4. 禁用开发者模式下的提示 (例如 chrome headless)
try {
window.chrome = { ...window.chrome, runtime: {} }; // 禁用 "正在接受自动化程序控制" 的提示
} catch (e) {
console.warn("无法修改 'window.chrome.runtime'");
}
// 5. 覆盖 console 中的一些方法,避免检测到自动化日志
try {
console.log = function() {};
console.info = function() {};
console.error = function() {};
} catch (e) {
console.warn("无法修改 'console' 方法");
}
// 6. 修改 `window.navigator.webdriver`,进一步隐藏自动化特征
try {
window.navigator.__defineGetter__('webdriver', function() {
return undefined;
});
} catch (e) {
console.warn("无法修改 'window.navigator.webdriver'");
}
// 7. 随机模拟用户行为:模拟鼠标移动、滚动等
// 在实际爬虫脚本中,根据需求调用相关的 JavaScript 方法来模拟鼠标和滚动行为。
// 例如,模拟鼠标移动:
document.body.dispatchEvent(new MouseEvent('mousemove', {
clientX: Math.floor(Math.random() * window.innerWidth),
clientY: Math.floor(Math.random() * window.innerHeight)
}));
// 8. 修改 document.readyState,使其看起来页面已经完全加载
if (document.readyState === "loading") {
document.documentElement.setAttribute('data-ready', 'true');
}
```
### 说明:
1. **隐藏 WebDriver 特征**:通过重定义 `navigator.webdriver` 属性,避免被反爬检测为自动化工具。此代码尝试捕获重定义错误,防止页面抛出异常。
2. **修改 `navigator.plugins` 和 `navigator.languages`**:有些反爬机制通过检测浏览器插件和语言来识别爬虫,修改这些值使其更符合真实用户。
3. **修改 `navigator.userAgent`**:避免浏览器的 `userAgent` 字符串中出现 `HeadlessChrome`,使其看起来像一个普通的浏览器。
4. **禁用 Chrome Headless 的开发者模式提示**:通过修改 `window.chrome.runtime` 来去除自动化提示。
5. **覆盖 `console` 方法**:通过重定义 `console` 的日志方法来避免反爬检测的日志特征。
6. **修改 `window.navigator.webdriver`**:进一步避免一些自动化检测系统通过 `window.navigator.webdriver` 属性来检测 Selenium。
7. **随机模拟用户行为**:通过模拟鼠标移动、点击和滚动行为,避免反爬系统根据这些行为识别自动化工具。
8. **修改 `document.readyState`**:通过检查页面的加载状态,使页面看起来像已经完全加载,避免被检测为加载不完全的自动化工具。
### 使用方法:
1. 将上述脚本保存为 `hide_selenium.js` 文件。
2. 在 Selenium Python 脚本中读取并执行该 JavaScript 文件:
```python
import time
from selenium import webdriver
# 反爬配置
opt = webdriver.ChromeOptions()
opt.add_argument('--disable-infobars') # 禁用 "Chrome正在被自动化软件控制" 的提示栏
opt.add_experimental_option('excludeSwitches', ["enable-automation"]) # 移除 "chrome正在被自动化程序控制" 的提示
opt.add_experimental_option('useAutomationExtension', False) # 禁用自动化扩展
# 启动浏览器并应用上述配置
driver = webdriver.Chrome(options=opt)
# 读取 JavaScript 文件内容
with open("hide_selenium.js", "r", encoding="utf-8") as file:
js_script = file.read()
# 执行 JavaScript 脚本来隐藏 Selenium 的自动化特征
driver.execute_script(js_script)
# 打开目标网址
driver.get("https://www.aqistudy.cn/")
print("打开页面,页面加载完成!")
# 等待页面加载
driver.implicitly_wait(10) # 等待10秒,确保页面加载完成
print("网址加载完成!")
# 等待 10 秒钟查看效果
time.sleep(10)
print("网页效果展示完成!")
# 关闭浏览器
driver.quit()
print("关闭浏览器")
```
### 注意:
- 该脚本可以大大减少 Selenium 被检测为自动化工具的概率,但不能完全避免所有的反爬措施。实际使用中,还需要结合 **模拟用户行为**(如鼠标移动、随机点击等)和 **使用代理池** 来进一步提高爬虫的隐蔽性。
- 在使用过程中,确保与目标网站的使用协议一致,避免违法爬取或对网站造成过大负担。
------------------------------------------------------------------------------------------------------------------------
最终版:
### 最终版 JavaScript 文件
将以下内容保存为 `hide_selenium.js`:
```javascript
// 隐藏 Selenium 自动化特征的 JavaScript 脚本
// 1. 隐藏 navigator.webdriver
try {
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
} catch (e) {
console.warn("无法重定义 'navigator.webdriver'");
}
// 2. 修改 navigator.plugins 和 navigator.languages
try {
Object.defineProperty(navigator, 'plugins', { get: () => [] });
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
} catch (e) {
console.warn("无法修改 'navigator.plugins' 或 'navigator.languages'");
}
// 3. 模拟真实的 window.outerWidth 和 window.outerHeight
try {
Object.defineProperty(window, 'outerWidth', { get: () => window.innerWidth });
Object.defineProperty(window, 'outerHeight', { get: () => window.innerHeight });
} catch (e) {
console.warn("无法修改 'window.outerWidth' 或 'window.outerHeight'");
}
// 4. 修复 document.visibilityState
try {
Object.defineProperty(document, 'visibilityState', { get: () => 'visible' });
} catch (e) {
console.warn("无法修改 'document.visibilityState'");
}
// 5. 模拟用户行为(鼠标和键盘事件)
try {
const simulateEvent = (type, options = {}) => {
const event = new Event(type, { bubbles: true, cancelable: true, ...options });
document.dispatchEvent(event);
};
simulateEvent('mousemove', { clientX: 100, clientY: 100 });
simulateEvent('scroll');
} catch (e) {
console.warn("无法模拟事件");
}
// 6. 修复权限检测
try {
const originalQuery = navigator.permissions.query;
navigator.permissions.query = (parameters) =>
parameters.name === 'notifications'
? Promise.resolve({ state: 'denied' })
: originalQuery(parameters);
} catch (e) {
console.warn("无法修复 'navigator.permissions.query'");
}
// 7. 修改 User-Agent 避免检测到 HeadlessChrome
const userAgent = navigator.userAgent;
if (userAgent.includes('HeadlessChrome')) {
try {
Object.defineProperty(navigator, 'userAgent', {
get: () => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36'
});
} catch (e) {
console.warn("无法修改 'navigator.userAgent'");
}
}
// 8. 覆盖 console 的方法
try {
console.log = function() {};
console.info = function() {};
console.error = function() {};
} catch (e) {
console.warn("无法覆盖 'console' 方法");
}
```
---
### 最终版 Python 示例文件
将以下内容保存为 `selenium_example.py`:
```python
import time
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
# 1. 配置 Selenium ChromeOptions
opt = webdriver.ChromeOptions()
opt.add_argument('--disable-infobars') # 禁用 "Chrome正在被自动化软件控制" 的提示栏
opt.add_experimental_option('excludeSwitches', ['enable-automation']) # 移除自动化提示
opt.add_experimental_option('useAutomationExtension', False) # 禁用自动化扩展
opt.add_argument('--disable-blink-features=AutomationControlled') # 禁用 Blink 特性检测
opt.add_argument('--start-maximized') # 启动时窗口最大化
opt.add_argument('--disable-dev-shm-usage') # 防止因内存不足导致的问题
opt.add_argument('--no-sandbox') # 禁用沙盒
opt.add_argument('--disable-extensions') # 禁用扩展
# 自定义 User-Agent
user_agent = (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
"(KHTML, like Gecko) Chrome/110.0.5481.178 Safari/537.36"
)
opt.add_argument(f'user-agent={user_agent}')
# 2. 启动 Chrome 浏览器
driver = webdriver.Chrome(options=opt)
# 3. 执行 JavaScript 文件
with open("hide_selenium.js", "r", encoding="utf-8") as file:
js_script = file.read()
print("执行的 JavaScript 代码如下:")
print(js_script)
driver.execute_script(js_script) # 执行 JavaScript
print("JavaScript 代码执行完成!")
# 4. 打开目标网址
url = "https://www.aqistudy.cn/"
driver.get(url)
print("页面已加载:", url)
# 5. 模拟用户行为
actions = ActionChains(driver)
actions.move_by_offset(100, 200).perform() # 模拟鼠标从当前位置向右下移动 100 像素和 200 像素
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") # 模拟滚动到页面底部
time.sleep(2) # 等待 2 秒,模拟用户在页面底部停留时间
driver.execute_script("window.scrollTo(0, 0);") # 模拟滚动回页面顶部
time.sleep(2) # 等待 2 秒,模拟用户在页面顶部停留时间
# 6. 等待页面加载
driver.implicitly_wait(10)
print("页面加载完成!")
# 7. 保持窗口打开 10 秒,供观察效果
time.sleep(10)
print("页面无异常效果!可开始接下来的操作!")
# 8. 关闭浏览器
driver.quit()
print("浏览器已关闭")
```
---
### 使用说明
1. 将 JavaScript 文件保存为 `hide_selenium.js`,与 Python 文件放在同一目录下。
2. 确保安装了最新版本的 Chrome 和 Selenium,并将 ChromeDriver 添加到环境变量中。
3. 运行 Python 文件进行测试:`python selenium_example.py`。
4. 如果目标页面仍然无法正确加载数据,请检查目标网站对反爬的特殊机制并调整模拟行为。
------------------------------------------------------------------------------------------------------------------------