Python网页自动化实战:Requests+Selenium实现数据抓取与表单交互

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 的关键一步。

  1. 查看你的Chrome浏览器版本 :打开Chrome,点击右上角三个点 -> 帮助 -> 关于Google Chrome。记下版本号(例如, 128.0.6613.138 )。
  2. 下载对应版本的ChromeDriver
    • 访问 ChromeDriver官方下载站 或国内镜像站。
    • 下载与你的Chrome主版本号完全一致的驱动(例如,Chrome是128.x,就下载128.x.x.x版本的驱动)。
  3. 放置驱动并配置路径
    • 方法一(推荐,便于管理) :将下载的 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结构,选择最稳定、最独特的定位方式。

  1. By.ID :通过元素的 id 属性定位。 id 通常是唯一的,是最佳选择。
  2. By.NAME :通过元素的 name 属性定位。常用于表单输入框。
  3. By.CLASS_NAME :通过元素的 class 属性定位。注意一个元素可能有多个class。
  4. By.TAG_NAME :通过标签名定位(如 ‘input‘ , ‘div‘ )。通常不够精确,需要结合其他条件。
  5. By.LINK_TEXT / By.PARTIAL_LINK_TEXT :通过链接的完整文本或部分文本来定位超链接。
  6. By.CSS_SELECTOR :通过CSS选择器定位。功能强大且灵活,是定位复杂元素的利器。例如:
    • #id 选择ID
    • .class 选择类
    • input[name=‘password‘] 选择具有特定属性的标签
  7. 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 反反爬策略与道德规范

自动化脚本可能会触发网站的反爬机制。除了之前提到的隐藏自动化特征,还有以下策略:

  1. 控制访问频率 :在关键操作(如翻页、提交)后使用 time.sleep(random.uniform(1, 3)) 加入随机延迟,模拟人类操作。
  2. 使用代理IP :如果IP被封锁,可以考虑使用代理池。Selenium可以通过 options.add_argument(‘--proxy-server=http://your-proxy:port‘) 来设置。
  3. 随机化User-Agent :准备一个User-Agent列表,每次请求随机选择一个。
  4. 处理验证码 :这是最棘手的。对于简单图形验证码,可以尝试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 被网站检测为自动化脚本

网站返回奇怪的内容,或者直接拒绝访问。

应对策略:

  1. 使用最新的Selenium和浏览器驱动
  2. 应用完整的“反检测”Chrome选项 (如前文所示)。
  3. 避免使用 --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}‘)
    
  4. 行为模拟 :加入随机延迟、随机鼠标移动轨迹(可通过ActionChains实现)等。

9.4 请求被限制或封禁(针对Requests)

使用Requests抓取时,收到403、429状态码或验证码。

应对策略:

  1. 设置合理的请求头 :包括 User-Agent , Referer , Accept-Language 等。
  2. 使用Session对象 requests.Session() 可以自动保持Cookies,模拟一个会话。
  3. 设置请求间隔 :在循环请求间使用 time.sleep(random.uniform(1, 3))
  4. 使用代理IP :当IP被封锁时更换代理。
  5. 遵守 robots.txt :这是最基本的道德和法律底线。

自动化网页操作是一个需要耐心和细致的工作,每一个网站都可能有其独特的“脾气”。最好的学习方式就是动手去试,遇到问题就利用浏览器的开发者工具去分析,去搜索错误信息,一步步调试。当你成功让脚本替代你完成那些枯燥的重复劳动时,那种成就感会让你觉得所有的折腾都是值得的。这套从“获取”到“交互”的完整技能树,将成为你在数据获取和流程自动化领域非常有力的工具。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值