1. 项目概述:从“看”网页到“动”网页的自动化跨越
在数据驱动的今天,无论是市场调研、竞品分析还是日常办公,我们常常需要从网页上获取信息,或者重复性地在网站上填写表单、执行登录操作。手动操作不仅效率低下,还容易出错。作为一名常年和数据打交道的开发者,我几乎每天都会遇到这类需求。比如,定时抓取某个资讯网站的最新文章标题,或者自动登录公司内部系统下载日报表。最初,我也是一个一个页面去翻,一个一个表单去填,直到被这种重复劳动折磨得忍无可忍,才下定决心用Python把这一切自动化。
这个项目的核心,就是利用Python教会你的程序如何“浏览”网页。它不仅仅是静态地“看”(获取内容),更要能动态地“动”(交互操作)。这听起来有点像爬虫,但比基础爬虫更进一步,涉及到了与网页元素的实时交互。实现这一套流程,意味着你可以将任何基于Web的、有规律的手动操作,转化为一个静默、高效、准确的自动化脚本。无论是零基础想入门自动化,还是已经会写简单爬虫想提升技能,掌握这套“获取+填表+登录”的组合拳,都能极大拓展你的能力边界,直接应用到无数真实场景中。
2. 核心工具选型:Requests 与 Selenium 的黄金组合
工欲善其事,必先利其器。在Python的生态里,针对“获取网页内容”和“自动交互”这两类需求,有两套截然不同但相辅相成的工具栈。选对工具,项目就成功了一半。
2.1 静态内容获取利器:Requests + BeautifulSoup
对于单纯的、不需要执行JavaScript代码的网页内容获取,
Requests
库是当之无愧的王者。它模拟了浏览器的HTTP请求,极其高效和轻量。
为什么是Requests?
它的设计哲学就是“为人类而生”,API简洁直观。发送一个GET请求获取网页HTML源码,只需要两行代码。相比之下,Python内置的
urllib
库就显得繁琐许多。在绝大多数信息展示类网站(如新闻门户、博客、文档网站),数据都直接包含在初始HTML响应中,
Requests
足以胜任。
BeautifulSoup的定位
Requests
负责把网页“下载”下来,得到的是原始的HTML字符串。要从这一大团“标记”中精准地提取出我们需要的信息(比如所有的文章标题、价格、链接),就需要一个解析器。
BeautifulSoup
(简称bs4)就是这个角色。它能够将复杂的HTML文档转换成一个复杂的树形结构(DOM树),然后让你可以像使用导航地图一样,通过标签名、CSS类、ID等属性,轻松定位并提取任何你想要的元素内容。
这对组合的适用场景
- 抓取静态渲染的网页内容。
- 目标数据直接存在于页面源代码中(右键“查看网页源代码”可见)。
- 无需处理登录状态(或登录状态可通过请求头简单维持)。
- 对执行速度要求高。
2.2 动态交互模拟核心:Selenium
然而,现代网页越来越多地采用了动态渲染技术。很多内容(尤其是需要登录后查看的,或者由JavaScript异步加载的数据)在初始HTML中并不存在,而是在浏览器执行了JS代码后才生成的。此外,自动填表和登录本身就是一个需要驱动浏览器、模拟点击、输入等用户行为的过程。这时,
Requests
就力不从心了。
Selenium为何不可替代?
Selenium
的本质是一个浏览器自动化测试工具。它可以直接启动一个真实的浏览器(如Chrome, Firefox),或者无界面的浏览器引擎,并允许你通过代码完全控制这个浏览器:导航到某个网址,在输入框里打字,点击按钮,下拉选择……就像有一个看不见的手在操作一样。因为它运行的是完整的浏览器环境,所以所有JavaScript都会被执行,动态内容完全可见,交互操作也能被完美模拟。
WebDriver:连接代码与浏览器的桥梁
Selenium
本身是一个控制协议和API库,它需要与一个名为
WebDriver
的组件配合工作。你需要根据你使用的浏览器(推荐Chrome)下载对应版本的
ChromeDriver
,并将其路径告知Selenium。
WebDriver
会启动浏览器实例,并接收来自Selenium代码的指令。
这对组合的适用场景
- 网页内容由JavaScript动态生成。
- 需要模拟复杂的用户交互流程(登录、翻页、下拉选择、上传文件等)。
- 需要处理基于Cookie或Session的登录状态保持。
- 需要执行操作后等待页面元素变化。
注意: 在实际项目中,我常常混合使用这两套方案。例如,用
Selenium完成复杂的登录过程并获取关键的登录凭证(如Cookies),然后将这个凭证交给Requests去高效地抓取后续的大量数据页面。这既利用了Selenium强大的交互能力,又发挥了Requests高效快速的优势。
3. 环境搭建与核心库安装
在开始写代码之前,我们需要把“战场”布置好。一个清晰独立的Python环境是专业开发的好习惯,它能避免不同项目间的库版本冲突。
3.1 创建并激活虚拟环境
打开你的终端(Windows用CMD或PowerShell,macOS/Linux用Terminal),执行以下命令:
# 1. 创建一个新的虚拟环境,命名为 ‘web_auto‘
python -m venv web_auto
# 2. 激活虚拟环境
# Windows:
web_auto\Scripts\activate
# macOS/Linux:
source web_auto/bin/activate
激活后,你的命令行提示符前通常会显示
(web_auto)
,表示你已经在这个独立环境中了。
3.2 安装必备的Python库
在激活的虚拟环境下,使用pip进行安装。我们将安装前面提到的核心库,以及一些辅助工具。
pip install requests beautifulsoup4 selenium lxml
-
requests: HTTP请求库。 -
beautifulsoup4: HTML/XML解析库。 -
selenium: 浏览器自动化库。 -
lxml: 一个高效的HTML解析器,作为BeautifulSoup的解析引擎之一,速度比纯Python实现的解析器快很多。
3.3 下载并配置浏览器驱动(以Chrome为例)
这是使用
Selenium
的关键一步。
-
查看你的Chrome浏览器版本
:打开Chrome,点击右上角三个点 -> 帮助 -> 关于Google Chrome。记下版本号(例如,
128.0.6613.138)。 -
下载对应版本的ChromeDriver
:
- 访问 ChromeDriver官方下载站 或国内镜像站。
- 下载与你的Chrome主版本号完全一致的驱动(例如,Chrome是128.x,就下载128.x.x.x版本的驱动)。
-
放置驱动并配置路径
:
-
方法一(推荐,便于管理)
:将下载的
chromedriver.exe(Windows)或chromedriver(macOS/Linux)文件,放在你项目目录下一个专门的drivers文件夹里。然后在代码中指定路径。 -
方法二(加入系统PATH)
:将驱动文件放在一个固定目录(如
C:\WebDriver\),并将该目录添加到系统的环境变量PATH中。这样Selenium就能自动找到。
-
方法一(推荐,便于管理)
:将下载的
实操心得 :驱动版本必须与浏览器版本匹配,否则Selenium会报错。建议将驱动文件放在项目内,这样项目迁移时环境是完整的。另外,浏览器会自动更新,而驱动不会。如果某天脚本突然不能用了,第一个要检查的就是浏览器版本是否升级,需要重新下载匹配的驱动。
4. 实战一:使用Requests+BeautifulSoup获取静态网页内容
让我们从一个最简单的任务开始:抓取一个静态网页的标题和所有链接。假设我们的目标是某个技术博客的首页。
4.1 发送请求与处理响应
import requests
from bs4 import BeautifulSoup
# 目标URL
url = ‘https://example-blog.com‘
# 设置请求头,模拟真实浏览器访问,避免被简单反爬
headers = {
‘User-Agent‘: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36‘
}
try:
# 发送GET请求
response = requests.get(url, headers=headers)
# 检查请求是否成功(状态码200表示成功)
response.raise_for_status()
# 设置响应的编码(通常为utf-8)
response.encoding = response.apparent_encoding or ‘utf-8‘
# 获取网页HTML内容
html_content = response.text
except requests.exceptions.RequestException as e:
print(f“请求出错: {e}“)
html_content = None
if html_content:
print(“成功获取网页内容!“)
# 可以打印前500字符看看内容
print(html_content[:500])
关键点解析 :
-
headers:这是反爬虫的第一道防线。没有User-Agent的请求很容易被服务器识别为脚本而拒绝。这里我们伪装成Chrome浏览器。 -
response.raise_for_status():这是一个好习惯。如果响应状态码不是200(比如404、500),这一行会直接抛出异常,让我们能及时处理错误,而不是拿着一个错误的页面内容去解析。 -
response.encoding:网页可能有自己的编码(如gbk),正确设置编码才能避免中文乱码。apparent_encoding是requests库推测的编码,通常比较准。
4.2 使用BeautifulSoup解析与提取数据
现在我们已经拿到了HTML字符串,接下来用BeautifulSoup来“读懂”它。
# 使用BeautifulSoup解析HTML,指定使用‘lxml‘解析器(需要先安装lxml库)
soup = BeautifulSoup(html_content, ‘lxml‘)
# 1. 提取网页标题(<title>标签内的内容)
page_title = soup.title.string if soup.title else ‘无标题‘
print(f“网页标题: {page_title}“)
# 2. 提取所有超链接(<a>标签的href属性)
all_links = []
for a_tag in soup.find_all(‘a‘, href=True): # 只找有href属性的a标签
link = a_tag[‘href‘]
# 处理相对链接,将其补全为绝对链接
if link.startswith(‘/‘):
link = requests.compat.urljoin(url, link)
all_links.append(link)
print(f“\n共找到 {len(all_links)} 个链接:“)
for i, link in enumerate(all_links[:10], 1): # 只打印前10个作为示例
print(f“ {i}. {link}“)
# 3. 更精确的提取:提取所有文章标题(假设文章标题在<h2 class=‘post-title‘>里)
article_titles = []
for h2_tag in soup.find_all(‘h2‘, class_=‘post-title‘): # 查找class为‘post-title‘的h2标签
title_text = h2_tag.get_text(strip=True) # 获取标签内文本,并去除首尾空白
article_titles.append(title_text)
print(f“\n文章标题列表: {article_titles}“)
BeautifulSoup查找元素的核心方法 :
-
find(): 返回第一个匹配的元素。 -
find_all(): 返回所有匹配元素的列表。 -
可以通过标签名(
‘div‘)、属性(id=‘main‘)、CSS类(class_=‘content‘,注意class是Python关键字,所以要加下划线)等多种方式组合查找。 -
get_text(): 获取元素内所有文本内容,strip=True参数可以自动清理多余的空格和换行。
注意事项 :网页结构可能会变!这是爬虫脚本最大的维护成本。今天
class=‘post-title‘的标签,明天网站改版可能就变成了class=‘article-heading‘。因此,在编写选择器时,尽量寻找那些稳定、有语义化的ID或CSS类。写好代码后,定期运行测试是必要的。
5. 实战二:使用Selenium实现自动登录
现在进入更具挑战性的部分:让程序自动完成登录。我们以一个虚构的登录页面为例。
5.1 初始化Selenium WebDriver
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 配置Chrome选项(可选,但推荐)
options = webdriver.ChromeOptions()
# 添加常用选项
options.add_argument(‘--disable-blink-features=AutomationControlled‘) # 隐藏自动化特征
options.add_experimental_option(“excludeSwitches”, [“enable-automation“]) # 同上
options.add_experimental_option(‘useAutomationExtension‘, False)
# options.add_argument(‘--headless‘) # 无头模式,不显示浏览器窗口(适合后台运行)
# 初始化WebDriver,指定驱动路径
# 假设chromedriver.exe放在项目根目录下
driver_path = ‘./chromedriver.exe‘ # Windows路径示例,Linux/macOS改为‘./chromedriver‘
driver = webdriver.Chrome(executable_path=driver_path, options=options)
# 设置隐式等待(全局等待元素出现的最大时间)
driver.implicitly_wait(10) # 单位:秒
配置项详解 :
-
--disable-blink-features=AutomationControlled和excludeSwitches:这些选项用于隐藏浏览器正在被自动化工具控制的特征。一些网站(如电商、社交平台)会检测这些特征,如果发现就直接屏蔽。加上这些选项可以提高脚本的隐蔽性。 -
--headless:无头模式。浏览器会在后台运行,不显示图形界面。这在服务器环境或不需要观察过程时非常有用,能节省资源。 调试阶段建议先关闭此选项 ,以便观察脚本执行情况。 -
implicitly_wait:隐式等待。它告诉WebDriver在查找任何元素时,如果元素没有立即出现,就等待一段时间(这里10秒),直到元素出现或超时。这比直接用time.sleep(固定时间)更智能、高效。
5.2 定位元素与执行登录操作
假设我们的登录页面有两个输入框(用户名、密码)和一个登录按钮。
# 打开登录页面
login_url = ‘https://example.com/login‘
driver.get(login_url)
# 使用显式等待,确保关键元素(如用户名输入框)已经加载完成
wait = WebDriverWait(driver, 10) # 创建显式等待对象,最长等10秒
try:
# 1. 定位用户名输入框并输入
# 假设输入框的id是‘username‘
username_input = wait.until(
EC.presence_of_element_located((By.ID, “username“))
)
username_input.clear() # 清空可能存在的默认值
username_input.send_keys(“your_username_here“) # 输入用户名
print(“已输入用户名“)
# 2. 定位密码输入框并输入
# 假设输入框的name是‘password‘
password_input = driver.find_element(By.NAME, “password“)
password_input.send_keys(“your_password_here“)
print(“已输入密码“)
# 3. 定位登录按钮并点击
# 假设按钮的CSS类是‘btn-login‘
login_button = driver.find_element(By.CSS_SELECTOR, “.btn-login“)
login_button.click()
print(“已点击登录按钮“)
# 4. 等待登录成功后的页面跳转或元素出现
# 例如,等待用户头像或“欢迎,XXX”的文字出现
success_element = wait.until(
EC.presence_of_element_located((By.XPATH, “//*[contains(text(), ‘欢迎‘)]“))
)
print(f“登录成功!页面显示: {success_element.text}“)
# 5. (可选)登录后获取Cookies,可供Requests后续使用
cookies = driver.get_cookies()
# 可以将cookies转换为Requests可用的字典格式
cookies_dict = {cookie[‘name‘]: cookie[‘value‘] for cookie in cookies}
print(“已获取登录Cookies:“, cookies_dict)
except Exception as e:
print(f“登录过程出现异常: {e}“)
# 可以在这里截图,方便调试
driver.save_screenshot(‘login_error.png‘)
finally:
# 暂时不关闭浏览器,以便观察
# driver.quit()
pass
元素定位的八种武器(By) : 这是Selenium最核心的技能之一。你必须能根据网页HTML结构,选择最稳定、最独特的定位方式。
-
By.ID:通过元素的id属性定位。id通常是唯一的,是最佳选择。 -
By.NAME:通过元素的name属性定位。常用于表单输入框。 -
By.CLASS_NAME:通过元素的class属性定位。注意一个元素可能有多个class。 -
By.TAG_NAME:通过标签名定位(如‘input‘,‘div‘)。通常不够精确,需要结合其他条件。 -
By.LINK_TEXT/By.PARTIAL_LINK_TEXT:通过链接的完整文本或部分文本来定位超链接。 -
By.CSS_SELECTOR:通过CSS选择器定位。功能强大且灵活,是定位复杂元素的利器。例如:-
#id选择ID -
.class选择类 -
input[name=‘password‘]选择具有特定属性的标签
-
-
By.XPATH:通过XML路径语言定位。功能最强大,但语法也相对复杂。可以遍历DOM树上的任何节点。例如:-
//input[@id=‘username‘]选择id为username的input标签。 -
//*[contains(text(), ‘欢迎‘)]选择文本中包含“欢迎”二字的任何元素。
-
实操心得 :优先使用
ID和NAME,因为它们通常最稳定。如果不行,再考虑CSS_SELECTOR和XPATH。在浏览器的开发者工具(F12)中,你可以直接右键点击元素,选择“Copy -> Copy selector”或“Copy -> Copy XPath”来快速获取定位表达式,但 不要完全依赖它 。自动生成的路径往往又长又脆弱,一旦页面结构微调就会失效。你应该学会自己编写简洁、稳定的选择器。
5.3 处理等待:隐式、显式与强制等待
等待是自动化脚本稳定性的关键。页面加载、元素渲染、AJAX请求都需要时间。
-
隐式等待
(
implicitly_wait):设置一次,全局生效。在查找元素时,如果没找到,WebDriver会轮询查找直到超时。它只对find_element这类查找操作有效。 -
显式等待
(
WebDriverWait+expected_conditions):针对某个特定条件进行等待,条件满足则立即继续,否则超时抛出异常。它更精确、更高效。例如等待元素可点击、等待元素可见、等待URL改变等。 这是推荐的最佳实践 。 -
强制等待
(
time.sleep):让程序无条件暂停固定时间。这是最不推荐的方式,因为它会浪费大量时间(即使页面早已加载好),且无法适应网络或服务器响应速度的变化。仅在调试或处理极端情况时使用。
6. 实战三:自动化填写复杂表单
登录只是交互的一种。更常见的是自动填写调查表、注册表、订单提交等复杂表单。其核心流程与登录类似,但涉及更多类型的表单元素。
6.1 处理各类表单元素
假设我们需要填写一个包含多种输入类型的表单。
# 接续上面的driver实例,假设我们已经导航到了表单页面
form_url = ‘https://example.com/survey‘
driver.get(form_url)
try:
# 1. 文本输入框
name_input = driver.find_element(By.ID, “fullName“)
name_input.send_keys(“张三“)
# 2. 邮箱输入框
email_input = driver.find_element(By.CSS_SELECTOR, “input[type=‘email‘]“)
email_input.send_keys(“zhangsan@example.com“)
# 3. 单选按钮 (Radio Button)
# 假设我们需要选择“性别:男”,其value为‘male‘
male_radio = driver.find_element(By.CSS_SELECTOR, “input[type=‘radio‘][value=‘male‘]“)
# 单选按钮需要先判断是否已选中,再决定是否点击
if not male_radio.is_selected():
male_radio.click()
# 4. 复选框 (Checkbox)
# 假设我们需要勾选“爱好:编程”和“阅读”
hobby_programming = driver.find_element(By.XPATH, “//label[contains(text(), ‘编程‘)]/input“)
hobby_reading = driver.find_element(By.XPATH, “//label[contains(text(), ‘阅读‘)]/input“)
for checkbox in [hobby_programming, hobby_reading]:
if not checkbox.is_selected():
checkbox.click()
# 5. 下拉选择框 (Select)
from selenium.webdriver.support.ui import Select
# 定位到<select>标签
city_select_element = driver.find_element(By.NAME, “city“)
city_select = Select(city_select_element)
# 有三种选择方式:
city_select.select_by_value(“beijing“) # 通过option的value属性选择
# city_select.select_by_index(1) # 通过索引选择(从0开始)
# city_select.select_by_visible_text(“北京市“) # 通过显示的文本选择
# 6. 文件上传
# 假设有一个type=‘file‘的input元素
file_input = driver.find_element(By.ID, “resumeUpload“)
# 直接send_keys文件的绝对路径即可
file_path = r“C:\Users\YourName\Documents\resume.pdf“ # Windows路径示例
file_input.send_keys(file_path)
# 7. 多行文本域 (Textarea)
comment_box = driver.find_element(By.ID, “comments“)
comment_box.send_keys(“这是一个通过Selenium自动填写的测试评论。\n希望一切顺利!“)
print(“表单填写完成!“)
except Exception as e:
print(f“填写表单时出错: {e}“)
driver.save_screenshot(‘form_fill_error.png‘)
6.2 表单提交与验证
填写完毕后,通常需要点击提交按钮,并处理可能的提交结果(成功提示、错误信息等)。
# 定位并点击提交按钮
submit_button = driver.find_element(By.XPATH, “//button[text()=‘提交‘]“)
submit_button.click()
# 等待提交后的反馈
try:
# 等待成功提示信息出现
success_msg = WebDriverWait(driver, 15).until(
EC.visibility_of_element_located((By.CLASS_NAME, “alert-success“))
)
print(f“表单提交成功!提示信息: {success_msg.text}“)
except:
# 如果没有成功提示,检查是否有错误信息
try:
error_msg = driver.find_element(By.CLASS_NAME, “alert-danger“)
print(f“表单提交失败!错误信息: {error_msg.text}“)
except:
print(“未检测到明确的成功或失败提示,请手动检查页面状态。“)
driver.save_screenshot(‘submit_result.png‘)
7. 高级技巧与实战避坑指南
掌握了基础操作后,一些高级技巧和“坑”的应对能让你写出更健壮、更高效的脚本。
7.1 处理iframe(内嵌框架)
有些登录框或表单被包裹在
<iframe>
标签内。你必须先切换到对应的iframe中,才能操作其中的元素。
# 假设登录框在一个id为‘loginIframe‘的iframe里
# 1. 切换到该iframe
login_iframe = driver.find_element(By.ID, “loginIframe“)
driver.switch_to.frame(login_iframe)
# 2. 现在可以定位iframe内的元素了
iframe_username = driver.find_element(By.ID, “iframe-username“)
iframe_username.send_keys(“user_in_iframe“)
# 3. 操作完成后,切换回主文档
driver.switch_to.default_content()
7.2 处理弹窗/Alert
当操作触发JavaScript弹窗(警告框、确认框、提示框)时,需要特别处理。
# 点击一个会触发确认框的按钮
driver.find_element(By.ID, “deleteButton“).click()
# 等待弹窗出现并处理
try:
WebDriverWait(driver, 5).until(EC.alert_is_present())
alert = driver.switch_to.alert
print(f“弹窗文本: {alert.text}“)
alert.accept() # 点击“确定”或“OK”
# alert.dismiss() # 点击“取消”或“Cancel”
except:
print(“未出现预期的弹窗。“)
7.3 使用Cookie维持登录状态
这是混合使用Selenium和Requests的关键。用Selenium登录后,把Cookies交给Requests,让Requests也能以登录状态访问页面。
# Selenium登录后获取cookies
cookies_selenium = driver.get_cookies()
# 转换为Requests可用的字典格式
cookies_for_requests = {}
for cookie in cookies_selenium:
cookies_for_requests[cookie[‘name‘]] = cookie[‘value‘]
# 使用Requests携带cookies访问需要登录的页面
session = requests.Session()
for name, value in cookies_for_requests.items():
session.cookies.set(name, value)
protected_page_url = ‘https://example.com/dashboard‘
resp = session.get(protected_page_url)
# 现在resp.content里就是登录后才能看到的内容了
7.4 反反爬策略与道德规范
自动化脚本可能会触发网站的反爬机制。除了之前提到的隐藏自动化特征,还有以下策略:
-
控制访问频率
:在关键操作(如翻页、提交)后使用
time.sleep(random.uniform(1, 3))加入随机延迟,模拟人类操作。 -
使用代理IP
:如果IP被封锁,可以考虑使用代理池。Selenium可以通过
options.add_argument(‘--proxy-server=http://your-proxy:port‘)来设置。 - 随机化User-Agent :准备一个User-Agent列表,每次请求随机选择一个。
-
处理验证码
:这是最棘手的。对于简单图形验证码,可以尝试OCR库(如
pytesseract),但成功率有限。对于复杂验证码(如点选、滑块),通常需要借助第三方打码平台或手动处理。 在遇到验证码时,程序应暂停并提示人工干预。
最重要的:遵守Robots协议与法律法规
-
在爬取任何网站前,检查其
robots.txt文件(通常在网站根目录,如https://example.com/robots.txt),尊重网站所有者设置的爬虫规则。 - 不要对目标网站造成过大访问压力(DDOS攻击是违法的)。
- 仅爬取公开、非敏感信息,并遵守网站的服务条款。
- 不得将爬取的数据用于非法或商业侵权用途。
8. 完整项目示例:自动登录并抓取个人中心数据
让我们整合以上所有知识,完成一个完整的微型项目:自动登录一个模拟的“用户中心”,并抓取“我的订单”列表。
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait, Select
from selenium.webdriver.support import expected_conditions as EC
import time
import random
def auto_login_and_fetch_orders():
"""自动登录并获取订单数据"""
# 初始化Selenium Driver (使用无头模式,并隐藏自动化特征)
options = webdriver.ChromeOptions()
options.add_argument(‘--disable-blink-features=AutomationControlled‘)
options.add_experimental_option(“excludeSwitches”, [“enable-automation“])
options.add_argument(‘--headless‘) # 无头模式
options.add_argument(‘--no-sandbox‘)
options.add_argument(‘--disable-dev-shm-usage‘)
driver = webdriver.Chrome(executable_path=‘./chromedriver.exe‘, options=options)
driver.implicitly_wait(5)
wait = WebDriverWait(driver, 10)
try:
# 第一步:访问登录页面
print(“[1/4] 正在访问登录页面...“)
driver.get(“https://demo.testfire.net/login.jsp“) # 这是一个用于测试的银行演示网站
# 第二步:定位并填写登录表单
print(“[2/4] 正在填写登录信息...“)
username = wait.until(EC.presence_of_element_located((By.ID, “uid“)))
username.send_keys(“admin“)
password = driver.find_element(By.ID, “passw“)
password.send_keys(“admin“)
# 加入随机延迟,模拟人类输入
time.sleep(random.uniform(0.5, 1.5))
# 第三步:点击登录按钮
login_btn = driver.find_element(By.NAME, “btnSubmit“)
login_btn.click()
print(“[3/4] 已提交登录信息,等待跳转...“)
# 第四步:验证登录成功,并导航到订单页面
# 等待登录后页面出现特定元素,比如欢迎语
wait.until(EC.presence_of_element_located((By.XPATH, “//*[contains(text(), ‘Welcome‘)]“)))
print(“登录成功!“)
# 假设“我的订单”链接文本是‘View Account Summary‘,我们点击它
order_link = driver.find_element(By.LINK_TEXT, “View Account Summary“)
order_link.click()
# 等待订单页面加载
wait.until(EC.presence_of_element_located((By.TAG_NAME, “table“)))
print(“已进入账户概览页面。“)
# 第五步:获取页面源码,并用BeautifulSoup解析订单表格
page_source = driver.page_source
soup = BeautifulSoup(page_source, ‘lxml‘)
# 假设订单信息在一个id为‘transactions‘的表格里
order_table = soup.find(‘table‘, {‘id‘: ‘transactions‘})
orders = []
if order_table:
# 跳过表头,遍历数据行
for row in order_table.find_all(‘tr‘)[1:]: # [1:] 跳过第一行表头
cols = row.find_all(‘td‘)
if len(cols) >= 4: # 假设至少有4列:日期、描述、金额、状态
order_date = cols[0].get_text(strip=True)
description = cols[1].get_text(strip=True)
amount = cols[2].get_text(strip=True)
status = cols[3].get_text(strip=True)
orders.append({
‘date‘: order_date,
‘desc‘: description,
‘amount‘: amount,
‘status‘: status
})
print(f“[4/4] 数据抓取完成!共找到 {len(orders)} 条交易记录。“)
for idx, order in enumerate(orders[:5], 1): # 打印前5条
print(f“ 记录{idx}: {order[‘date‘]} | {order[‘desc‘]} | {order[‘amount‘]} | {order[‘status‘]}“)
# 第六步:(可选)将数据保存到文件
import csv
with open(‘orders.csv‘, ‘w‘, newline=‘’, encoding=‘utf-8-sig‘) as f:
writer = csv.DictWriter(f, fieldnames=[‘date‘, ‘desc‘, ‘amount‘, ‘status‘])
writer.writeheader()
writer.writerows(orders)
print(“数据已保存到 orders.csv 文件。“)
return orders
except Exception as e:
print(f“自动化流程出错: {e}“)
# 出错时截图
driver.save_screenshot(‘error_snapshot.png‘)
return None
finally:
# 确保关闭浏览器,释放资源
driver.quit()
print(“浏览器已关闭。“)
# 运行主函数
if __name__ == “__main__“:
result = auto_login_and_fetch_orders()
这个示例涵盖了从启动浏览器、登录、导航、等待、提取数据到保存文件的完整流程,并加入了错误处理和资源清理。你可以根据实际目标网站的结构,修改其中的URL、元素定位器和数据提取逻辑,就能应用到自己的项目中。
9. 常见问题排查与调试技巧
即使按照教程一步步来,在实际操作中你依然会遇到各种问题。这里记录了我踩过的一些坑和解决方法。
9.1 元素定位失败(NoSuchElementException)
这是最常见的问题,意味着Selenium在当前页面找不到你指定的元素。
可能原因及解决方案:
| 可能原因 | 解决方案 |
|---|---|
| 页面尚未加载完成 |
使用
WebDriverWait
和
expected_conditions
进行显式等待,而不是
find_element
后立即操作。
|
| 元素在iframe内 |
使用
driver.switch_to.frame()
切换到正确的iframe后再定位。
|
| 元素是动态生成的 |
等待元素出现、可见或可点击。使用
EC.presence_of_element_located
、
EC.visibility_of_element_located
或
EC.element_to_be_clickable
。
|
| 定位表达式写错了 |
在浏览器开发者工具的Console中,使用
$$(“你的CSS选择器”)
或
$x(“你的XPath”)
测试表达式是否能选中元素。
|
| 页面有多个匹配元素 |
find_element
只返回第一个。使用
find_elements
获取列表,或编写更精确的选择器。
|
| 页面结构已更改 | 网站改版了。你需要重新分析页面HTML,更新你的定位表达式。 |
调试技巧
:在
find_element
语句前加入
time.sleep(5)
,然后手动观察浏览器页面,看看元素是否真的出现了。或者,在出错后立即截图
driver.save_screenshot(‘debug.png‘)
,查看截图确认页面状态。
9.2 点击或输入无效
有时候代码执行了,但页面没反应。
可能原因及解决方案:
-
元素不可交互
:元素可能被遮挡(如弹窗、遮罩层),或者处于不可见/禁用状态。使用
EC.element_to_be_clickable等待元素可点击,并检查是否有遮挡。 -
需要滚动到元素位置
:有些元素需要滚动到视窗内才能交互。使用
driver.execute_script(“arguments[0].scrollIntoView(true);“, element)将元素滚动到屏幕中央。 -
触发了JavaScript验证
:有些输入框在输入后会有JS验证,可能需要触发
blur事件。可以在输入后发送一个TAB键:element.send_keys(Keys.TAB)。
9.3 被网站检测为自动化脚本
网站返回奇怪的内容,或者直接拒绝访问。
应对策略:
- 使用最新的Selenium和浏览器驱动 。
- 应用完整的“反检测”Chrome选项 (如前文所示)。
-
避免使用
--headless模式 ,有些网站对无头模式检测更严。如果必须用,可以尝试添加更多参数来模拟真实用户:options.add_argument(‘--headless‘) options.add_argument(‘--disable-gpu‘) options.add_argument(‘--window-size=1920,1080‘) # 设置窗口大小 user_agent = ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...‘ options.add_argument(f‘user-agent={user_agent}‘) - 行为模拟 :加入随机延迟、随机鼠标移动轨迹(可通过ActionChains实现)等。
9.4 请求被限制或封禁(针对Requests)
使用Requests抓取时,收到403、429状态码或验证码。
应对策略:
-
设置合理的请求头
:包括
User-Agent,Referer,Accept-Language等。 -
使用Session对象
:
requests.Session()可以自动保持Cookies,模拟一个会话。 -
设置请求间隔
:在循环请求间使用
time.sleep(random.uniform(1, 3))。 - 使用代理IP :当IP被封锁时更换代理。
-
遵守
robots.txt:这是最基本的道德和法律底线。
自动化网页操作是一个需要耐心和细致的工作,每一个网站都可能有其独特的“脾气”。最好的学习方式就是动手去试,遇到问题就利用浏览器的开发者工具去分析,去搜索错误信息,一步步调试。当你成功让脚本替代你完成那些枯燥的重复劳动时,那种成就感会让你觉得所有的折腾都是值得的。这套从“获取”到“交互”的完整技能树,将成为你在数据获取和流程自动化领域非常有力的工具。

1301

被折叠的 条评论
为什么被折叠?



