一、无头浏览器:
1.什么是selenium:
Selenium是一个Web的自动测试工具, 最初是为了网站自动化测试二开发的, Selenium可以直接运行在浏览器上,特支持所有主流的浏览器(包括PhantomJS这些无界面的浏览器), 可以接收指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏.
2.Phantom JS的介绍:
PhantomJS是一个基于Webkit的"无界面"(headless)浏览器, 它会把网站加载到内存并执行页面上的JavaScript.
下载地址: http://phantomjs.org/download.html
3. Chromedriver的介绍:
Chromedriver也是一个能够被selenium驱动的浏览器,但是和PhantomJS的区别在于它是有界面的.
下载地址:https://npm.taobao.org/mirrors/chromedriver
4.driver的安装:
最简单的安装方式: 解压后吧bin目录下的可执行文件移动到环境变量下
5. PhantomJS安装实例:
5.1 下载PhantomJS
wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2
5.2 解压并建立软连接:
tar -xvjf phantomjs-2.1.1-linux-x86_64.tar.bz2
sudo cp -R phantomjs-2.1.1-linux-x86_64 /usr/local/share/
sudo ln -sf /usr/local/share/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/
6. chromedriver安装示例:
chromedriver下载地址:
http://chromedriver.storage.googleapis.com/index.html
----- Linux 下安装方式
chromedriver_linux64.zip : 版本 ChromeDriver v2.22 (2016-06-06)
(支持 Chrome v49-52,当前Ubuntu虚拟机下的Chrome版本为50.0)
1. unzip chromedriver_linux64.zip
2. sudo chmod +x chromedriver
3. sudo mv chromedriver /usr/local/bin/
----- MacOS X 下安装方式
chromedriver_mac64.zip : 版本 ChromeDriver v2.32 (2017-08-30)
(支持 Chrome v59-61)
1. 安装 同Linux
----- Windows 下安装方式
chromedriver_win32.zip : 版本 ChromeDriver v2.32 (2017-08-30)
(支持 Chrome v59-61)
1. 解压 chromedriver_win32.zip
2. 将 chromedriver 移动到 Python安装目录下
(其实放哪都可以,但是需要配环境变量,放Python安装目录下省事)
注意:Chromedriver和电脑上的chrome版本有对应关系,建议使用最新的Chromedriver版本并且更新chrome浏览器到最新版
7.小结
- 能够安装PhtantomJs和Chromedriver浏览器
- 能够知道selenium是自动化测试工具/local/share/phantomjs-2.1.1-linux-x86_64/bin/phantomjs /usr/local/bin/
二、selenium的基本使用:
安装selenium:
pip install selenium
1.加载网页:
selenium通过控制浏览器,对应获取的数据都是elements的内容.
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
# driver = webdriver.PhantomJS(executable_path='/home/worker/Desktop/driver/phantomjs')
driver = webdriver.Chrome(executable_path=r''/home/worker/Desktop/driver/chromedriver')
# 想一个url发起请求
driver.get("https://www.baidu.com/")
# 保存页面
driver.save_screenshot("baidu.png")
time.sleep(5)
# 退出
driver.quit()
2.定位和操作 :
以百度搜索为例:
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
# driver = webdriver.PhantomJS(executable_path='/home/worker/Desktop/driver/phantomjs')
driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
# 想一个url发起请求
driver.get("https://www.baidu.com/")
driver.find_element_by_id('kw').send_keys('python')
time.sleep(5)
driver.find_element_by_id('su').click()
time.sleep(5)
# 退出
driver.quit()
3.查看请求信息:
driver.page_source # 查看网页源码
driver.get_cookies() # # 获取当前浏览器的全部cookies
driver.current_url # 获取当前页面的url
4.退出:
driver.close() #退出当前页面
driver.quit() #退出浏览器
5.练习:
模拟百度搜索’传智博客’
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
driver.get("https://www.baidu.com/")
driver.find_element_by_id('kw').send_keys("传智播客")
time.sleep(5)
driver.find_element_by_id('su').click()
time.sleep(5)
# 退出
driver.quit()
6.小结:
- selenium的导包:from selenium import webdriver
- selenium创建driver对象:webdriver.PhantomJS()
- selenium请求数:driver.get(“http://www.baidu.com/”)
- selenium查看数据: driver.page_source
- 关闭无界面浏览器: driver.quit()
- 根据id定位元素: driver.find_element_by_id(“kw”)
- 操作点击事件: click()
- 给输入框赋值:send_keys()
三、selenium元素定位的方法:
1.selenium的定位操作:
定位元素语法:
find_element_by_id(返回一个元素)
find_elements_by_xpatn(返回一个包含元素的列表)
find_elements_by_link_text(根据连接文本获取元素列表)
find_elements_by_partial_link_text(根据连接包含的文本获取袁旭列表)
find_elements_by_tag_name(根据标签名获取元素列表)
find_elements_by_class_name(根据类名获取元素列表)
注意:
find_element和find_elements的区别: 多了s就返回列表, 没有s就返回匹配到的第一个元素
by_link_text和by_partial_link_text的区别: 全部文本和包含某个文本
练习:
以豆瓣首页为例: https://www.douban.com/
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
driver.get("https://www.douban.com/")
# find_element_by_id (返回一个元素)
ret1 = driver.find_element_by_id("anony-nav")
print("ret1: {}".format(ret1))
# find_elements_by_xpath (返回一个包含元素的列表)
ret2 = driver.find_elements_by_xpath('//*[@id="anony-nav"]/h1')
for el in ret2:
print("ret2: {}".format(el))
# find_elements_by_link_text (根据连接文本获取元素列表)
ret3 = driver.find_elements_by_link_text("下载豆瓣 App")
for el in ret3:
print("ret3: {}".format(el))
# find_elements_by_partial_link_text (根据链接包含的文本获取元素列表)
ret4 = driver.find_elements_by_partial_link_text("豆瓣")
for el in ret4:
print("ret4: {}".format(el))
# find_elements_by_tag_name (根据标签名获取元素列表)
ret5 = driver.find_elements_by_tag_name("h1")
for el in ret5:
print("ret5: {}".format(el))
# find_elements_by_class_name (根据类名获取元素列表)
ret6 = driver.find_elements_by_class_name("section")
for el in ret6:
print("ret6: {}".format(el))
time.sleep(5)
# 退出
driver.quit()
2.获取数据:
find_element仅仅能获取元素, 不能够直接获取其中的数据,如果需要获取其中的数据, 需要使用一下方法:
- element.text: 获取文本
- element.get_attribute(“href”): 获取属性值
四、selenium的其他方法:
1.selenium处理cookie:
driver.get_cookies(): 获取的是完整的cookie信息, 不光有name、value, 还有domain等其他信息.
# 把cookie转化成字典
dookie_dict = {cookie['name']: cookie['value'] for cookie in driver.get_cookies()}
# 删除一条cookie
driver.delete_cookie('cookie_name')
# 删除所有的cookie
driver.delete_cookies()
2.页面等待:
- 如果页面采用了动态html技术,name页面上部分元素出现的时间编不能确定,这个时候可以设置一个等待时间, 强制要求在时间内出现,否则报错.
- 页面等待的方法: time.sleep(10)
3.selenium执行js代码:
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
driver.get("http://www.itcast.cn/")
time.sleep(2)
js = 'window.scrollTo(0,document.body.scrollHeight)' # js语句:页面到最后
driver.execute_script(js) # 执行js的方法
time.sleep(5)
# 退出
driver.quit()
4.switch方法切换的操作:
4.1 标签页切换
一个 浏览器肯定会有很多窗口, 所有我们肯定要有方法来实现窗口的切换. 切换窗口的方法如下:
# 1. 获取当前所有的窗口
current_windows = driver.window_handles
# 2.根据窗口索引进行切换
driver.switch_to.window(current_windows[1])
完整代码:
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
# 打开第一个页面
driver.get("https://www.baidu.com/")
time.sleep(1)
driver.find_element_by_id("kw").send_keys('python')
time.sleep(1)
driver.find_element_by_id('su').click()
time.sleep(1)
# 通过js打开第二个标签页
js = 'window.open("http://www.sougou.com/");'
driver.execute_script(js)
time.sleep(3)
# 获取当前所有窗口
windows = driver.window_handles
print(windows)
time.sleep(2)
# 根据窗口进行切换
driver.switch_to.window(windows[0])
time.sleep(2)
driver.switch_to.window(windows[1])
time.sleep(6)
# 退出
driver.quit()
4.2 页内iframe:
iframe是html中常用的一种技术, 即一个页面中嵌套了另一个页面, selenium默认是访问不了frame中的内容的, 对应的解决思路是: driver.switch_to.frame()
在使用selenium登良路QQ邮箱的过程中, 会发现无法在邮箱登录input标签中输入内容,通过观察源码可以发现, from表单在一个frame中,所以需要切换到frame中
login_frame = driver.find_element_by_id('login_frame') # 定位到frame
driver.switch_to.frame(login_frame) # 转向到该frame中
练习:
模拟登录QQ邮箱:
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
mail_url = "https://mail.qq.com/cgi-bin/loginpage"
driver.get(mail_url)
time.sleep(2)
# 根据定位,切换到iframe
login_frame = driver.find_element_by_id('login_frame')
driver.switch_to.frame(login_frame)
driver.find_element_by_link_text("帐号密码登录").click()
# 输入邮箱的账号密码
driver.find_element_by_xpath('//*[@id="u"]').send_keys("117****619@qq.com")
driver.find_element_by_xpath('//*[@id="p"]').send_keys("********")
driver.find_element_by_xpath('//*[@id="login_button"]').click()
time.sleep(3)
# 切换到iframe元素的外边
windows = driver.window_handles
driver.switch_to.window(windows[0])
username = driver.find_element_by_xpath('//*[@id="useralias"]').text
print(username)
time.sleep(10)
# 退出
driver.quit()
4.3 alert弹窗:
当触发了某个事件之后,页面出现了弹窗提示,处理这个提示或者获取提示信息方法如下:
driver.switch_to.alert() # 跟frame一样的处理方式!
5. 页面前进和后退
driver.forward() # 前进
driver.back() # 后退
6. selenium的优缺点
- selenium能够执行页面上的js,对于js渲染的数据和模拟登陆处理起来非常容易
- selenium由于在获取页面的过程中会发送很多请求,所以效率非常低,所以在很多时候需要酌情使用
五、selenium的爬虫案例练习:
1.斗鱼爬虫:
爬取斗鱼直播平台的所有房间信息:https://www.douyu.com/directory/all
1.1 思路分析:
- 数据的位置:
每个房间的数据都在class=layout-Cover-list的ul中
- 实现翻页和控制程序结束:
selenium中通过点击就可以实现翻页,观察存在下一页和不存在下一页时候标签的变化
存在下一页时:aria-disabled=“false”
不存在下一页时:aria-disabled=“true”
1.2 实现代码:
ps:应该是斗鱼做了反爬, 始终没有得到预期结果
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
# driver = webdriver.PhantomJS(executable_path='/home/worker/Desktop/driver/phantomjs')
# driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
class Douyu:
def __init__(self):
"""初始化数据"""
self.start_url = "https://www.douyu.com/directory/all"
self.driver = driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
self.content_list = []
def get_content_list(self):
"""提取数据"""
time.sleep(5)
li_list = self.driver.find_elements_by_xpath('//ul[@class="layout-Cover-list"]/li')
for li in li_list:
item = {}
item['title'] = li.find_element_by_xpath('//h3[@class="DyListCover-intro"]').text
item['zone'] = li.find_element_by_xpath('//span[@class="DyListCover-zone"]').text
item['anchor'] = li.find_element_by_xpath('//h2[@class="DyListCover-user"]').text
item['watch_num'] = li.find_element_by_xpath('//span[@class="DyListCover-hot"]').text
self.content_list.append(item)
print(item)
# 提取下一页数据
next_url = self.driver.find_element_by_xpath('//li[@title="下一页"]')
if next_url.get_attribute("aria-disabled")=="false":
return next_url
else:
return False
def save_content_list(self):
"""保存content"""
for content in self.content_list:
print(content)
def run(self):
"""运行逻辑"""
self.driver.get(self.start_url)
next_url = self.get_content_list()
while next_url:
next_url.click()
time.sleep(3)
next_url = self.get_content_list()
# self.save_content_list()
self.driver.close()
if __name__ == "__main__":
douyu = Douyu()
douyu.run()
ps: 有看出问题的同学,欢迎留言(笑哭)…
2.网易云音乐爬虫:
完成网易云音乐的所有列表名字和url地址
地址: http://music.163.com/#/discover/playlist
2.1 思路分析:
- 确定程序入口位置;
- 确定首页数据的位置和url地址;
- 实现反野和程序停止的判断;
2.2 技术点:
- 数据在iframe中:
2.3 参考代码:
# coding=utf-8
from selenium import webdriver
import time
# 指定driver的绝对路径
# driver = webdriver.PhantomJS(executable_path='/home/worker/Desktop/driver/phantomjs')
# driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
class CloudMusic(object):
def __init__(self, *args, **kwargs):
self.start_url = 'http://music.163.com/#/discover/playlist'
self.driver = webdriver.Chrome(executable_path=r'F:\学习\python学习内容\python练习\02_selenium的使用\chromedriver_win32\chromedriver.exe')
def get_content_list(self):
"""提取数据"""
# 切换进iframe
data_frame = self.driver.find_element_by_id('g_iframe')
self.driver.switch_to.frame(data_frame)
# 获取数据
li_list = self.driver.find_elements_by_xpath('//*[@id="m-pl-container"]/li')
print(f"该页有歌单:{len(li_list)}")
content_list = []
for li in li_list:
item = {}
item['title'] = li.find_element_by_class_name('msk').get_attribute("title")
item['href'] = li.find_element_by_class_name('msk').get_attribute("href")
print(item)
content_list.append(item)
# 下一页
page_url_list = self.driver.find_elements_by_xpath('.//div[@class="u-page"]/a')
next_url = page_url_list[-1] if page_url_list[-1].get_attribute("class") != "zbtn znxt js-disabled" else None
return content_list, next_url
def save_content_list(self, content_list):
for content in content_list:
print(content)
def run(self):
"""实现主逻辑"""
# 请求首页面
self.driver.get(self.start_url)
time.sleep(3)
# 获取数据
contemt_list, next_url = self.get_content_list()
# 请求下一页
while next_url is not None:
js = 'window.scrollTo(0,document.body.scrollHeight)' # js语句:滚动到页面最底部
self.driver.execute_script(js) # 执行js的方法
print(next_url.get_attribute('href'))
next_url.click() #页面没有完全加载完,会报错
time.sleep(3)
# 此时在iframe标签中 代码逻辑需要我们先切出
windows = self.driver.window_handles
self.driver.switch_to.window(windows[0])
content_list, next_url = self.get_content_list()
self.save_content_list(content_list)
# 保存逻辑
# self.save_content_list(content_list)
# 退出浏览器
self.driver.quit()
if __name__ == "__main__":
cloudmusic = CloudMusic()
cloudmusic.run()