在使用Ajax采集数据时,有些Ajax接口含有很多的加密参数,直接很难发现规律。此时,就可以使用模拟浏览器运行的方式来采集。Python 提供了许多模拟浏览器运行的库,如Selenium、Splash、PyV8、Ghost等。
一、模拟浏览器爬取数据Selenium的使用。
1.1、安装准备工作
安装selenium库:pip install selenium
(1)、谷歌(Chrome)浏览器需要ChromeDriver 驱动的安装与配置
- 查看浏览器版本号:点击Chrome右上角的菜单->帮助->关于Google Chrome即可查看,我这里的版本版本 79.0.3945.130
- ChromeDriver下载:https://chromedriver.storage.googleapis.com/index.html
- 下载对应版本及对应系统:chromedriver_win32.zip
- 环境变量配置:这里是windows系统我们直接解压后将chromedriver.exe文件拖到Python(anaconda)的Scripts目录下。
- cmd窗口测试:cmd-->chromedriver,输出Starting ChromeDriver ...on port ...,则安装配置成功。
- 代码测试: 执行下列代码,会弹出一个空白的Chrome浏览器
- from selenium import webdriver
browser=webdriver.Chrome()
(2)、火狐(Firefor)浏览器需要GeckoDriver的安装与配置
- 下载地址:https://github.com/mozilla/geckodriver/releases
- 下载对应版本:如:geckodriver-v0.26.0-win64.zip
- 环境配置:windows系统直接解压拖到Python(anaconda)的Scripts目录下。
- cmd窗口测试:cmd-->geckodriver,输出...geckodriver...,则安装配置成功。
- 代码测试: 执行下列代码,会弹出一个空白的Firefor浏览器
- from selenium import webdriver
browser=webdriver.Firefox()
1.2、声明浏览器对象
- from selenium import webdriver #导入库
- browser =webdriver. Chrome() #谷歌浏览器的声明及初始化
- browser =webdriver. Firefox() #火狐浏览器的声明及初始化
- browser =webdriver. Edge() #Edge浏览器的声明及初始化
- browser =webdriver. PhantomJS() #无界面浏览器PhantomJS的声明及初始化
- browser =webdriver. Safari() #Safari浏览器的声明及初始化
- ......
完成浏览器对象的初始化并将其赋值为browser对象。调用browser对象,让其执行各个动作以模拟浏览器操作。
1.3、访问页面,get()方法
from selenium import webdriver
browser=webdriver.Chrome()
browser.get('https://www.taobao.com/') #访问网页
url= browser.current_url
print(url) #获取url
cookies = browser.get_cookies()
print(type(cookies),cookies) #获取cookies,格式:<class 'list'>
SC = browser.page_source
print(type(SC),SC) #获取源代码,格式:<class 'str'>
1.4、查找节点
单个节点:
获取单个节点:方法 | 说明:返回WebElement类型 | 示例: |
---|---|---|
get() | 请求访问页面 | from selenium import webdriver browser=webdriver.Chrome() browser.get('https://www.taobao.com/') |
find_element_by_id | 根据id属性的值获取节点 | input_01=browser.find_element_by_id('q') #定位到淘宝的搜索框 |
find_element_by_name | 根据name属性值获取节点 | input_02=browser.find_element_by_name('q') |
find_element_by_class_name | 根据class属性值获取节点 | input_03=browser.find_element_by_class_name('search-combobox-input-wrap') |
find_element_by_xpath | 根据Xpath选择器定位 | input_04=browser.find_element_by_xpath('//*[@id="q"]') |
find_element_by_css_selector | 根据CSS选择器定位 | input_05=browser.find_element_by_css_selector('#q') |
find_element_by_link_text | 根据文本定位 | input_06=browser.find_element_by_link_text("淘宝网") |
find_element_by_partial_link_text | 根据局部文本定位 | input_07=browser.find_element_by_partial_link_text("淘宝网") |
find_element_by_tag_names | 根据标签名称定位 | input_08=browser.find_element_by_tag_name('input') |
find_element() | 根据查找方法By和值定位,获取第一个满足条件的节点 |
from selenium.webdriver.common.by import By input_09=browser.find_element(By.ID,'q') #注意大小写 |
多个节点:
返回满足条件的多个节点,其实就是把返回单个节点的element改为elements即可。每个方法都一样。如:
find_elements_by_id |
find_elements_by_name |
find_elements_by_class_name |
find_elements_by_xpath |
find_elements_by_css_selector |
find_elements_by_link_text |
find_elements_by_partial_link_text |
find_elements_by_tag_names |
find_elements() |
1.5、节点交互
Selenium驱动浏览器执行一些操作。比较常见的用法:
- send_keys():输入文字的方法
- clear():清空文字的方法
- click():点击按钮的方法
- 其他:https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.remote.webelement
案例:
from selenium import webdriver
import time
browser=webdriver.Chrome()
browser.get('https://www.taobao.com')
input=browser.find_element_by_id('q')
input.send_keys('python')
time.sleep(6)
input.clear()
input.send_keys(r'python爬虫书籍')
button=browser.find_element_by_class_name('btn-search')
button.click()
'''
代码解释:
这里首先驱动浏览器打开淘宝,然后用find_element_by_id()方法获取输入框,
然后用send_keys()方法输入iPhone文字,
等待6秒后用clear()方法清空输入框,
再次调用send keys()方法输入iPad文字,
再用find_element_by_class_name()方法获取搜索按钮,
最后调用click()方法完成点击搜索动作。
'''
1.6、动作链
一般来说我们与页面的交互用find_element和节点交互就可以完成了。但是一些更复杂的动作,类似于拖动,双击,长按等等。这时候就需要用到Action Chains
(动作链)了。
常用动作方法:
方法 | 说明 |
---|---|
click (on_element=None) |
左键单击传入的元素,如果不传入的话,点击鼠标当前位置。 |
context_click (on_element=None) |
右键单击。 |
double_click (on_element=None) |
双击。 |
click_and_hold (on_element=None) |
点击并抓起 |
drag_and_drop (source, target) |
在source元素上点击抓起,移动到target元素上松开放下。 |
drag_and_drop_by_offset (source, xoffset, yoffset) |
在source元素上点击抓起,移动到相对于source元素偏移xoffset和yoffset的坐标位置放下。 |
send_keys (*keys_to_send) |
将键发送到当前聚焦的元素。 |
send_keys_to_element (element, *keys_to_send) |
将键发送到指定的元素。 |
reset_actions () |
清除已经存储的动作。 |
更多 | https://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.common.action_chains |
案例:
from selenium import webdriver
from selenium.webdriver import ActionChains
browser =webdriver.Chrome() #①
url='http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url) #②
browser.switch_to.frame('iframeResult') #③
source=browser.find_element_by_css_selector('#draggable') #④
target=browser.find_element_by_css_selector('#droppable') #⑤
actions =ActionChains(browser) #⑥
actions.drag_and_drop(source, target) #⑦
actions.perform() #⑧
'''
代码注释:
①:申明Chrome浏览器对象;
②:请求访问页面;
③:切换Frame节点,后面有单独解释;
④:定位拖拽节点;
⑤:定位目标节点;
⑥:声明ActionChains对象并将其赋值为actions变量;
⑦:调用actions变量的drag_and_drop()方法;
⑧:调用perform()方法执行动作
'''
1.7、执行JavaScript
对于某些操作,SeleniumAPI没有提供。如:下拉进度条,利用模拟运行JavaScript的execute_script()方法
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
browser.execute_script('alert("To Bottom")')
'''
这里就利用execute_script()方法将进度条下拉到最底部,然后弹出alert提示框。
'''
1.8、获取节点信息
(1)、通过page_source
属性可以获取网页的源代码,可以用解析库(如Xpath、Beautiful Soup、pyquery等)来提取信息。
(2)、Selenium提供了选择节点的方法,返回的是WebElement
类型,它也有相关的方法来直接提取节点信息。
- 获取属性:可以使用get_attribute()方法,但需要事先选中节点。
from selenium import webdriver
from selenium.webdriver import ActionChains
browser = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
browser.get(url)
logo = browser.find_element_by_id('special')
print(logo)
print(logo.get_attribute('class'))
- 获取文本:每个
WebElement
节点都有text
属性,直接调用这个属性即可。 -
from selenium import webdriver browser = webdriver.Chrome() url = 'https://www.zhihu.com/explore' browser.get(url) input = browser.find_element_by_xpath('//*[@id="root"]/div/div[2]/header/div[1]/ul/li[1]/a') print(input.text) #首页
- 获取节点id、位置、标签名、大小(宽高)
-
from selenium import webdriver browser = webdriver.Chrome() url = 'https://www.zhihu.com/explore' browser.get(url) input = browser.find_element_by_xpath('//*[@id="root"]') print(input.id) #节点id:017b8ce6-0706-4b23-8c66-2a46c96858a1 print(input.location) #节点在页面中的相对位置:{'x': 0, 'y': 0} print(input.tag_name) #标签名称:div print(input.size) #节点大小:{'height': 3665, 'width': 1017}
1.9、切换Frame
一种节点叫作iframe,也就是子Frame,相当于页面的子页面,它的结构和外部网页的结构完全一致。Selenium打开页面后,它默认是在父级Frame里面操作,而此时如果页面中还有子Frame,它是不能获取到子Frame里面的节点的。这时就需要使用switch_to.frame()
方法来切换Frame。
import time
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
try:
logo = browser.find_element_by_class_name('logo')
except NoSuchElementException:
print('NO LOGO')
browser.switch_to.parent_frame()
logo = browser.find_element_by_class_name('logo')
print(logo)
print(logo.text)
'''
代码注释:
首先通过switch_to.frame()方法切换到子Frame里面,
然后尝试获取父级Frame里的logo节点(这是不能找到的),
如果找不到的话,就会抛出NoSuchElementException异常,
异常被捕捉之后,就会输出NO LOGO。
接下来,重新切换回父级Frame,然后再次重新获取节点,发现此时可以成功获取了。
所以,当页面中包含子Frame时,如果想获取子Frame中的节点,
需要先调用switch_to.frame()方法切换到对应的Frame,然后再进行操作。
'''
1.10、延时等待
为了确保节点都全部加载出来,在进行解析,需要设置延时等待。方式有两种:隐式等待和显式等待。
隐式等待:
隐式等待的效果其实并没有那么好,因为我们只规定了一个固定时间,而页面的加载时间会受到网络条件的影响。
from selenium import webdriver
browser = webdriver.Chrome()
browser.implicitly_wait(20) #implicitly_wait()方法实现了隐式等待。
browser.get('https://www.zhihu.com/explore')
input = browser.find_element_by_class_name('zu-top-add-question')
print(input)
'''
超出设定时间后,则抛出找不到节点的异常。但是隐式等待的时间是固定的,不够灵活。
'''
显式等待:
指定要查找的节点,设置不同的等待条件,然后指定一个最长等待时间。如果在规定时间内加载出来了这个节点,就返回查找的节点;如果到了规定时间依然没有加载出该节点,则抛出超时异常。
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
wait = WebDriverWait(browser, 30)
input = wait.until(EC.presence_of_element_located((By.ID, 'q')))
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '.btn-search')))
print(input, button)
等待条件 |
含义 |
---|---|
|
标题是某内容 |
|
标题包含某内容 |
|
节点加载出来,传入定位元组,如 |
|
节点可见,传入定位元组 |
|
可见,传入节点对象 |
|
所有节点加载出来 |
|
某个节点文本包含某文字 |
|
某个节点值包含某文字 |
|
加载并切换 |
|
节点不可见 |
|
节点可点击 |
|
判断一个节点是否仍在DOM,可判断页面是否已经刷新 |
|
节点可选择,传节点对象 |
|
节点可选择,传入定位元组 |
|
传入节点对象以及状态,相等返回 |
|
传入定位元组以及状态,相等返回 |
|
是否出现警告 |
更多 | http://selenium-python.readthedocs.io/api.html#module-selenium.webdriver.support.expected_conditions |
1.11、前进和后退
平常使用浏览器时都有前进和后退功能,Selenium也可以完成这个操作,它使用back()
方法后退,使用forward()
方法前进。
import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com/')
browser.get('https://www.taobao.com/')
browser.get('https://www.python.org/')
browser.back()
time.sleep(1)
browser.forward()
browser.close()
'''
这里我们连续访问3个页面,然后调用back()方法回到第二个页面,接下来再调用forward()方法又可以前进到第三个页面。
'''
1.12、获取、删除、添加Cookies
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
print(browser.get_cookies()) #获取
browser.add_cookie({'name': 'name', 'domain': 'www.zhihu.com', 'value': 'germey'}) #添加
print(browser.get_cookies()) #获取
browser.delete_all_cookies() #删除
print(browser.get_cookies())
1.13、选项卡管理
在访问网页的时候,会开启一个个选项卡。在Selenium中,我们也可以对选项卡进行操作。示例如下:
import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')
print(browser.window_handles)
browser.switch_to_window(browser.window_handles[1])
browser.get('https://www.taobao.com')
time.sleep(1)
browser.switch_to_window(browser.window_handles[0])
browser.get('https://python.org')
首先访问了百度,然后调用了execute_script()
方法,这里传入window.open()
这个JavaScript语句新开启一个选项卡。接下来,我们想切换到该选项卡。这里调用window_handles
属性获取当前开启的所有选项卡,返回的是选项卡的代号列表。要想切换选项卡,只需要调用switch_to_window()
方法即可,其中参数是选项卡的代号。这里我们将第二个选项卡代号传入,即跳转到第二个选项卡,接下来在第二个选项卡下打开一个新页面,然后切换回第一个选项卡重新调用switch_to_window()方法,再执行其他操作即可。
1.14、异常处理
官网文档:http://selenium-python.readthedocs.io/api.html#module-selenium.common.exceptions
from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException
browser = webdriver.Chrome()
try:
browser.get('https://www.baidu.com')
except TimeoutException: #请求超时
print('Time Out')
try:
browser.find_element_by_id('hello')
except NoSuchElementException: #没有找到节点
print('No Element')
finally:
browser.close()
本文来源:崔庆才 著 《python3网络爬虫开发实战》学习笔记