2020-07-18--selenium爬取动态页面

Selenium的使用

selenium是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,例如 点击 下拉等操作,同时还可以获取浏览器当前呈现的页面的源代码,做到可见既可爬。对于一些js动态渲染的页面来说,此种爬取方式非常有效。

安装selenium

pip install selenium

在Google浏览器中配置ChromeDriver

点击Google菜单 帮助 -> 关于Google Chrome -> 查看版本号 (版本 71.0.3578.98(正式版本) (64 位))

下载ChromeDriver https://chromedriver.storage.googleapis.com/index.html

对比自己浏览器版本下载相应版本

解压后把文件放到 python的script目录下或者Anaconda的Script目录下。
例如:E:\Anaconda\Scripts\


测试 在python程序中执行

from selenium import webdriver
browser = webdriver.Chrome()

程序运行后 弹出空白的Chrome浏览器 说明我们之前配置都没有问题

基本使用

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')
  input.send_keys(Keys.ENTER)
  wait = WebDriverWait(browser,10)
  wait.until(EC.presence_of_element_located((By.ID,'content_left')))
  print(browser.current_url)
  print(browser.get_cookies())
  print(browser.page_source)
finally:
  browser.close()

可以看到自动弹出浏览器 首先跳转百度 然后搜索框输入Python 接着跳转搜索结果页
搜索结果出来后 控制台输出当前URL Cookies 网页源代码
使用selenium 驱动浏览器加载网页就可以拿到js渲染结果了 不用担心什么加密系统

声明浏览器对象

Selenium 支持非常多的浏览器 例如 Chrome Firefox Edge等 还有Android BlackBerry等手机端浏览器 也支持PhantomJS 无界面浏览器

初始化方法:

from selenium import webdriver

browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.PhantomJS()
browser = webdriver.Safari()

访问页面

get() 方法请求页面 参数 传入链接URL 例如访问淘宝

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
print(browser.page_source)
browser.close()

弹出Chrome浏览器 自动访问淘宝 控制台输出网页源代码随后浏览器关闭

查找节点

Selenium 可以驱动浏览器完成各种操作,例如填充表单,模拟点击等 例如想要完成某个输入框输入文字操作 需要查找输入框位置

单个节点:

例如淘宝页面提取搜索框节点

id是q name是q

利用 find_element_by_name() 获取name值 find_element_by_id() 获取id 还可以根据xpath CSS选择器等获取方式

示例如下:

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.taobao.com')

input_first = browser.find_element_by_id('q')
input_second = browser.find_element_by_css_selector('#q')
input_third = browser.find_element_by_xpath('//*[@id="q"]')

print(input_first,input_second,input_third)
browser.close()

返回WebElement类型

获取单个节点的方法:

find_element_by_id()
find_element_by_name()
find_element_by_xpath()
find_element_by_link_text()
find_element_by_partial_link_text()
find_element_by_tag_name()
find_element_by_class_name()
find_element_by_css_selector()

另外 还提供了通用方法 find_element() 需要传入两个参数:1.查找方式 2.值 例如:find_element(By.ID,id) 等价于 find_element_by_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)
browser.close()

多个节点:

find_elements() 查找所有满足条件的节点
示例: 查找淘宝左侧导航条的所有条目

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('http://www.taobao.com')
lis = browser.find_elements_by_css_selector('.service-bd li')
print(lis)
browser.close()

返回列表 每个元素是WebElement类型

获取多个节点的方法:

find_elements_by_id()
find_elements_by_name()
find_elements_by_xpath()
find_elements_by_link_text()
find_elements_by_partial_link_text()
find_elements_by_tag_name()
find_elements_by_class_name()
find_elements_by_css_selector()

节点交互

send_keys() 输入文字
clear() 清空文字
click() 点击按钮

实例:

from selenium import webdriver

dr = webdriver.Chrome()

# 打开淘宝,get方法无返回值
dr.get('https://www.taobao.com')
#查找淘宝搜索框id为q
input = dr.find_element_by_id('q')
#搜索框中键入ipone
input.send_keys('iphone')
#休眠一秒
time.sleep(1)
#清空搜索框
input.clear()
#重新键入ipad
input.send_keys('iPad')
#查找搜索按钮
# btn = dr.find_element_by_css_selector('button.btn-search.tb-bg')
btn1 = dr.find_element_by_class_name('btn-search.tb-bg')
#点击按钮
btn1.click()

首先打开淘宝 获取输入框 输入iphone文字 等待一秒 清空输入框 输入ipad 说去搜索按钮 点击按钮。

动作链

例如鼠标拖拽 键盘按键等动作。
实例:

from selenium import webdriver
from selenium.webdriver import ActionChains

dr = webdriver.Chrome()

url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
dr.get(url)

#切换浏览器对象
dr.switch_to.frame('iframeResult')

#作用对象
source = dr.find_element_by_css_selector('#draggable')
target = dr.find_element_by_css_selector('#droppable')

#为浏览器生成动作链对象
actions = ActionChains(dr)
#调用方法传入参数
actions.drag_and_drop(source,target)
#执行动作
actions.perform()

完成如此下动作

鼠标拖拽方框到指定位置。

执行javascript

from selenium import webdriver

dr = webdriver.Chrome()
dr.get('https://www.zhihu.com/explore')

'''
每一秒向下浏览500的浏览器高度
'''
st = 'let x = 0;' \
     'window.setInterval(function(){' \
     'window.scrollTo(x,x+500);' \
     'x=x+500;' \
     '},1000)'
dr.execute_script(st)

浏览器自动向下滑动。

获取节点信息

获取属性 get_attribute() 方法 获取节点属性 前提是选中这个节点

获取文本值 href属性和text文本

import time

from selenium import webdriver

browser = webdriver.Chrome()

'''
<ul class="nav-hd ">
<li><a href="https://www.tmall.com?spm=a21bo.2017.201859.1.319b11d9lcQUm5" data-spm-anchor-id="a21bo.2017.201859.1">天猫</a></li>
<li><a href="https://ju.taobao.com" data-spm-anchor-id="a21bo.2017.201859.2">聚划算</a></li>
<li><a href="https://chaoshi.tmall.com" data-spm-anchor-id="a21bo.2017.201859.3">天猫超市</a></li>
</ul>
'''
browser.get('http://www.taobao.com')

#查找超链接a元素,返回列表
list = browser.find_elements_by_css_selector('ul.nav-hd li a')
print(list)

#遍历列表
for ele in list:
    print(type(ele))
    print(ele.text)
    print(ele.get_attribute('href'))
    time.sleep(1)
    
browser.close()

结果:

切换frame

页面中有一种节点iframe 也就是frame 相当于当前页面的子页面 结构和外部网页结构完全一致 selenium 打开页面默认是在父级frame里面操作,如果里面还有子页面是获取不到里面节点的 需要使用 switch_to.frame() 方法切换子frame。
browser.switch_to.frame('iframeResult')#切换到子frame
如果要切换出来---到父页面中:
browser.switch_to.parent_frame()#切换到父frame

实例:

子页面:

查询logo代码:

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')#切换到子frame
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)

因为logo是在原有的页面上,当切换到iframe子页面中时,是取不到logo的,所以要在父页面中查询。

延时等待

selenium中get方法会在网页框架加载结束后结束执行 如果此时获取page_source 可能并不是网页加载完全的页面 如果某些页面存在ajax加载 网页源代码也不一定能获取的到 所以需要延时等待一定时间,确保节点已经加载出来

两种方式:隐式等待 显式等待

1.隐式等待

当使用隐式等待时 selenium没有在DOM中找到节点 将继续等待 超出设置时间后 抛出异常 默认时间是0 示例: implicitly_wait() 方法

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

browser = webdriver.Chrome()

browser.get('https://www.taobao.com')
#隐式等待:1秒钟接收不到元素,报错
#等待1秒
browser.implicitly_wait(1)

#查找元素
input_first = browser.find_element(By.ID,'q')
print(input_first)

browser.close()

2.显式等待 until() 方法

如果页面加载受网络条件影响 隐式效果就不是很好

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait#引入 WebDriverWait对象

browser = webdriver.Chrome()
browser.get('https://www.taobao.com/')
wait = WebDriverWait(browser,10)         #等待对象wait,最长等待时间
input = wait.until(EC.presence_of_element_located((By.ID,'q')))#调用until方法 节点出现,停止等待
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.btn-search')))#调用until方法 当按钮可点击时,停止等待
print(input,button)

更多等待条件

title_is() 标题是某内容
title_contains()    标题包含某内容
presence_of_element_located() 节点加载出来 传入定位元组 例如(By.ID,'P')
visibility_of_element_located()    节点可见 传入定位元组
visibility_of() 可见 传入节点对象
presence_of_all_element_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_located_to_be_selected()    节点可选择 传入节点对象
element_selection_state_to_be()    传入节点对象以及状态 相等返回True 否则False
element_located_selected_to_be() 传入定位元组以及状态 相等返回True 否则False
alert_is_present()    是否出现警告

前进和后退

  • forward() 方法前进
  • back()方法后退。

也就是操作js的History对象。
示例:

import time
from selenium import webdriver

browser = webdriver.Chrome()
# 依次访问baidu和taobao
browser.get('https://www.baidu.com')
browser.get('https://www.taobao.com')

#返回baidu
browser.back()
time.sleep(1)
#进入taobao
browser.forward()

Cookies

  • 获取 get_cookies()
  • 添加 add_cookie()
  • 删除 delete_all_cookies()

示例:

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
#获取cookie
print(browser.get_cookies())

#添加cookie
browser.add_cookie({'name':'zhangsan','domain':'wwww.zhihu.com','value':'germery'})
print(browser.get_cookies())

#删除cookie
browser.delete_cookie()
print(browser.get_cookies())

选项卡管理

在访问网页时会出现一个个选项卡 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)

time.sleep(1)
#切换到第二个选项卡窗口
browser.switch_to_window(browser.window_handles[1])
#打开taobao
browser.get('https://www.taobao.com')

time.sleep(1)
#切换到第一个选项卡窗口
browser.switch_to_window(browser.window_handles[0])
#打开知乎
browser.get('https://www.zhihu.com')

打开baidu。,在打开新的选项卡,现在有两个选项卡了,切换到第二个选项卡,打开taobao,切换到第一个选项卡,打开知乎。

异常处理

在使用selenium的过程中 难免会出现一些异常 例如 超时 节点未找到 一旦出现此类错误程序就不会执行了 使用 try except 捕获异常

示例:节点未找到

from selenium import webdriver

browser = webdriver.Chrome()
browser.get('https:www.baidu.com')
browser.find_element_by_id('hello')

报错 NoSuchElementException
防止程序遇到异常中断 捕获异常示例:

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')

browser.close()

出现异常时,会输出相应内容。

综合实战----爬取网易云音乐评论

rom selenium import webdriver
import time
from pyquery import PyQuery as pq

dr = webdriver.Chrome()
# dr.maximize_window()
dr.get('https://music.163.com/#/song?id=31877470')

# 进入浮动窗口,通过name查找
dr.switch_to.frame('contentFrame')
#输出代码
src = dr.page_source
#准换pq对象
doc = pq(src)
# print(src)
# time.sleep(3)
#查找歌名文本
name = dr.find_element_by_css_selector('em.f-ff2')
print(name.text)

'''
歌词部分:
<div id="lyric-content" class="bd bd-open f-brk f-ib" data-song-id="31877470" data-third-copy="false" ,="" copy-from="">
    作曲 : 昔日布道尔吉<br>作词 : 贾樟柯 / 左小祖咒<br><br>你走了那么多年<br><br>你还在我的身边<br><br>那一天你微笑的脸<br><br>
    如今闭上眼 我还能看得见<br><br>穿过旷野的风 你慢些走<br><br>
    
    <div id="flag_more" class="f-hide">
    我用沉默告诉你 我醉了酒<br><br>乌兰巴托的夜 那么静那么静<br><br>连风都听不到 听不到<br><br>飘向天边的云 你慢些走<br><br>
    我用奔跑告诉你 我不回头<br><br>乌兰巴托的夜 那么静那么静<br><br>连云都不知道 不知道<br><br>乌兰巴托的夜<br><br>嘿 你在<br><br>
    你在这世界 每个角落存在<br><br>嘿 你在<br><br>你穿过风<br>穿过云 穿过一切 回来<br><br>乌兰巴托的夜 那么静那么静<br><br>连风都听不到 听不到<br><br>
    乌兰巴托的夜 那么静那么静<br><br>连云都不知道 不知道<br><br>我们的世界改变了什么<br><br>我们的世界期待着什么<br><br>我们的世界剩下些什么<br><br>
    我们的世界只剩下荒漠<br><br>穿过旷野的风 你慢些走<br><br>唱歌的人不时掉眼泪<br>
    </div>
    <div class="crl">
        <a id="flag_ctrl" href="javascript:void(0)" class="s-fc7">展开<i class="u-icn u-icn-69"></i></a>
    </div>
</div>
'''
'''
展开按钮:
<div class="crl">
    <a id="flag_ctrl" href="javascript:void(0)" class="s-fc7">展开<i class="u-icn u-icn-69"></i></a>
</div>
'''


#播放歌曲按钮
play = dr.find_element_by_css_selector('a.u-btn2.u-btn2-2.u-btni-addply.f-fl')
play.click()

#歌词
morebody = doc('div#lyric-content')
print(morebody.text())
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 224,242评论 6 522
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,953评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 171,299评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,709评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,723评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,236评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,629评论 3 428
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,594评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,135评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,156评论 3 345
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,285评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,914评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,600评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,073评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,203评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,798评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,339评论 2 365