Selenium抓取动态页面

 

动态渲染页面不止Ajax一种,有些页面是直接由Javascript计算后生成的,并不包含Ajax请求。还有的页面虽然是通过Ajax获取的数据,但是Ajax接口含有很多加密参数,我们很难直接找出其中的规律将数据抓取下来。为解决这个问题,就直接使用模拟浏览器运行的方式,只要浏览器中能够显示出来的都能够抓取到。

selenum相当于一个机器人,可以执行点击、下拉等操作。下面通过它来打开百度页面:

from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://baidu.com')

还可以获取网页源代码:

print(driver.page_source)

 

close()方法和quit()方法分别可以关闭当前页面和整个浏览器:

from selenium import webdriver
import time

driver = webdriver.Chrome()
driver.get('https://baidu.com')
driver.get('https://www.taobao.com')
time.sleep(1)
driver.close()#关闭当前页面
time.sleep(1)
driver.quit()#关闭整个浏览器

然后再来看一个例子:

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

browser = webdriver.Chrome()
try:
    browser.get('https://www.baidu.com')
    input = browser.find_element_by_id('kw')
    input.send_keys('Python')#搜索关键字为Python
    input.send_keys(Keys.ENTER)
    wait = WebDriverWait(browser,10)
    wait.until(EC.presence_of_all_elements_located((By.ID,'content_left')))
    print(browser.current_url)
    print('*'*300)
    print(browser.get_cookies())
    print('*' * 300)
    print(browser.page_source)
finally:
    browser.close()

输出结果是当前的url,Cookies和网页源代码,拿到的都是浏览器中呈现的,因此Selenium可以直接拿到Javascript的渲染结果。

通过以下方法可以定位我们想要的元素:

find_element_by_id:根据id查找某个元素
find_element_by_class_name:根据类名查找元素
find_element_by_tag_name:根据标签名查找元素
find_element_by_xpath:根据xpath语法来获取元素
find_element_by_css_selector:根据CSS选择器选择元素

下面来看一个例子:

                   

现在我们想得到搜索框,可以通过id、name等方式:

from selenium import webdriver
driver = webdriver.Chrome()
driver.get('https://baidu.com')
inputTag = driver.find_element_by_id('kw')#获取搜索框
inputTag.send_keys('python')#向搜索框发送python
inputTag = driver.find_element_by_name('wd')
inputTag = driver.find_element_by_class_name('s_ipt')
inputTag = driver.find_element_by_xpath("//input[@id='kw']")

获取网页源代码之后就可以用xpath进行解析:

from selenium import webdriver
from lxml import etree
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome()
driver.get('https://baidu.com')
inputTag = driver.find_element_by_id('kw')#获取搜索框
inputTag.send_keys('python')#向搜索框发送python
inputTag.send_keys(Keys.ENTER)
source =driver.page_source
html = etree.HTML(source)
print(etree.tostring(html,encoding='utf-8').decode('utf-8'))

操作表单元素:

表单元素:input标签:看type='text'或'password'或者'email'、'number'就表示文本框。
button input[type='submit']表示按钮。
checkbox:input[type='checkbox']
select:下拉列表

使用clear()方法可以清除输入框中的内容:inputTag.clear()

操作checkbox:因为要选中checkbox标签,在网页中是通过鼠标点击的,因此想要选中checkbox标签就要先选中这个标签,然后执行click事件:Tag.click()

查找结点

Selenium可以驱动浏览器完成像填充表单、模拟点击等各种操作,比如我们想要完成向某个输入框输入文字的操作,总需要知道这个输入框在哪里吧?而Selenium提供了一系列查找节点的方法,我们可以用这些方法来获得想要的节点,然后可以执行一些当作或者获取某些信息。

单个节点

比如我们想要从淘宝页面中提取搜索框这个节点,先来查看它的源代码查找它的搜索框:

              

发现它的id和name都是q,因此我们就通过这种方式获取它,通过find_element()方法,传入查找方式By和值也可以,比如find_element(By.ID,id)就相当于find_element_id(id):

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
input_first = browser.find_element(By.ID,'q')
print(input_first)

多个节点

有时候我们需要查找满足条件的所有节点,就需要用到find_elements()方法,比如要查找淘宝页面中主题市场中的所有条目:

                                                  

from selenium import webdriver
from selenium.webdriver.common.by import By

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
lis = browser.find_elements(By.CSS_SELECTOR,'.service-bd li')
for li in lis:
    print(li)
browser.close()

它将所有满足条件的li标签返回,结果如下:

<selenium.webdriver.remote.webelement.WebElement (session="59fc32ac4d359eb362126e3cffca7852", element="0.4544509519611133-1")>
<selenium.webdriver.remote.webelement.WebElement (session="59fc32ac4d359eb362126e3cffca7852", element="0.4544509519611133-2")>
...
<selenium.webdriver.remote.webelement.WebElement (session="59fc32ac4d359eb362126e3cffca7852", element="0.4544509519611133-1")>

每个节点都是Element类型的。

同样find_elements_by_id,find_elements_by_class_name,find_elements_by_tag_name,find_elements_by_xpath,find_elements_by_css_selector也能够匹配多个节点。

节点交互

Selenium可以让浏览器执行一些动作,比如输入文字可以用:send_keys()方法,清空文字可以用clear()方法,点击按钮可以用click()方法。先来举个例子:

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('书包')
time.sleep(1)
input.clear()
input.send_keys('笔记本')
button = browser.find_element_by_class_name('btn-search')
button.click()

动作链

有时候需要做一连串的动作,这时候就需要用到动作连,先来看一个例子:

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)#声明ActionChains对象
actions.drag_and_drop(source,target)#执行拖曳操作
actions.perform()

它还可以模拟执行JavaScript,使用excute_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('alter("To Bottom")')#将进度条下拉到最底部,然后弹出alter提示框

使用page_source属性可以获取网页源代码接着就可以对其进行解析了,但是Selenium也提供了一些方法和属性来直接提取节点信息。使用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('zh-top-link-logo')
print(logo)
print(logo.get_attribute('class'))

每个WebElement节点都有text属性,直接调用这个属性就可以得到节点内部的文本信息:

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
browser.get(url)
input = browser.find_element_by_class_name('zu-top-add-question')#获取'提问'按钮这个节点
print(input.text)#将其文本值打印出来

WebElement节点还有一些其它属性,比如id属性可以获取节点id,location属性可以获取该节点在页面中的相对位置,tag_name属性可以获取标签名称,size属性可以获取节点大小:

from selenium import webdriver
from selenium.webdriver import ActionChains

browser = webdriver.Chrome()
url = 'https://www.zhihu.com/explore'
browser.get(url)
input = browser.find_element_by_class_name('zu-top-add-question')#获取'提问'按钮这个节点
print(input.id)
print(input.location)
print(input.tag_name)
print(input.size)

Selenium打开页面以后默认是在父级Frame里面操作的,而此时如果页面中还有子Frame,它是不能获取到子Frame里面的节点的,需要用switch_to.fram()方法来切换Frame:
 

from selenium import webdriver
import time
from selenium.common.exceptions import NoSuchElementException

browser = webdriver.Chrome()
url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')#切换到子frame里面
#尝试获取父级frame里的logo节点
try:
    logo = browser.find_element_by_class_name('logo')
except NoSuchElementException:
    print('no logo')
browser.switch_to.parent_frame()#切换到父级frame,再次重新获取节点
logo = browser.find_element_by_class_name('logo')
print(logo)
print(logo.text)

Selenium中的get()方法会在网页框架加载结束后结束执行,此时如果获取page_source,可能并不是浏览器完全加载完成的页面,如果某些页面有额外的Ajax请求,我们在网页源代码中也不能成功获取到,所以需要延时等待一定时间,确保节点加载完毕,有隐式等待和显示等待两种等待方式。

当使用隐式等待执行测试的时候,如果Selenium没有在DOM中找到节点,将继续等待,超出设定时间后,则抛出找不到节点的异常,也就是说当查找结点而节点并没有立即出现的时候就等待一段3时间再查找DOM:

from selenium import webdriver

browser = webdriver.Chrome()
browser.implicitly_wait(10)#隐式等待10秒
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,10)#指定最长等待时间
#传入等待条件presence_of_element_located,代表节点出现,参数是节点的定位元组ID为q的搜索框
#10秒内如果ID为q的节点成功加载出来就返回该节点
input = wait.until(EC.presence_of_element_located((By.ID,'q')))
#对于按钮,则将等待条件更改为element_to_be_clickable表示可点击,如果10秒内可点击就返回这个按钮节点
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.btn-search')))
print(input,button)

使用forward()方法和back()方法可以实现前进和后退:

from selenium import webdriver
import time

browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
browser.get('https://www.baidu.com')
browser.get('https://www.python.org')
browser.back()
time.sleep(1)
browser.forward()
browser.close()

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值