- 模拟登录与数据采集
今天我们讨论了如何通过 Python 模拟登录并抓取登录后的数据,主要涵盖了以下内容:
模拟登录步骤:
分析登录页面:使用浏览器开发者工具(F12)分析登录表单,提取表单字段、提交URL和其他必要的参数(如 CSRF Token)。
发送请求:使用 requests.Session() 保持会话状态,发送登录请求,并提交用户名、密码、CSRF Token 等表单数据。
检查登录是否成功:通过判断页面源代码(driver.page_source)是否包含特定字符串(如 “Welcome”)来判断是否登录成功。
遇到的主要问题:
动态网页:如果目标网页是动态页面,使用 requests 模拟登录可能会失败,因为它不能执行 JavaScript,导致无法正确渲染页面内容。
解决方案:
使用 Selenium 或 Playwright 来模拟浏览器操作,执行 JavaScript,获取动态加载的内容。
可以通过 driver.page_source 获取页面完整源代码,或者通过 driver.current_url 检查是否成功跳转到登录后的页面。
使用 find_element 方法检查页面中是否包含特定元素(如用户名、账户信息、退出按钮等)来判断登录是否成功。
2. 如何判断是否登录成功
在没有明确的“登录成功”信息(如“Welcome”)时,我们可以使用以下方法判断登录是否成功:
通过页面URL变化判断:检查是否跳转到预期的登录后页面。
通过页面内容判断:查找登录后才会显示的元素,例如用户的姓名、账户信息、退出按钮等。
错误信息提示:检查登录失败时的错误提示(如“用户名或密码错误”)来确认登录状态。
3. 代码示例与技巧
使用 driver.page_source 获取页面源代码,判断是否包含特定的成功标识。
使用 find_element() 方法查找页面元素,判断是否包含登录后的元素(如用户信息、退出按钮等)。
结合 try-except 结构处理找不到元素时的异常。
import requests
import time
from bs4 import BeautifulSoup
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options# By 类是 Selenium 中用于定位网页元素的枚举类
# 它提供了多种定位元素的方法,比如:
# By.ID - 通过元素的 id 属性定位
# By.CLASS_NAME - 通过类名定位
# By.NAME - 通过 name 属性定位
# By.TAG_NAME - 通过标签名定位
# By.XPATH - 通过 XPath 定位
# By.CSS_SELECTOR - 通过 CSS 选择器定位
# from selenium.webdriver.common.keys import Keys
# Keys 类模拟键盘按键操作
# 常用的键盘操作包括:
# Keys.RETURN 或 Keys.ENTER - 回车键
# Keys.TAB - Tab 键
# Keys.SPACE - 空格键
# Keys.ESCAPE - ESC 键
# Keys.CONTROL - Ctrl 键
# Keys.ALT - Alt 键# `expected_conditions` (通常缩写为EC) 是 Selenium WebDriver 中的一个非常重要的模块,它提供了一系列预定义的条件,用于等待特定的事件发生。这是 Selenium 中实现显式等待(Explicit Wait)的关键组件。# 主要用途:
# 1. 等待元素可见
# 2. 等待元素可点击
# 3. 等待元素存在
# 4. 等待元素消失
# 等等...# 在你的代码中,你使用了 `EC.presence_of_element_located()` 来等待元素出现:# ```python:scray/模拟登录.py
# wait = WebDriverWait(driver, 20)
# username_field = wait.until(EC.presence_of_element_located((By.ID, "LoginForm:email")))
# ```# 一些常用的 EC 条件:
# ```python
# # 等待元素可见
# EC.visibility_of_element_located()# # 等待元素可点击
# EC.element_to_be_clickable()# # 等待元素存在于DOM中
# EC.presence_of_element_located()# # 等待元素包含特定文本
# EC.text_to_be_present_in_element()# # 等待标题包含特定文本
# EC.title_contains()
# ```# 这比使用 `time.sleep()` 更智能,因为它会在条件满足时立即继续执行,而不是固定等待一段时间,既提高了脚本的效率,又增加了其稳定性。# #使用 使用 requests 模拟登录# # 登录页面的URL
# login_url = 'https://lumen.ni.com/nicif/us/header_login/content.xhtml?action=login&du=https%3A%2F%2Fwww.ni.com%2Fen%2Fshop%2Flabview.html'# # 创建一个会话对象,以便保持登录状态
# session = requests.Session()# # 获取登录页面,提取 CSRF Token
# response = session.get(login_url)# # 使用 BeautifulSoup 解析页面内容,提取 CSRF Token
# soup = BeautifulSoup(response.text, 'html.parser')
# #csrf_token = soup.find('input', {'name': 'csrf_token'})['value']# # 登录请求的表单数据
# login_data = {
# 'username': '2283483060@qq.com',
# 'password': '!726:vr,Hq:rVj7',
# #'csrf_token': csrf_token
# }# # 登录请求头,模拟浏览器
# headers = {
# 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
# }# # 发送POST请求进行登录
# login_response = session.post(login_url, data=login_data, headers=headers)# # 检查是否登录成功,通常可以通过页面中的一些标识或返回状态来判断
# if login_response.status_code == 200 and 'Welcome' in login_response.text:
# print('登录成功!')
# else:
# print('登录失败!')# # 登录成功后,可以继续抓取登录后的页面
# profile_url = 'https://www.ni.com/en/shop/labview.html'
# profile_page = session.get(profile_url)# # 打印个人主页的内容
# print(profile_page.text)#动态页面登录# 假设登录前的URL是login_page_url,登录后的URL是profile_page_url
login_page_url = "https://lumen.ni.com/nicif/us/header_login/content.xhtml?action=login&du=https%3A%2F%2Fwww.ni.com%2Fen.html"
profile_page_url = "https://www.ni.com/en.html"# 初始化 Chrome 浏览器
service = Service(ChromeDriverManager().install())
chrome_options = Options()
chrome_options.add_argument('--ignore-ssl-errors=yes')
chrome_options.add_argument('--ignore-certificate-errors')# 在创建 driver 时使用这些选项
driver = webdriver.Chrome(service=service, options=chrome_options)# 打开登录页面
driver.get("https://lumen.ni.com/nicif/us/header_login/content.xhtml?action=login&du=https%3A%2F%2Fwww.ni.com%2Fen%2Fshop%2Flabview.html")#time.sleep(10)
wait = WebDriverWait(driver, 20)#添加了显式等待机制,替代了简单的 time.sleep()
# 找到表单字段并填写数据
username_field = wait.until(EC.presence_of_element_located((By.ID, "LoginForm:email")))
password_field = wait.until(EC.presence_of_element_located((By.ID, "LoginForm:password")))username_field.send_keys("xxxxxx@qq.com")#
password_field.send_keys("xxxx")# 提交表单
# 用户填完用户名和密码后直接按回车键登录
password_field.send_keys(Keys.RETURN)# 等待页面加载完成
driver.implicitly_wait(15)# 模拟登录操作后,检查当前URL是否是登录后的页面
if driver.current_url == profile_page_url:print("登录成功!")
else:print("登录失败!")# 登录前后IP有可能发生变化#检查logout元素是否存在
try:# 使用 CSS 选择器查找登出链接logout_link = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, ".analytics-headerlogout-link.logout-link")))print("登录成功!")# 获取当前用户名或其他登录后才能看到的元素# user_profile = driver.find_element(By.CSS_SELECTOR, ".user-profile-name")# print(f"当前登录用户: {user_profile.text}")except Exception as e:print(f"登录失败!错误信息: {str(e)}")# 获取登录后的页面内容
profile_page = driver.page_source
#print(profile_page)# 关闭浏览器
driver.quit()
# 让我解释一下 implicitly_wait() 和 time.sleep() 的主要区别:
# time.sleep()
# 是一个固定的、硬性的等待时间
# 无论元素是否已经加载完成,都会等待指定的完整时间
# 会降低脚本执行效率,因为可能会等待不必要的时间
# implicitly_wait()
# 是一个动态的、智能的等待机制
# 设置了一个最大等待时间
# 在这个时间内,如果找到了要查找的元素,会立即继续执行
# 如果超过设定时间还没找到元素,才会抛出异常
# 3. 建议的最佳实践
# 使用显式等待(Explicit Wait)是更好的选择,因为它更精确和可控:
# 显式等待允许你:
# 指定具体要等待的条件(元素可见、可点击等)
# 只对特定操作设置等待时间
# 提供更好的错误处理机制
# 所以,建议将你代码中的 implicitly_wait() 替换为更具体的显式等待,这样可以使自动化测试更加稳定和可靠。