赞
踩
声明以下的例子,只是来作为测试学习,并不作为真正的爬虫
官网中文文档:https://www.selenium.dev/zh-cn/documentation/
其访问比较慢,最好是用 VPN
Selenium 是一个自动化测试工具,历史悠久,功能强大,技术成熟。其优点是能直接在浏览器上操作,利用它可以像人一样完成,输入文本框内容,点击,下拉等操作,它不但能做自动化测试,在爬虫领域也是一把利器,能解决大部分网页反爬问题,Selenium可以根据驱动的代码指令来获取网页内容,甚至是验证码的截屏,或判断网站上的某些动作是否发生。我们这边主要是围绕着爬虫展开。
1、Selenium 运行比较慢,它需要等待浏览器的元素加载完毕,所以耗时。
2、驱动的适配,浏览器版本不同,浏览器类型不同,得使用不同的驱动器。
3、像一些安全性比较高,比较大型的网站是能检测出是否使用了Selenium来爬取网站
我本机是 mac os,浏览器使用 chrome来进行测试
pip install selenium
这里是测试演示用,安装完成后是最新的版本, selenium4,如果是正式使用建议还是使用稳定版本
selenium增加了一些新特性,也弃用了一些方法,如下简单的列举几个变化:
find_element_by
系列方法;我在这里测试的,也是以4为主,具体看这篇文章
Selenium库中有个叫WebDriver的 API。WebDriver有点像加载网站的浏览器,但它可以像BeautifulSoup使用 xpath,css选择器来查找页面元素,与页面上的元素进行交互(发送文本,点击等),以及执行其他动作比如调用 JS来下拉页面。
注意:官网需要VPN 翻墙
Firefox浏览器驱动下载地址:
https://github.com/mozilla/geckodriver/releases/
Edge浏览器驱动下载地址:
这是windows下的浏览器
https://developer.microsoft.com/en-us/microsoftedge/tools/webdriver/#downloads
无头浏览器
PhantomJs安装教程(无界面浏览器,它会把网站加载到内存并执行页面上的 JavaScript,因为不会展示图形界面,所以运行起来比完整的浏览器要高效),它一般是用在像linux线上服务器,不过现在 chrome(v65以上) 也支持无头浏览器了。
注意: 下载安装步骤如下:需要下对应浏览器版本的驱动
查看 chrome的版本是最新版本123,所以下载对应的驱动
下载对应的版本:
macos 分为 intel 还是arm 的,我因电脑是 intel,所以选择时也要选mac64
浏览器对应的驱动版本
将驱动文件解压,放在环境变量的目录/usr/local/bin 就行
mv Downloads/chromedriver-mac-x64/chromedriver /usr/local/bin/
之后的代码都是使用了测试用例,具体代码看码云上的源码,这里先做一个前置操作,设置chrome 驱动的路径,默认在代码中不设置,程序会自动从环境变量中去寻找,但是 selenium启动时会超慢
def setUp(self) -> None:
"""
@todo: 设置驱动的路径,主要是为了加快selenium的启动速度,不用再去搜索驱动的路径
@return:
"""
self.service = ChromService(executable_path="/usr/local/bin/chromedriver")
pass
def test_chrome_driver(self): """ @todo: selenium测试chrome的浏览器 @return: """ # 设置驱动的路径,这是 selenium4之后的新写法 # 实例化浏览器对象 browser = webdriver.Chrome(service=self.service) # 发送请求访问百度 browser.get('https://www.baidu.com') # 获取页面标题 print("当前页面标题:", browser.title) time.sleep(10) # 退出浏览器 browser.quit() pass
其流程是:
①、开启了一个 新的chrome 窗口,并打开 baidu 网站
②、输出 title 标题
③、关闭浏览器,注意,在 selenium4之前最后一定要手动退出,不然会有残留进程,之后是不用写,自动关闭
源码地址:https://gitee.com/allen-huang/python/blob/master/crawler/do-selenium/test_base.py
current_url
:当前响应的URLtitle
:当前网页的标题page_source
:页面源码name
:浏览器驱动名称
get()
:打开一个网址,同时会把 response 信息自动保存back()
:返回上一个网址forward()
:前进到一个网址refresh()
:再刷新一个网址save_screenshot()
:保存当前页面的截屏快照close()
:关闭当前标签页,不关闭浏览器quit()
:关闭浏览器,释放进程
cloase()
和quit()
的区别:代码展示:
def test_attr_method(self): """ @todo: 测试浏览器的属性和方法 @return: """ # 实例化浏览器对象 driver = webdriver.Chrome(service=self.service) # 获取浏览器的名称 print(driver.name) # 访问百度首页,这里先暂时不使用https driver.get('http://www.baidu.com') # 属性1:打印当前响应对应的URL,之前 http的转换成了 https print(driver.current_url) # 属性2:打印当前标签页的标题 print(driver.title) # 属性3:打印当前网页的源码长度 print(len(driver.page_source)) # 休息2秒,跳转到豆瓣首页,这里的两秒是等当前页面加载完毕,也就是浏览器转完圈后等待的两秒 time.sleep(2) driver.get('https://www.douban.com') # 休息2秒,再返回百度 time.sleep(2) driver.back() # 休息2秒,再前进到豆瓣 time.sleep(2) driver.forward() # 休息2秒,再刷新页面 time.sleep(2) driver.refresh() # 保存当前页面的截屏快照 driver.save_screenshot("./screenshot.png") # 关闭当前标签页 driver.close() # 关闭浏览器,释放进程 driver.quit() pass
只罗列常用的几个,其他的可以查下文档就行,像 xpath 和 css 选择器定位是属于万能定位,尽量用这两个
语法格式:
find_element(by=By.ID, value='标签的id属性名)
测试代码
def test_element_id(self): """ @todo: 通过id定位元素,测试输入框的输入和点击 @return: """ # 实例化浏览器对象 browser = webdriver.Chrome(service=self.service) # 打开百度首页 browser.get('https://www.baidu.com') # 输入关键字内容sora,并赋值给 标签的id属性名来定位;这是新的写法, browser.find_element(by=By.ID, value='kw').send_keys('sora') # 点击搜索按钮 browser.find_element(by=By.ID, value='su').click() # 获取搜索结果 # 关闭浏览器 time.sleep(5) browser.quit() pass
语法格式:
find_element(by=By.CLASS_NAME, value='标签的class属性名')
测试代码:
def test_element_class_name(self):
"""
@todo: 通过class_name定位元素,测试输入框的输入和点击
@return:
"""
browser = webdriver.Chrome(service=self.service)
browser.get('https://www.baidu.com')
# 使用class_name定位输入框,并把输入框的内容设置为sora
browser.find_element(by=By.CLASS_NAME, value='s_ipt').send_keys('sora')
browser.find_element(by=By.CLASS_NAME, value='s_btn').click()
time.sleep(2)
browser.quit()
pass
find_element(by=By.XPATH, value='xpath的表达式')
def test_element_xpath(self): """ @todo: 通过xpath定位元素,测试输入框的输入和点击 @return: """ # 实例化浏览器对象 browser = webdriver.Chrome(service=self.service) browser.get('https://www.baidu.com') # 使用xpath定位输入框,并把输入框的内容设置为sora browser.find_element(by=By.XPATH, value='//*[@id="kw"]').send_keys('sora') # 点击搜索按钮 browser.find_element(by=By.XPATH, value='//*[@id="su"]').click() # 关闭浏览器 time.sleep(5) browser.quit() pass
browser.find_element(by=By.CSS_SELECTOR, value="选择器表达式")
def test_element_css_selector(self): """ @todo: 通过css选择器定位元素,测试输入框的输入和点击 @return: """ # 实例化浏览器对象,打开百度首页 browser = webdriver.Chrome(service=self.service) browser.get('https://www.baidu.com') # 使用css选择器定位输入框,先进行清空文本框,并把输入框的内容设置为sora browser.find_element(by=By.CSS_SELECTOR, value="#kw").clear() browser.find_element(by=By.CSS_SELECTOR, value="#kw").send_keys('sora') browser.find_element(by=By.CSS_SELECTOR, value="#su").click() # 关闭浏览器 time.sleep(2) browser.quit() pass
天天基金网为例子,如下图:
语法格式:
格式上和单个获取的差不多,唯一有差别的是方法后面多了一个s
,使用了find_elements
测试代码:
def test_elements_xpath(self):
"""
@todo: 通过xpath定位元素列表,测试获取列表的数据
@return:
"""
browser = webdriver.Chrome(service=self.service)
browser.get('https://fund.eastmoney.com/data/fundranking.html#tall')
# 使用xpath定位列表
elements = browser.find_elements(by=By.XPATH, value='//table[@id="dbtable"]/tbody/tr/td[3]/a')
for elem in elements:
print(elem.text)
# 页面加载完后,再等待2秒,自动关闭浏览器,这里没有使用quit()方法,也能自动释放
time.sleep(2)
browser.quit()
pass
打印结果:
定位的是元素的对象,定位不到报错
定位的是列表,列表里面存元素对象,如果定位不到则是空的数据
它这个和 xpath 获取文档属性值不太一样,这是需要注意的
元素对象
.text元素对象
.get_attribute(‘属性名’)def test_op_element(self): """ 测试获取元素的文本内容 @return: """ # 实例化浏览器对象 browser = webdriver.Chrome(service=self.service) # 天天基金网,查看所有基金,这只是测试,不作为正式的爬虫 browser.get('https://fund.eastmoney.com/data/fundranking.html#tall') # 使用xpath定位元素列表,基金代码 elems = browser.find_elements(by=By.XPATH, value='//table[@id="dbtable"]/tbody/tr/td[3]/a') for elem in elems: # 代码的内容 print(elem.text) # 获取基金的链接 print(elem.get_attribute('href')) time.sleep(2) browser.quit() pass
send_keys()在使用时,一定要文本框,不然是不生效的,它就是来输入内容用
def test_element_css_selector(self): """ @todo: 通过css选择器定位元素,测试输入框的输入和点击 @return: """ # 实例化浏览器对象,打开百度首页 browser = webdriver.Chrome(service=self.service) browser.get('https://www.baidu.com') # 使用css选择器定位输入框,先进行清空文本框,并把输入框的内容设置为sora browser.find_element(by=By.CSS_SELECTOR, value="#kw").clear() browser.find_element(by=By.CSS_SELECTOR, value="#kw").send_keys('sora') browser.find_element(by=By.CSS_SELECTOR, value="#su").click() # 关闭浏览器 time.sleep(2) browser.quit() pass
- 1、
webdriver.ChromeOptions()
,实例化 chrome 浏览器配置对象- 2、
chrome_options.add_argument('--headless')
,开启无头模式- 3、
chrome_options.add_argument('--disable-gpu')
,不开启显卡- 4、
browser = webdriver.Chrome(options=chrome_options)
,实例化浏览器
如下代码展示
def test_headless_driver(self): """ @todo: 测试chrome无头模式浏览器 @return: """ # 1.实例化配置对象 chrome_options = webdriver.ChromeOptions() # 2.配置对象开启无头模式 chrome_options.add_argument('--headless') # 3.配置对象添加无显卡模式,即无图形界面 chrome_options.add_argument('--disable-gpu') # 4.实例化浏览器对象 browser = webdriver.Chrome(service=self.service, options=chrome_options) browser.get('https://www.baidu.com') # 查看当前页面url print(browser.current_url) # 获取页面标题 print("页面标题:", browser.title) # 获取渲染后的页面源码 print("页面源码-长度:", len(browser.page_source)) # 获取页面cookie print("cookie-data", browser.get_cookies()) # 关闭页面的标签页 browser.close() time.sleep(5) # 关闭浏览器 browser.quit() pass
在 selenium4开始,phantomJS已经废弃了,建议使用 chrome 和 firefox 的无头浏览器其,这是只是简单的介绍下。
def test_phantjs_driver(self):
driver = webdriver.PhantomJS()
driver.get('https://www.baidu.com')
print(driver.title)
driver.quit()
pass
源码地址:https://gitee.com/allen-huang/python/blob/master/crawler/do-selenium/test_advanced.py
在通过 selenium打开一个页面,通过点击去其他页面获取数据,这个时候,我们切换标签页来实现,但同时不使用句柄,就无法获取目标页面的内容,这句柄相当于是指针的概念。以下以58同城出租网为例
这是一个列表结构,用来存放所有页面的句柄信息,每打开一个标签的网址,就会存一个,在切换到别的页面时,就去定位对应的索引
driver.window_handles
就是去指定句柄列表中,具体的句柄值,来切换某个页面的资源,索引 index
表示具体哪个页面
driver.switch_to.window(driver.window_handles[index])
将
driver.switch_to.window(driver.window_handles[-1])
这行代码注释,执行后,新标签页面虽然打开了,但资源并没有切换过去,这里是部分源码,具体看test_label_switch
方法
... driver.get("https://hz.58.com/") # todo 获取当前所有的标签页的句柄构成的列表,这不能提前申明变量,因为句柄相当于是指针,这是动态变化的 # current_windows = driver.window_handles # 获取当前页 print(driver.current_url) print(driver.window_handles) # 点击整租的链接,打开租房页面 driver.find_element(by=By.XPATH, value="/html/body/div[3]/div[1]/div[1]/div/div[1]/div[1]/span[2]/a").click() # todo 根据标签页句柄,将句柄切换到最新新打开的标签页,这一定要有,不然句柄不会变化,那么下一页内容取不到 # driver.switch_to.window(driver.window_handles[-1]) print(driver.current_url) print(driver.window_handles) elem_list = driver.find_elements(by=By.XPATH, value="/html/body/div[6]/div[2]/ul/li/div[2]/h2/a") # 统计租房列表的个数 print(len(elem_list)) ...
这个时候发现,页面并没有跳转
注意: 句柄列表,这不能提前申明为变量,因为句柄随着新便签页的打开,这是动态变化的current_windows = driver.window_handles
不能这么赋值。
iframe 即在页面中嵌套子页面,selenium默认是访问不了frame 中的内容的,需要使用
switch_to.frame(frame_element)
,下面以 qzone为例说明
学习文章参考:https://www.cnblogs.com/lc-blogs/p/17269626.html
def test_no_switchto_frame(self): # 声明浏览器驱动实例 driver = webdriver.Chrome(service=self.service) # 打开 qzone 登录页 driver.get("https://qzone.qq.com/") # 默认进来是采用扫码,我们这里需要切换成账号密码登录 driver.find_element(by=By.ID, value="switcher_plogin").click() # 填写账号 driver.find_element(by=By.ID, value="u").send_keys("531881823.qq.com") # 并不是真实的密码 driver.find_element(by=By.ID, value="p").send_keys("123456") # 点击登录按钮 driver.find_element(by=By.XPATH, value='//*[@id="login_button"]').click() time.sleep(5) driver.quit() pass
如果所定位的元素是在 iframe 中,那么必须先切入到 iframe 中再对元素进行定位。
switch_to.frame(frame_element)
frame_element表示可以传入的元素属性值,用来定位 frame ,可以传入id属性、name、index 以及selenium 的 WebElement 对象,假设有来自某网站的 a.com/index.html,如下 HTML 代码:
<html lang="en">
<head>
<title>iframetest</title>
</head>
<body>
<iframe src="a.html" id="fid" name="fname">
...
<div id="test-a">
<a href="test.com" class="test-a">打开页面</a>
</div>
...
</iframe>
</body>
</html>
可以如下定位:
from selenium import webdriver # 打开Chrome浏览器 driver = webdriver.Chrome() # 浏览器访问地址 driver.get("https://a.com/index.html") # 1.用id来定位 driver.switch_to.frame("fid") # 2.用name来定位 driver.switch_to.frame("fname") # 3.用frame的index来定位,第一个是0 driver.switch_to.frame(0) # 4.用WebElement对象来定位 driver.switch_to.frame(driver.find_element(by=By.XPATH,value='//*[@id="fid"]'))
通常采用 id 和 name 就能够解决绝大多数问题。但有时候iframe并无这两项属性,则可以用 index 和 WebElement 来定位:
def test_switchto_frame(self):
driver = webdriver.Chrome(service=self.service)
driver.get("https://qzone.qq.com/")
# 切入iframe
driver.switch_to.frame("login_frame")
driver.find_element(by=By.ID, value="switcher_plogin").click()
driver.find_element(by=By.ID, value="u").send_keys("531881823.qq.com")
driver.find_element(by=By.ID, value="p").send_keys("123456")
driver.find_element(by=By.XPATH, value='//*[@id="login_button"]').click()
time.sleep(5)
driver.quit()
pass
注:切入到 iframe 中之后,如果想对 iframe 外的元素进行操作,必须先从 iframe 中切出后才能操作元素。
从 iframe 中切出的方法:switch_to.default_content()
driver.switch_to.default_content()
如果像一些比较大的平台,登录会比较复杂,那么我们可以先用 selenium 进行模拟登录,拿到 cookie之后,就可以使用requests模块来进行操作了。
看如下代码:就是来展示 cookie的测试
def test_cookie(self):
"""
获取cookie
@return:
"""
driver = webdriver.Chrome(service=self.service)
driver.get("https://baidu.com/")
# 获取cookie的值
cookie_info = {data["name"]: data["value"] for data in driver.get_cookies()}
pprint(cookie_info)
time.sleep(2)
driver.quit()
pass
最后打印出的 key,value 对应的值就是在浏览器中的 cookie结构图,如下图所示:
selenium可以来执行我们的JS代码
driver.execute_script(js)
def test_js(self): """ 执行js,打开一个新窗口,跳转到二手房页面 @return: """ # 打开浏览器,进到链家首页 driver = webdriver.Chrome(service=self.service) driver.get("https://hz.lianjia.com/") # 我想重开一个窗口查看所有的二手房页面 js = "window.open('https://hz.lianjia.com/ershoufang/');" # 执行js代码 driver.execute_script(js) time.sleep(5) driver.quit() pass
def test_js2(self): """ 执行js代码,滚动条往下拉 @return: """ driver = webdriver.Chrome(service=self.service) driver.get("https://hz.lianjia.com/") # 执行js代码,滚动条往下拉500像素 js = "window.scrollTo(0,1000);" driver.execute_script(js) time.sleep(2) # 然后点击查看更多二手房的按钮 driver.find_element(by=By.XPATH, value='//div[@id="ershoufanglist"]/div/div[1]/p/a').click() # 强制等待5秒 time.sleep(5) driver.quit() pass
一开始定位不到时,就报错提示找不到元素
滚动正确的位置后,没再报错,能打开新的标签页
现在的网页越来越多采用了 Ajax 技术,这样程序便不能确定何时某个元素完全加载出来了。如果实际页面等待时间过长导致某个dom元素还没出来,但是你的代码直接使用了这个WebElement,这时候就会报定位不到的错误
为了避免这种元素定位不到的问题,所以 Selenium 提供了3种等待方式,强制等待,隐式等待,显式等待。
强制等待:
就是 time.sleep(x 秒),即整个页面等待 x秒
隐式等待(较为常用
):
针对是元素定位,隐式等待设置了一个时间,在每隔一段时间检查所有元素是否定位成功,如果定位了,就会进行下一步,如果没有定位成功,超时了,就会报超时错误。
假如设置了10秒,在10秒内会定期进行元素定位,但在第5秒钟的时候都加载成功了,那么就会往下执行,它不会一直等到10秒。如果超过了10秒就会报超时错误
driver.implicitly_wait(最大等待时间)
代码:
def test_page_wait(self): """ 隐式等待 @return: """ # 打开浏览器,进到百度首页 driver = webdriver.Chrome(service=self.service) driver.get("https://www.baidu.com/") # 隐式等待,最大等待10秒 driver.implicitly_wait(10) # 点击百度的logo图标的src 属性 elem = driver.find_element(by=By.XPATH, value='//img[@id="s_lg_img_new"]') print(elem.get_attribute("src")) # 强制等待2秒 time.sleep(2) driver.quit() pass
了解
):注意:
我们在隐式等待不好使的时候,才使用强制等待。
这和requests的代理使用类似,都是避免同一个 IP访问多了,封掉,所以使用代理IP
# 声明浏览器配置对象
opt = webdriver.ChromeOptions()
# 需要在配置对象中添代理服务器
opt.add_argument("--proxy-server=http://ip地址:端口号")
def test_proxy(self): """ 使用代理测试121.234.119.235 @return: """ # 声明浏览器配置对象 opt = webdriver.ChromeOptions() opt.add_argument("--proxy-server=http://121.234.119.235:64256") # 打开浏览器,进到百度首页 driver = webdriver.Chrome(service=self.service, options=opt) driver.get("https://www.baidu.com/") driver.save_screenshot('baidu.png') time.sleep(5) driver.quit() pass
频繁使用同一个user-agent,也是容易被封,所以需要不定期的更换 user-agent
# 声明浏览器配置对象
opt = webdriver.ChromeOptions()
# 设置user-agent修改请求头
opt.add_argument("--user-agent=xxxxxxxx")
def test_user_agent(self): """ selenium 可以修改请求头,user-agent,模拟成不同的浏览器 @return: """ user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15" # 声明浏览器配置对象 opt = webdriver.ChromeOptions() # 设置user-agent修改请求头 opt.add_argument(f"--user-agent={user_agent}") # 打开浏览器,进到百度首页 driver = webdriver.Chrome(service=self.service, options=opt) driver.get("https://www.baidu.com/") time.sleep(5) driver.quit() pass
selenium 本是是不能直接获取到请求和响应头,我们还得借助第三方库seleniumwire模块,它可以说是 selenium和requests的结合体。
pip install selenium-wire
def test_selenium_headers(self): """ selenium获取请求头和响应头 @return: """ user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15" # 导入seleniumwire库 from seleniumwire import webdriver # 声明浏览器配置对象 opt = webdriver.ChromeOptions() # 设置user-agent修改请求头 opt.add_argument(f"--user-agent={user_agent}") # 打开浏览器,进到百度首页 driver = webdriver.Chrome(service=self.service, options=opt) driver.get("https://www.baidu.com/") print("请求头:") for request in driver.requests: pprint(request.headers) break print("响应头:") for request in driver.requests: pprint(request.response.headers) break time.sleep(5) driver.quit() pass
service = ChromService(executable_path="/usr/local/bin/chromedriver")
driver = webdriver.Chrome(service=service)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。