前期准备
pip install selenium
获取浏览器驱动
我使用的浏览器是Chrome,所以这里只介绍关于Chrome获取浏览器驱动的方法:
需要注意的是:selenium 4.x 对之前版本的部分API调用方式进行了调整,这里就包括关于浏览器获取驱动的方式,最新版本获取驱动的方式如下:
1.在Chrome浏览器的设置页面查看自己浏览的版本
2.在如下页面获取对应的驱动:
人机验证
API如下:
# 获取驱动并打开网页
service = Service(executable_path=r"/Users/liujianlei/Downloads/mopi/chromedriver-mac-arm64/chromedriver")
driver = webdriver.Chrome(service=service)
常用API
selenium常用引入的包如下:
from bs4 import BeautifulSoup
import requests
import openpyxl
from fake_useragent import UserAgent
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
常用api如下:
获取元素
新版本对获取元素的api调用方式,新的api使用方式如下:
# inputTag = driver.find_element_by_id("value") # 利用ID查找
# 改为:
inputTag = driver.find_element(By.ID, "value")
# inputTags = driver.find_element_by_class_name("value") # 利用类名查找
# 改为:
inputTag = driver.find_element(By.CLASS_NAME, "value")
# inputTag = driver.find_element_by_name("value") # 利用name属性查找
# 改为:
inputTag = driver.find_element(By.NAME, "value")
# inputTag = driver.find_element_by_tag_name("value") # 利用标签名查找
# 改为:
inputTag = driver.find_element(By.TAG_NAME, "value")
# inputTag = driver.find_element_by_xpath("value") # 利用xpath查找
# 改为:
inputTag = driver.find_element(By.XPATH, "value")
# inputTag = driver.find_element_by_css_selector("value") # 利用CSS选择器查找
# 改为:
inputTag = driver.find_element(By.CSS_SELECTOR, "value")
控制浏览器操作
- 控制浏览器窗口大小
driver.set_window_size(480, 800)
- 浏览器后退,前进
# 后退 driver.back()
# 前进 driver.forward()
- 刷新
driver.refresh() # 刷新
鼠标操作
- perform(): 执行所有 ActionChains 中存储的行为;
- context_click(): 右击;
- double_click(): 双击;
- drag_and_drop(): 拖动;
- move_to_element(): 鼠标悬停。
- 需要注意的是:鼠标操作完之后,一定要紧跟perform(),不然这
键盘操作
- send_keys(Keys.BACK_SPACE) 删除键(BackSpace)
- send_keys(Keys.SPACE) 空格键(Space)
- send_keys(Keys.TAB) 制表键(Tab)
- send_keys(Keys.ESCAPE) 回退键(Esc)
- send_keys(Keys.ENTER) 回车键(Enter)
- send_keys(Keys.CONTROL,'a') 全选(Ctrl+A)
- send_keys(Keys.CONTROL,'c') 复制(Ctrl+C)
- send_keys(Keys.CONTROL,'x') 剪切(Ctrl+X)
- send_keys(Keys.CONTROL,'v') 粘贴(Ctrl+V)
- send_keys(Keys.F1) 键盘 F1
- ……
- send_keys(Keys.F12) 键盘 F12
获取断言信息
text = driver.page_source # 获取当前页面的源码:需要注意的是:
动态加载:driver.page_source 获取的源码是当前浏览器渲染后的完整页面源码,包括通过 JavaScript 动态加载的内容。而 requests 获取的源码是初始的静态页面源码,不包含 JavaScript 动态加载的内容。
AJAX 请求:如果页面中通过 AJAX 请求加载内容,requests 获取的源码可能不包含这部分内容,而 driver.page_source 获取的源码会包含通过 AJAX 请求加载的内容。
JavaScript 渲染:requests 不能执行 JavaScript,因此无法获取 JavaScript 渲染后的页面内容,而 driver.page_source 获取的源码是经过 JavaScript 渲染后的页面内容。
综上所述,如果网站通过 JavaScript 动态加载内容或进行 AJAX 请求,那么 driver.page_source 获取的源码和 requests 获取的源码可能会有所不同。在实际应用中,您需要根据具体的需求和情况选择合适的获取源码的方法。
由于以上等原因,最终使用requests获取的网页源码可能和selenium获取的源码有所缺失,一般我们直接使用selenium获取网页的源码
title = driver.title # 打印当前页面title
now_url = driver.current_url # 打印当前页面URL
等待页面加载完成
- 显示等待
显式等待使WebdDriver等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException)。
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.Firefox()
driver.get("http://www.baidu.com")
element = WebDriverWait(driver, 5, 0.5).until(
EC.presence_of_element_located((By.ID, "kw"))
)
element.send_keys('selenium')
driver.quit()
WebDriverWait类是由WebDirver 提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。具体格式如下:
WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
- driver :浏览器驱动。
- timeout :最长超时时间,默认以秒为单位。
- poll_frequency :检测的间隔(步长)时间,默认为0.5S。
- ignored_exceptions :超时后的异常信息,默认情况下抛NoSuchElementException异常。
- WebDriverWait()一般由until()或until_not()方法配合使用,下面是until()和until_not()方法的说明。
- until(method, message=‘’) 调用该方法提供的驱动程序作为一个参数,直到返回值为True。
- until_not(method, message=‘’) 调用该方法提供的驱动程序作为一个参数,直到返回值为False。
在本例中,通过as关键字将expected_conditions 重命名为EC,并调用presence_of_element_located()方法判断元素是否存在。
- EC对应的不同方法如下:
- presence_of_element_located:等待直到某个元素出现在DOM中。
- visibility_of_element_located:等待直到某个元素在页面上可见。
- element_to_be_clickable:等待直到某个元素可被点击。
- text_to_be_present_in_element:等待直到某个元素包含特定的文本。
- 隐式等待
隐式等待:通过设置隐式等待时间,可以让WebDriver在查找元素或执行操作时等待一定的时间。如果在规定的时间内找到了元素,就会立即执行后续的操作;如果超过了设定的时间而没有找到元素,就会抛出NoSuchElementException异常。隐式等待对整个WebDriver的生命周期都起作用,只需要设置一次即可。
driver.implicitly_wait(10)
切换模块
切换窗口
handles = driver.window_handles
print("handles的类型为:",type(handles))
print("handles = ",handles)
print("title的值为:",title)
# 切换具柄
driver.switch_to.window(handles[1])
切换实例:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 获取驱动并打开网页
service = Service(executable_path=r"/Users/liujianlei/Downloads/mopi/chromedriver-mac-arm64/chromedriver")
driver = webdriver.Chrome(service=service)
# 打开网页
url = "https://login.anjuke.com/login/form"
driver.get(url)
# 等待元素加载出来
wait = WebDriverWait(driver,10,0.5).until(EC.presence_of_element_located((By.XPATH,"//*[@id=\"iframeLoginIfm\"]")))
# 查找对应的iframe元素
iframe = driver.find_element(By.XPATH,"//*[@id=\"iframeLoginIfm\"]")
# 切换为iframe
driver.switch_to.frame(iframe)
# 点击切换为账号密码登录
element = driver.find_element(By.XPATH,"//*[@id=\"pwdTab\"]")
# 点击元素
element.click()
# 输入用户名
user_element = driver.find_element(By.ID,"pwdUserNameIpt")
user_element.send_keys("123")
# 输入密码
pwd_element = driver.find_element(By.ID,"pwdIpt")
pwd_element.send_keys("123")
# 勾选我已同意
element_agree = driver.find_element(By.ID,"checkagree")
element_agree.click()
# 点击登录
login_element =driver.find_element(By.ID,"pwdSubmitBtn")
login_element.click()
# 切换为上一层的图层
driver.switch_to.parent_frame()
# 点击家居网进行页面跳转,查看当前是哪个窗口
element_web = driver.find_element(By.XPATH,"/html/body/div[1]/a[1]/i")
element_web.click()
# 获取当前的window
window_name = driver.title
print("{}为当前的title".format(window_name))
time.sleep(100)
# element = driver.find_element(By.XPATH,"//*[@id=\"phoneIpt\"]")
# element.send_keys("13153382278")
# 切换新的元素
切换iframe
# 切换为iframe
driver.switch_to.frame(iframe)
# 点击切换为账号密码登录
element = driver.find_element(By.XPATH,"//*[@id=\"pwdTab\"]")
# 点击元素
element.click()
# 输入用户名
user_element = driver.find_element(By.ID,"pwdUserNameIpt")
user_element.send_keys("123")
# 输入密码
pwd_element = driver.find_element(By.ID,"pwdIpt")
pwd_element.send_keys("123")
# 勾选我已同意
element_agree = driver.find_element(By.ID,"checkagree")
element_agree.click()
# 点击登录
login_element =driver.find_element(By.ID,"pwdSubmitBtn")
login_element.click()
# 切换为上一层的图层
driver.switch_to.parent_frame()
切换实例:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 练习:访问聚合,点击“开发者”
url = "https://www.juhe.cn/"
service = Service(executable_path=r"/Users/liujianlei/Downloads/mopi/chromedriver-mac-arm64/chromedriver")
driver = webdriver.Chrome(service= service)
driver.get(url)
driver.find_element(By.LINK_TEXT,"开发者").click()
# 查看是哪个窗口
title = driver.title
# 获取所有的窗口具柄
handles = driver.window_handles
print("handles的类型为:",type(handles))
print("handles = ",handles)
print("title的值为:",title)
# 切换具柄
driver.switch_to.window(handles[1])
# 点击新页面的元素
element = driver.find_element(By.XPATH,"//*[@id=\"__layout\"]/div/div[1]/div[1]/div/div/div[1]/div/a[2]").click()
# 切换回原来的具柄
driver.switch_to.window(handles[0])
#点击新的元素
driver.find_element(By.XPATH,"/html/body/div[3]/header/div/div[1]/ul/li[2]/a").click()
time.sleep(200)
警告框处理
alert = driver.switch_to_alert()
- text:返回 alert/confirm/prompt 中的文字信息。
- accept():接受现有警告框。
- dismiss():解散现有警告框。
- send_keys(keysToSend):发送文本至警告框。keysToSend:将文本发送至警告框。
下拉框选择
from selenium import webdriver
from selenium.webdriver.support.select import Select
from time import sleep
driver = webdriver.Chrome()
driver.implicitly_wait(10)
driver.get('http://www.baidu.com')
sel = driver.find_element_by_xpath("//select[@id='nr']")
Select(sel).select_by_value('50') # 显示50条
文件上传
driver.find_element_by_name("file").send_keys('D:\\upload_file.txt') # # 定位上传按钮,添加本地文件
cookie操作
WebDriver操作cookie的方法:
- get_cookies(): 获得所有cookie信息。
- get_cookie(name): 返回字典的key为“name”的cookie信息。
- add_cookie(cookie_dict) : 添加cookie。“cookie_dict”指字典对象,必须有name 和value 值。
- delete_cookie(name,optionsString):删除cookie信息。“name”是要删除的cookie的名称,“optionsString”是该cookie的选项,目前支持的选项包括“路径”,“域”。
- delete_all_cookies(): 删除所有cookie信息
调用JavaScript代码
js="window.scrollTo(100,450);"
driver.execute_script(js) # 通过javascript设置浏览器窗口的滚动条位置
通过execute_script()方法执行JavaScripts代码来移动滚动条的位置。
窗口截图
driver.get_screenshot_as_file("D:\\baidu_img.jpg") # 截取当前窗口,并指定截图图片的保存位置
关闭浏览器
- close() 关闭单个窗口
- quit() 关闭所有窗口
练习
爬取关于cxk的b站视频数据
from bs4 import BeautifulSoup
import requests
import openpyxl
from fake_useragent import UserAgent
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
# 创建excel表格
wb = openpyxl.Workbook()
ws = wb.active
ws.append(["名称","视频地址","观看次数","弹幕数量","发布时间"])
output_file = "./cxk-with-basketball-video.xlsx"
# 创建虚拟ua
ua = UserAgent()
headers = {"user-agent":ua.random}
# 打开b站官网
url = "https://www.bilibili.com/"
# 创建service
service = Service(executable_path=r"/Users/liujianlei/Downloads/mopi/chromedriver-mac-arm64/chromedriver")
driver = webdriver.Chrome(service= service)
# 打开b站
driver.get(url)
# 定位输入框并输入内容
content = "蔡徐坤 篮球"
driver.find_element(By.CLASS_NAME,"nav-search-input").send_keys(content)
# 获取按钮
driver.find_element(By.CLASS_NAME,"nav-search-btn").click()
# 进行页面的跳转
time.sleep(1)
handles = driver.window_handles
driver.switch_to.window(handles[-1])
# 显示等待直到元素加载完成
#wait = WebDriverWait(driver,10,0.5).until(EC.presence_of_element_located((By.XPATH,"//*[@id=\"bili-header-container\"]/div/div/ul[1]/li[8]/a/span")))
# 获取对应的URL并进行访问
# new_url = driver.current_url
# response = requests.get(new_url,headers=headers)
text = driver.page_source
soup = BeautifulSoup(text,"html.parser")
# 获取当前共有多少页
buttons = soup.find_all("button",{"class":"vui_button vui_button--no-transition vui_pagenation--btn vui_pagenation--btn-num"})
last_index = int(buttons[-1].text)
# 使用bs进行内容的读取
# for i in range(1,34):
# print("当前是第{}页".format(i))
tags = soup.find_all(class_="bili-video-card__wrap __scale-wrap")
for tag in tags:
contents = tag.find_all("span", {"class": "bili-video-card__stats--item"})
view_num = contents[0].text
lang_num = contents[1].text
title = tag.find("h3", {"class": "bili-video-card__info--tit"}).get("title")
time = tag.find("span", {"class": "bili-video-card__info--date"}).text
href = tag.find("a", attrs={"data-idx": "all"}).get("href")
# 将结果进行保存
ws.append([title, href, view_num, lang_num, time])
# 浏览器页面定位到下面
# driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
# print("已经滑动到最底部了")
driver.implicitly_wait(10)
#WebDriverWait(driver,20,0.5).until(EC.element_to_be_clickable((By.CLASS_NAME,"vui_button vui_pagenation--btn vui_pagenation--btn-side")))
# 定位下一页元素并进行点击
# 最后将excel表格进行保存
driver.find_element(By.CSS_SELECTOR,
"#i_cecream > div > div:nth-child(2) > div.search-content--gray.search-content > div > div > div > div.flex_center.mt_x50.mb_x50 > div > div > button:nth-child(11)").click()
wb.save(output_file)
# 关闭driver
driver.close()
notes:
元素定位不到的可能原因:
如果在使用Selenium时无法定位到下一页按钮,可能有几种原因。以下是一些可能的解决方法:
- 等待页面加载完成:确保页面已经完全加载,有时下一页按钮可能需要一些时间才会出现在DOM中。您可以使用隐式等待或显式等待确保页面加载完成后再进行定位。
- 使用合适的定位方法:尝试使用不同的定位方法来寻找下一页按钮,比如通过ID、CSS选择器、XPath等。
- 查看页面源代码:查看页面源代码,确认下一页按钮的HTML结构和属性,以便更好地定位它。
- 检查是否在iframe中:如果下一页按钮位于iframe中,您需要先切换到该iframe,然后再进行定位。
- 使用JavaScript点击:如果其他方法无法定位到下一页按钮,可以尝试使用JavaScript来模拟点击操作。
以下是一个示例,演示了如何使用Selenium和JavaScript模拟点击下一页按钮:
# 导入需要的模块 from selenium import webdriver # 启动浏览器 driver = webdriver.Chrome() # 打开网页 driver.get("https://www.example.com") # 使用JavaScript点击下一页按钮 next_button = driver.find_element_by_xpath("//button[@id='next']") driver.execute_script("arguments[0].click();", next_button)
如果这些方法仍然不能解决问题,那么可能需要对具体的网页和定位情况进行更深入的分析和调试。
版权归原作者 六子干侧开 所有, 如有侵权,请联系我们删除。