发布时间:2023-03-14 08:30
Ajax其实也是JavaScript动态渲染的页面的一种情形,不过JavaScript 动态渲染的页面不止Ajax 这一种: 比如中国青年网(详见 http://news.youth.cn/gn/ ), 它的分页部分是由 JavaScript 生成的,并非原始 HTML代码,这其中并不包含 Ajax 请求。 比如 ECharts 的官方实例(详见 http: //echarts.baidu.com/demo.html#bar-negative ),其图形都是经过 JavaScript 计算之后生成的。 再有淘宝这种页面,它即使是Ajax 获取的数据,但是其 Ajax 接口含有很多加密参数,我 一一 们难以直接找出其规律,也很难直接分析 Ajax 来抓取。
为了解决这些问题,我们可以直接使用模拟浏览器运行的方式来实现, 这样就可以做到在浏览器 中看到是什么样,抓取的源码就是什么样,也就是可见即可爬,这样我们就不用再去管网页内部的 JavaScript用了什么算法渲染页面,不用管网页后台的A jax 接口到底有哪些参数.
Python 提供了许多模拟浏览器运行的库,如 Selenium、 Splash、 PyV8、 Ghost
目录
1、Selenium使用
1、基本使用
2、声明浏览器对象
3、访问页面
4、查找节点
5、节点交互
6、动作链
7、执行JavaScript
8、获取节点信息
8.1 获取属性
8.2 获取文本值
8.3 获取ID,位置,标签名和大小
9、切换Frame
10、延时等待
10.1 隐式等待
10.2 显示等待
11、前进和后退
12、Cookies操作
13、选项卡管理
14、捕获异常
Selenium是一个 自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作, 同时还可以获取浏览器当前呈现的页面的源代码 ,做到可见即可爬, 对于一些 JavaScript动态渲染的页面来说,此种抓取方式非常有效
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
try:
driver.get("http://www.baidu.com")
input = driver.find_element_by_id("kw")
input.send_keys("Python")
input.send_keys(Keys.ENTER)
wait = WebDriverWait(driver,10)
wait.until(EC.presence_of_element_located((By.ID,"content_left")))
print(driver.current_url)
print(driver.get_cookies())
print(driver.page_source)
finally:
driver.close()
运行代码后,会自动弹出一个Chrome浏览器,浏览器首先会跳转到百度,然后再搜索框中输入Python,接着会跳转到搜索结果页面
我们在代码中打印了当前得到的URL、Cookies和源代码都是浏览器中真是内容,如果用 Selenium来驱动浏览器加载网页的话, 就可以直接拿到JavaScript渲染的结果了, 不用担心使用的是什么加密系统
Selenium支持非常多的浏览器,如 Chrome、Firefox、Edge ,还有Android、 BlackBerry 等手机端的浏览器。 另外,也支持无界面浏览器PhantomJS
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver = webdriver.Firefox()
driver = webdriver.Edge()
driver = webdriver.PhantomJS()
driver = webdriver.Safari()
这样就完成了浏览器对象的初始化,并将其赋值为driver对象
使用get() 方法来获取请求网页,参数传入连接URL即可
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
browser = webdriver.Chrome()
browser.get("https://www.taobao.com")
print(browser.page_source)
browser.close()
运行后,可弹出Chrome浏览器并且自动访问了淘宝,然后控制台输出了淘宝输出了淘宝页面的源代码
Selenium 可以驱动浏览器完成各种操作 ,比如填充表单、模拟点击等。比如,我们想要完成向某 个输入框输文字 的操作,总需要知道这个输入框在哪里吧?而 Selenium 提供了一系列查找节点的方法,我们可以用这些方法来获取想要的节点,以便下一步执行一些动作或者提取信息
4.1 单个节点
从淘宝页面中提取搜索框这个节点
可以通过他的属性,id,name来定位到该搜索框
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com")
input_frist = driver.find_element_by_id('q')
input_seconde = driver.find_element_by_css_selector('#q')
input_third = driver.find_element_by_xpath("//*[@id='q']")
print(input_frist,input_seconde,input_third)
driver.close()
结果:
通过三种方式获取输入框,分别是ID,CSS选择器和XPath获取,可以看到返回的结果是完全一致的
其中还包含以下获取单节点的方法:
driver.find_element_by_xpath()
driver.find_element_by_id()
driver.find_element_by_name()
driver.find_element_by_link_text()
driver.find_element_by_partial_link_text()
driver.find_element_by_tag_name()
driver.find_element_by_class_name()
driver.find_element_by_css_selector()
Selenium还提供了通用方法,find_element() ,它需要传入两个参数:查找方式By和值,实际上,他就是find_element_by_id() 这种方法的通用函数版本,比如:find_element_by_id(id) 就等价于find_element(By.ID,id) ,二者得到的结果完全一致,
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com")
input_frist = driver.find_element(By.ID,'q')
print(input_frist)
driver.close()
结果:
4.2、多个节点
网页上的目标只要一个,那么可以使用find_element() 方法,但如果有多个节点,再用 find_e lement () 方法查找,就只能得到第一个节点了, 如果要查找所有满足条件的节点, 需要用 find_elements() 这样的方法.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com")
lis = driver.find_elements_by_css_selector('.service-bd li')
print(lis)
driver.close()
结果:
[, , , , , , , , , , , , , , ]
find_element() 方法,只能获取匹配的第一个节点,结果是WebElement类型,如果用find_elemnts() 方法,则结果是列表形式,列表中每个节点是WebElement类型
这些也是获取多个节点的方法
driver.find_elements_by_css_selector(
driver.find_elements_by_id()
driver.find_elements_by_name()
driver.find_elements_by_xpath()
driver.find_elements_by_partial_link_
driver.find_elements_by_tag_name()
driver.find_elements_by_class_name()
我们也可以使用find_elements() 方法来选择
driver.find_elements(By.CSS_SELECTOR,".service-bd li")
Selenium可以驱动浏览器来执行一些操作,也就是说可以让浏览器模拟执行一些动作,比较常见的用法有:输入文字时用send_keys() 方法,清空文字时用clear() 方法,点击按钮时用click() 方法。
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com")
input = driver.find_element_by_id('q')
input.send_keys("huawei")
time.sleep(2)
input.clear()
input.send_keys("matepad")
button = driver.find_element_by_class_name("btn-search")
button.click()
先定位到输入框,然后输入huawei,等待两秒在清空,在输入matepad,点击搜索
一些交互动作都是针对某个节点执行的,比如:对于输入框,我们就调用它的输入文字和清空文字方法;对于按钮,就调用它的点击方法,其实还有另外一些操作,他们没有特定的对象,比如鼠标的拖拽,键盘的按键等,这些动作用另一种方式来执行,那就是动作链
比如:现在实现一个节点的拖拽操作,将某个节点从一处拖拽到另外一处
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
driver.switch_to.frame("iframeResult")
source = driver.find_element_by_css_selector("#draggable")
target = driver.find_element_by_css_selector("#droppable")
actions = ActionChains(driver)
actions.drag_and_drop(source,target)
actions.perform()
这就是打开一个网页进行拖拽的节点和拖拽到的目标节点,声明一个ActionChains对象并将其赋值为actions变量,然后通过调用actions变量的drag_and_drop() 方法,在调用perform() 方法来进行执行,此时就完成了拖拽
对于某些操作,Slenium API斌没有提供,比如,下拉进度条,它可以直接模拟运行JavaScript,此时使用execute_script() 方法就可以实现
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.zhihu.com/explore")
driver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
driver.execute_script('alert("To Bottom")')
利用execute_script() 方法将进度跳下拉最底部,然后弹出alert提示框
我们可以通过page_source属性可以获取网页的源代码,接着就可以使用解析库(如正则表达式,Beautiful Soup,pyquery等)提取信息
既然 Selenium 已经提供了选择节点的方法,返回的是WebElement 类型,那么它也有相关的方法和属性来直接提取节点信息,如属性、文本等 这样的话,我们就可以不用通过解析源代码来提取信息了,非常方便
使用 get_attribute() 方法来获取节点的属性,但是其前提是先选中这个节点,
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.zhihu.com/explore")
logo = driver.find_element_by_class_name("ExploreCollectionCard-title")
print(logo)
print(logo.get_attribute("class"))
结果:
ExploreCollectionCard-title
通过get_attribute() 方法,然后转入想要获取的属性名,就可以得到它的值了
每个WebElement节点都有text属性,直接调用这个属性就可以得到内部的文本信息,这相当于Beautiful Soup的get_text() 方法,pyquery的text() 方法
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.zhihu.com/explore")
logo = driver.find_element_by_class_name("ExploreCollectionCard-title")
print(logo)
print(logo.text)
结果:
学习提高
WebElemen节点还有一些其他属性,比如ID属性可以获取节点id,location属性可以获取该节点在页面中的相对位置,tag_name属性可以获取标签名称,size属性可以获取节点的大小,也就是宽高,这些属性有时候还是很有用的
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.zhihu.com/explore")
logo = driver.find_element_by_class_name("ExploreCollectionCard-title")
print(logo.id)
print(logo.location)
print(logo.tag_name)
print(logo.size)
结果:
0caa35fa-5868-44c6-950d-c8392bdc63a2
{'x': 40, 'y': 3409}
a
{'height': 28, 'width': 320}
网页中有一种节点叫作iframe,也就是子Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium打开页面后,它默认是在父级别Frame里面操作,而此时如果页面中还有子Frame,它是不能获取到子Frame里面的节点的,这时就需要使用switch_to.frame() 方法来切换Frame
import time
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
driver.switch_to.frame("iframeResult")
try:
logo = driver.find_element_by_class_name("logo")
except NoSuchElementException:
print("NO LOGO")
driver.switch_to.parent_frame()
logo = driver.find_element_by_class_name("logo")
print(logo)
print(logo.text)
结果:
NO LOGO
在Selenium中,get() 方法会在网页框架加载结束后结束执行,此时如果获取page_source,可能并不是浏览器完全加载完成的页面,如果某些页面有额外的Ajax请求,我们在网页源代码中也不一定能成功获取到,这里就需要延时等待一定时间,确保节点已经加载处理啊
使用隐式等待执行测试的时候,如果 Selenium 没有在DOM中找到节点,将继续等待,超出设定时间后,则抛出找不到节点的异常, 换句话说,当查找节点而节点并没有立即出现的时候,隐式等待将等待一段时间再查找DOM ,默认的时间是0
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.zhihu.com/explore")
driver.implicitly_wait(10)
logo = driver.find_element_by_class_name("ExploreCollectionCard-title")
print(logo)
结果:
使用implicitly_wait() 方法实现了隐士等待
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com/")
wait = WebDriverWait(driver,10)
input = wait.until(EC.presence_of_element_located((By.ID,'q')))
button = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR,'.btn-search')))
print(input,button)
引入WebDriverWait这个对象,指定最长等待时间,然后调用until() 方法,传入要等待条件excpeted_conditions比如:这里传入presence_of_element_located这个条件,代表节点出现的意思,其参数就是定位节点元组
在10面内如果ID为q的节点(即搜索框)成功加载出来,就返回该节点,如果10秒还没有加载出来,就抛出异常
常用的等待含义
title_is | 标题是某内容 |
title_contains | 标题包含某些内容 |
presence_of_element_located | 节点加载出来,传入定位元组(By.ID,‘p’) |
visibility_of_element_located | 节点可见,传入定位元组 |
visibility_of | 可见,传入节点对象 |
presence_of_all_elements_located | 所有节点加载出来 |
text_to_be_present_in_element | 某个节点文本包含某文字 |
text_to_be_present_in_element_value | 某个节点值包含某文字 |
frame_to_be_available_and_switch_to_it | 加载并切换 |
invisibility_of_element_located | 节点不可见 |
element_to_be_clickable | 节点可点击 |
staleness_of | 判断一个节点是否存在DOM,可判断页面是哦福已经刷新 |
element_to_be_selected | 节点可选择,传节点对象 |
element_located_to_be_selected | 节点可选择,传入定位元组 |
element_selection_state_to_be | 传入节点对象以及状态,相等返回True,否则返回False |
element_located_selection_state_to_be | 传入定位元组以及状态,相等返回True,否则返回False |
alert_is_present | 是否出现警告 |
平常使用浏览器时都有前进和后退功能, Selenium也可以完成这个操作,它使用back() 方法后退, 使用于forward() 方法前进
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com/")
driver.get('https://www.baidu.com/')
driver.get('https://www.python.org/')
driver.back()
time.sleep(1)
driver.forward()
driver.close()
使用 Selenium ,还可以方便地对 Cookies 进行操作,例如获取、添加、 删除
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com/")
print(driver.get_cookies())
driver.add_cookie({'name':'domain','domain':'www.taobao.com','value':'germey'})
print(driver.get_cookies())
driver.delete_all_cookies()
print(driver.get_cookies())
结果:
[{'domain': '.taobao.com', 'expiry': 2272113900, 'httpOnly': False, 'name': 'cna', 'path': '/', 'sameSite': 'None', 'secure': True, 'value': '7ZxcGrzqCCgCAXtwEYzGcf5w'}, {'domain': '.taobao.com', 'expiry': 1641998700, 'httpOnly': False, 'name': '_m_h5_tk_enc', 'path': '/', 'sameSite': 'None', 'secure': True, 'value': 'd9e042eff1e4af78a5d1a243ebe5bb2b'}, {'domain': '.taobao.com', 'expiry': 1656945899, 'httpOnly': False, 'name': 'isg', 'path': '/', 'secure': False, 'value': 'BAAA-rPvdRaYsQkShG1T14qB0Y7SieRTxgpiCHqRzJuu9aAfIpm049ZHCFw18pwr'}, {'domain': '.taobao.com', 'expiry': 1641998700, 'httpOnly': False, 'name': '_m_h5_tk', 'path': '/', 'sameSite': 'None', 'secure': True, 'value': '3434eb6af78e421b8a32e6073184148f_1641403980978'}]
[{'domain': '.www.taobao.com', 'httpOnly': False, 'name': 'domain', 'path': '/', 'secure': True, 'value': 'germey'}, {'domain': '.taobao.com', 'expiry': 2272113900, 'httpOnly': False, 'name': 'cna', 'path': '/', 'sameSite': 'None', 'secure': True, 'value': '7ZxcGrzqCCgCAXtwEYzGcf5w'}, {'domain': '.taobao.com', 'expiry': 1641998700, 'httpOnly': False, 'name': '_m_h5_tk_enc', 'path': '/', 'sameSite': 'None', 'secure': True, 'value': 'd9e042eff1e4af78a5d1a243ebe5bb2b'}, {'domain': '.taobao.com', 'expiry': 1656945899, 'httpOnly': False, 'name': 'isg', 'path': '/', 'secure': False, 'value': 'BAAA-rPvdRaYsQkShG1T14qB0Y7SieRTxgpiCHqRzJuu9aAfIpm049ZHCFw18pwr'}, {'domain': '.taobao.com', 'expiry': 1641998700, 'httpOnly': False, 'name': '_m_h5_tk', 'path': '/', 'sameSite': 'None', 'secure': True, 'value': '3434eb6af78e421b8a32e6073184148f_1641403980978'}]
[]
在访问网页的时候,会开启一个个选项卡 Selenium 中,我们也可以对选项卡进行操作
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.get("https://www.taobao.com/")
driver.execute_script('window.open()')
print(driver.window_handles)
driver.switch_to.window(driver.window_handles[1])
driver.get("https://www.baidu.com")
time.sleep(1)
driver.switch_to.window(driver.window_handles[0])
driver.get("https://www.python.org")
在使用 Selenium 的过程中,难免会遇到一些异常,所以我们需要try except语句来捕获各种异常
import time
from selenium.webdriver import ActionChains
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import TimeoutException,NoSuchElementException
driver = webdriver.Chrome()
try:
driver.get("https://www.baidu.com")
except TimeoutException:
print('Time Out')
try:
driver.find_element_by_id("word")
except NoSuchElementException:
print("No Element")
finally:
driver.close()