请求库之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 版本,最好两个混合用

 

posted @ 2022-09-29 10:34  不会钓鱼的猫  阅读(40)  评论(0编辑  收藏  举报