请求库之selenium模块
一、介绍
selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题
selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器
from selenium import webdriver
# 得到一个浏览器对象,相当于你打开了一个浏览器
browser=webdriver.Chrome()
browser=webdriver.Firefox()
browser=webdriver.PhantomJS()
browser=webdriver.Safari()
browser=webdriver.Edge()
二、安装
1 有界面浏览器
selenium+chromedriver
#安装:selenium+chromedriver
pip3 install selenium
浏览器驱动,镜像站:http://npm.taobao.org/mirrors/chromedriver/
最新的版本去官网找:https://sites.google.com/a/chromium.org/chromedriver/downloads
驱动要跟浏览器版本对应 84.0.4147.105:驱动用84.0.4147.30/ 找比本机低的版本,向下兼容
下载--->解压是不同平台的可执行文件,windows是exe文件
下载chromdriver.exe放到python安装路径的scripts目录中,因为python路径已经加到环境变量中了
# 验证安装
# chromdriver.exe如果没有加入环境变量,需要指定使用跟那个驱动
# 指定路径写绝对路径,这里因为放在项目根路径下,用的相对路径
from selenium import webdriver
import time
driver=webdriver.Chrome(executable_path='./chromedriver.exe') # 得到一个谷歌浏览器对象
time.sleep(2)
driver.get('https://www.baidu.com/') # 相当于在浏览器地址栏里输入了百度网址
time.sleep(2)
print(driver.page_source) # 拿到html页面,如果页面执行了js,就能拿到js渲染的数据
time.sleep(2)
driver.close() # 关闭浏览器
driver对象贯穿始终,当前在哪个页面上,driver拿的就是这个页面的东西;而每次requests拿到response是不同的
2 无界面浏览器
selenium+谷歌浏览器headless模式
phantomjs,已经不维护了
# 谷歌浏览器支持不打开页面
from selenium.webdriver.chrome.options import Options
from selenium import webdriver
chrome_options = Options() # 得到一个配置
chrome_options.add_argument('window-size=1920x3000') # 指定浏览器分辨率
chrome_options.add_argument('--disable-gpu') # 谷歌文档提到需要加上这个属性来规避bug
chrome_options.add_argument('--hide-scrollbars') # 隐藏滚动条, 应对一些特殊页面
chrome_options.add_argument('blink-settings=imagesEnabled=false') # 不加载图片, 提升速度
# 手动指定使用的浏览器位置,Chrome驱动exe放在环境变量 或executable_path指定了,这句就不用写了
chrome_options.binary_location = r"C:\ProgramFiles(x86)\Google\Chrome\Application\chrome.exe"
# 浏览器不提供可视化页面. linux下如果系统不支持可视化不加这条会启动失败
chrome_options.add_argument('--headless')
drive=webdriver.Chrome(chrome_options=chrome_options, executable_path='./chromedriver.exe')
drive.get('https://www.baidu.com/')
print(drive.page_source)
drive.close() # 不打开浏览器页面,但是进程是开启的,如果请求后不关闭,后面再执行,进程就越开越多
三、基本使用
#模拟登录百度
from selenium import webdriver
from selenium.webdriver.common.by import By #按照什么方式查找,By.ID, By.CSS_SELECTOR
from selenium.webdriver.common.keys import Keys #键盘按键操作
import time
drive=webdriver.Chrome(executable_path='./chromedriver.exe')
# 1、模拟输入-->搜索
drive.get('https://www.baidu.com/')
time.sleep(0.01)
input_search=drive.find_element(By.ID, 'kw') # 按id查找,找到搜索的input框
input_search.send_keys('美女') # 在框里写入美女,可以从自己数据库拿出来动态输入
time.sleep(2)
方式1:
sou=drive.find_element(By.ID, 'su') # 找到搜索按钮
sou.click() # 点击搜索按钮
方式2:模拟键盘操作,直接输入回车
input_search.send_keys(Keys.ENTER)
time.sleep(3)
drive.close()
# 2、模拟登录
drive=webdriver.Chrome(executable_path='./chromedriver.exe')
# 隐式等待:找一个控件,如果控件没有加载出来,等待5s,如果超过5秒没加载出来报错
# 隐式等待是等待所有,只需要写这一句,以后找所有控件都按这个操作来
drive.implicitly_wait(5)
drive.get('https://www.baidu.com/')
# 登录按钮a标签,按a标签内容'登录'查找到这个a标签
login_button=drive.find_element(By.LINK_TEXT, '登录')
login_button.click()
# 找到用户名密码登录按钮并点击,如果后面的控件还没有加载出来就点击会报错,所以用了隐式等待
login_u=drive.find_element(By.ID, 'TANGRAM__PSP_11__footerULoginBtn')
login_u.click()
# 找到用户名密码输入框,输入用户名密码
username=drive.find_element(By.ID, 'TANGRAM__PSP_11__userName')
username.send_keys('yxp654799481')
password=drive.find_element(By.ID, 'TANGRAM__PSP_11__password')
password.send_keys('yxp997997')
time.sleep(3)
# 找到并点击提交按钮
submit=drive.find_element(By.ID, 'TANGRAM__PSP_11__submit')
submit.click()
time.sleep(10)
# 拿到登录的cookie(建cookie池,用requests模块发请求),如果没登录进去会返回未登录的cookie
print(drive.get_cookies())
drive.close()
四、选择器
1、selenium内置的选择器
示例详解
from selenium import webdriver
from selenium.webdriver.common.by import By
1、find_element_by_id # 通过id查找控件
新版 find_element(By.ID, 'search_input')
2、find_element_by_link_text # 通过标签内容(文本)找
新版 find_element(By.LINK_TEXT, 'hao123')
3、find_element_by_partial_link_text # 通过标签内容找,模糊匹配
新版 find_element(By.PARTIAL_LINK_TEXT, 'hao')
4、find_element_by_tag_name # 通过标签名查找
新版 find_element(By.TAG_NAME, 'title')
5、find_element_by_class_name # 通过类名查找
新版 find_element(By.CLASS_NAME, 's_ipt')
6、find_element_by_name # name属性
新版 find_element(By.NAME, 'wd')
7、find_element_by_css_selector # 通过css选择器
新版 find_element(By.CSS_SELECTOR, '#kw')
8、find_element_by_xpath # 通过xpaht选择器
新版 find_element(By.XPATH, '//*[@id="kw"]')
# 强调:
# 新版用法,先声明By模块,from selenium.webdriver.common.by import By
# find_element_by_xxx 单数只找第一个
# find_elements_by_xxx 复数是查找到所有,结果为列表
2、xpath选择器
示例详解
介绍
XPath 是一门在 XML 文档中查找信息的语言
/ 从根节点选取。类似bs4的遍历文档树
// 不管位置,直接找
. 选取当前节点
/@属性名 取当前标签的属性
/text() 取当前标签的文本
# 示例文档
doc='''
<html>
<head>
<base href='http://example.com/' />
<title>Example website</title>
</head>
<body>
<div id='images'>
<a href='image1.html' aa='bb'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
<a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
<a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
<a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
<a href='image5.html' class='li li-item' name='items'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
<a href='image6.html' name='items'><span><h5>test</h5></span>Name: My image 6 <br /><img src='image6_thumb.jpg' /></a>
</div>
</body>
</html>
'''
from lxml import etree
html=etree.HTML(doc)
html=etree.parse('search.html',etree.HTMLParser()) # 直接读取文本文件进行解析
# 1 找出所有节点,拿到一个列表,里面是所有标签对象
res=html.xpath('//*')
# 2 指定节点(结果为列表),找head标签
res=html.xpath('//head')
# 3 子节点,子孙节点
a=html.xpath('//div/a') # 找所有div下的a
a=html.xpath('//body/a') # 无数据,body的子节点没有a标签,找不到
a=html.xpath('//body//a') # body为根节点就能找到所有a标签
# 4 父节点
a=html.xpath('//body//a[@href="image1.html"]/..') # a标签按herf属性="image1.html"过滤
a=html.xpath('//body//a[1]/..') # 取第一个a标签,/..找父节点
# 也可以这样
a=html.xpath('//body//a[1]/parent::*')
# 5 属性匹配
a=html.xpath('//body//a[@href="image1.html"]')
# 6 文本获取(重要) /text() 取当前标签的文本
a=html.xpath('//body//a[@href="image1.html"]/text()') # 取出1个a标签文本放在列表里
a=html.xpath('//body//a/text()') # 取出所有a标签文本,放在列表
# 7 属性获取 @href 取当前标签的href属性
a=html.xpath('//body//a/@href') # 取出所有a标签href属性,放在列表
# 注意索引从1开始(不是从0)
a=html.xpath('//body//a[1]/@href')
# 8 属性多值匹配
# a标签有多个class类,class='li li-item',直接匹配就不可以了,需要用contains
a=html.xpath('//body//a[@class="li"]') # 还差一个class属性,取不到
a=html.xpath('//body//a[contains(@class,"li")]') # class属性包含li,能取到a标签
a=html.xpath('//body//a[contains(@class,"li")]/text()')
# 9 多属性匹配
a=html.xpath('//body//a[contains(@class,"li") or @name="items"]') # 或的关系,能拿出两个
a=html.xpath('//body//a[contains(@class,"li") and @name="items"]/text()') #只能拿出一个
a=html.xpath('//body//a[contains(@class,"li")]/text()')
# 10 按序选择
a=html.xpath('//a[2]/text()') # 第二个a标签
a=html.xpath('//a[2]/@href')
a=html.xpath('//a[last()]/@href') # 取最后一个
a=html.xpath('//a[position()<3]/@href') # 位置小于3的
a=html.xpath('//a[last()-2]/@href') # 倒数第二个
# 11 节点轴选择
# ancestor:祖先节点
a=html.xpath('//a/ancestor::*') # 使用了* 获取所有祖先节点
a=html.xpath('//a/ancestor::div') # 获取祖先节点中的div
# attribute:属性值
a=html.xpath('//a[1]/attribute::*') # 拿出第一个a标签的所有属性
a=html.xpath('//a[1]/@aa') # 拿出第一个a标签的aa属性
# child:直接子节点
a=html.xpath('//a[1]/child::*')
a=html.xpath('//a[1]/child::img/@src') # 拿第一个a标签img子节点的src属性
# descendant:所有子孙节点
a=html.xpath('//a[6]/descendant::*')
a=html.xpath('//a[6]/descendant::h5/text()')
# following:当前节点之后所有节点(兄弟节点和兄弟内部的节点)
a=html.xpath('//a[1]/following::*')
a=html.xpath('//a[1]/following::*[1]/@href')
# following-sibling:当前节点之后同级节点(只找兄弟)
a=html.xpath('//a[1]/following-sibling::*')
a=html.xpath('//a[1]/following-sibling::a')
a=html.xpath('//a[1]/following-sibling::*[2]')
a=html.xpath('//a[1]/following-sibling::*[2]/@href')
print(a)
# 浏览器调试找到需要的标签,复制selector(css选择器),复制xpath(xpath选择器)
# 以后去查找标签,bs4的find, css,xpath(通用的)
3、获取标签属性
# 重点
tag.get_attribute('href') # tag是查找出来的标签(控件),get_attribute找当前控件的href属性对的值
tag.text # 获取文本内容
tag.location # 当前控件在页面位置,应用:验证码截图
# 了解
tag.id # 当前控件id号
tag.tag_name # 标签名
tag.size # 标签的大小
五、等待元素被加载
1、selenium只是模拟浏览器的行为,而浏览器解析页面是需要时间的(执行css,js),一些元素可能需要过一段时间才能加载出来,为了保证能查找到元素,必须等待
2、等待的方式分两种:
隐式等待:在browser.get(url)前就设置,针对所有元素有效
显式等待:在browser.get(url)之后设置,只针对某个元素有效
# 隐式等待:在查找所有元素时,如果尚未被加载,则等10秒
browser=webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('https://www.baidu.com') # 先设置隐式等待,再访问url
# 显式等待:
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait #等待页面加载某些元素
browser=webdriver.Chrome()
browser.get('https://www.baidu.com') # 先访问url
# 显式地等待某个元素被加载
wait=WebDriverWait(browser,10)
wait.until(EC.presence_of_element_located((By.ID,'content_left')))
六、元素交互操作
1 基本操作
tag.send_keys() # 往里面写内容(搜索框、input框)
tag.click() # 点击控件(提交按钮)
tag.clear() # 清空控件内容
2 自己写js
# 有些动作用selenium执行不了,比如:有些网站需要往下滑动才加载,交互动作难实现,需要执行js,打开标签,往下滑动(不要滑到底,边滑边加载页面)
from selenium import webdriver
import time
bro=webdriver.Chrome(executable_path='./chromedriver.exe')
bro.implicitly_wait(5)
bro.get('https://www.baidu.com/')
bro.execute_script('alter('hello')') # 里面写js代码
bro.execute_script('window.scrollTo(0,document.body.offsetHeight)') # scrollTo(起始位,终止位)函数,js控制把屏幕拉到底
bro.execute_script('window.open()') # js操作新打开一个页面
bro.execute_script('window.open()') # js操作再新打开一个页面
time.sleep(2)
bro.close()
3 ActionChains 动作链(了解)
from selenium.webdriver import ActionChains
driver = webdriver.Chrome()
driver.get('http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
# 移动x轴和y轴的距离
ActionChains(driver).move_by_offset(xoffset=2,yoffset=2).perform()
# 直接移动到某个控件上
ActionChains(driver).move_to_elment().perform()
# 在某个控件上,移动X轴与y轴的距离
ActionChains(driver).move_to_element_with_offset(img, xoffset, yoffset).perform()
# 点击一个控件,并按住不动,相当于鼠标左键一直按着,再移动距离就实现了拖动
# 用来模拟滑动验证,但是滑动验证后台规则设计复杂,已经很难破解了
ActionChains(driver).click_and_hold(sourse).perform()
# 释放动作链
ActionChains(driver).release().perform()
# frame的切换,现在很少了,相当于1个浏览器双开了2个页面,可以相互切换
driver.switch_to.frame('iframeResult') #切换到id为iframeResult的frame上
七、其他
1、模拟浏览器前进和后退
from selenium import webdriver
import time
bro=webdriver.Chrome(executable_path='./chromedriver.exe')
bro.get('https://www.baidu.com')
bro.get('https://www.taobao.com')
bro.get('http://www.sina.com.cn/')
bro.back()
time.sleep(1)
brow.forward()
bro.close()
2、cookies
bro=webdriver.Chrome()
bro.get('https://www.zhihu.com/explore')
bro.get_cookies() # 获取cookie
bro.add_cookie({'k1':'xxx','k2':'yyy'}) # 已经有cookie,登录时自动带cookie登录(了解)
bro.delete_all_cookies() # 删除浏览器中的所有cookie(了解)
3、选项卡管理(了解)
from selenium import webdriver
import time
browser=webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()') # 执行js代码,新打开一个标签页面
print(browser.window_handles) #获取所有的选项卡
browser.switch_to_window(browser.window_handles[1]) # 移动到新打开的标签上
browser.get('https://www.taobao.com')
time.sleep(2)
browser.switch_to_window(browser.window_handles[0]) # 移动到旧的标签上
browser.get('https://www.sina.com.cn')
browser.close() # 只关闭了0页面,还要切到1上再关闭1的标签
4、异常处理
from selenium import webdriver
# selenlium中的异常分几类,不用分这么细,总的捕获异常就行了,所有操作放在try中
from selenium.common.exceptions import TimeoutException,NoSuchElementException,NoSuchFrameException
browser=webdriver.Chrome()
try:
browser.get(url)
except Exception as e:
print(e)
finally: # 无论是否出异常,最终都要关掉
browser.close()
5、selenium 规避检测解决方法
正常网页用代码window.navigator.webdriver在console检测会返回false或undefined,但是在selenium中在selenium中会出现True
# 老版本的chrom使用的方法
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options=options, executable_path='./chromedriver')
driver.get("https://taobao.com")
driver.close()
# 新版本的Chrom使用的方法(chrom79版之后)
from selenium import webdriver
driver = webdriver.Chrome()
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
driver.get('http://exercise.kingname.info')
driver.close()
# 为了防止你不确定自己的 chrom 版本,最好两个混合用