用 Selenium 爬取网页时,当前访问的 url 就是爬虫当中的目标 url,获取内容只要是页面上可见的,都可以爬取(可见即可爬)。
步骤
Selenium + 浏览器 + 浏览器驱动
1、导入
2、url(找动态 url,抓取到的数据是加密的)
3、获取内容,做解析
Selenium 是一个用于 Web 应用程序测试的工具,最初是为网站自动化测试而开发的,Selenium 可以直接运行在浏览器上,它支持所有主流的浏览器,可以接收指令,让浏览器自动加载页面,获取需要的数据,甚至页面截屏。
chromedriver 是一个驱动 Chrome 浏览器的驱动程序,使用它才可以驱动浏览器。
针对不同的浏览器有不同的 driver,但 Chrome 的兼容性最好。
Ajax:可以使用网页实现异步更新,可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
1、获取 Ajax 数据的方式
直接分析 Ajax 调用的接口,然后通过代码请求这个接口。
使用 Selenium + chromedriver 模拟浏览器行为获取数据。
2、获取 Ajax 数据方式对比
方式 | 优点 | 缺点 |
---|---|---|
找数据接口 | 直接可以请求到数据,代码量少,性能高 | 分析接口比较复杂,尤其通过 js 混淆的接口,容易被发现是爬虫 |
Selenium | 直接模拟浏览器行为,浏览器能请求到的,使用 Selenium 也能请求到 | 代码量多,性能低 |
1、驱动下载地址
下载 Chrome 驱动
下载 Firefox 驱动
2、安装 Selenium 第三方库(建议指定版本安装)
pip install selenium==4.0.0a1
3、安装驱动(Chrome 的兼容性最好,建议安装 chromedriver)
终端输入命令:where python 可以查看 Python 的安装目录
# 导入模块,加载驱动
from selenium import webdriver
# 内置库
import time
# 加载驱动
drive = webdriver.Chrome()
# 窗口最大化
drive.maximize_window()
# 加载网站
drive.get('https://www.baidu.com')
# 代码停3秒再运行
time.sleep(3)
# 关闭当前的窗口
# drive.close() # 要打开多个窗口,关闭的是当前的窗口
# 退出驱动
drive.quit() # 关闭所有窗口,退出浏览器
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 窗口最大化
driver.maximize_window()
# 加载网站
driver.get('https://www.baidu.com')
根据 id 来查找定位元素。
# find_element 找一个元素,find_elements 找多个
el = driver.find_element(By.ID, 'kw')
# 返回的是一个元素对象
print(el)
通过 class 属性值定位某个元素。
el = driver.find_element(By.CLASS_NAME, 's_ipt')
# 返回的是一个元素对象
print(el)
通过 name 属性查找定位某个元素。
el = driver.find_element(By.NAME, 'wd')
# 返回的是一个元素对象
print(el)
通过标签定位元素。
input_tag = driver.find_elements(By.TAG_NAME, 'input')
# find_elements 定位元素,返回的数据类型是 list
print(input_tag)
通过 xpath 语法定位元素。
el = driver.find_element(By.XPATH, '//input[@id="kw"]')
# 返回的是一个元素对象
print(el)
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 窗口最大化
driver.maximize_window()
# 加载网站
driver.get('https://www.baidu.com')
# 定位到元素
el = driver.find_element(By.ID, 'kw')
# 元素输入框里面输入值
el.send_keys('唐僧')
# 等待2秒
time.sleep(2)
# 点击元素
driver.find_element(By.ID, 'su').click()
# 清空内容
el.clear()
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 操作键盘
from selenium.webdriver.common.keys import Keys
# 加载驱动
driver = webdriver.Chrome()
# 窗口最大化
driver.maximize_window()
# 加载网站
driver.get('https://music.163.com/')
# 搜索内容
driver.find_element(By.ID, 'srch').send_keys('愿')
# 按回车键
driver.find_element(By.ID, 'srch').send_keys(Keys.ENTER)
# 等待2秒
time.sleep(2)
# 网页里面嵌套了一个网页,要进入该网页才会获取对应的数据
driver.switch_to.frame('g_iframe')
# 播放按钮
driver.find_element(By.ID, 'song_2010214999').click()
cookies = driver.get_cookies()
value = driver.get_cookie(name)
driver.delete_cookie('key')
# 导入模块,加载驱动
from selenium import webdriver
# 加载驱动
driver = webdriver.Chrome()
# 窗口最大化
driver.maximize_window()
# 加载网站
driver.get('https://www.baidu.com')
# 获取百度的 cookie
cookies = driver.get_cookies()
# 返回的 list,列表里面嵌套的字典
print(cookies)
'''
通过程序拿到的 cookie 与页面的是不一致,还需要做处理
只需要里面的两个字段值,name 和 value
BAIDUID_BFESS=F875E52E04E65B3748D8FDDE2E25399E; ZFY=idY6KuND2T0e6ROY1txjofI8Nvn4lb6hXiw:BN2mgeaA:C
'''
for cookie in cookies:
print(cookie['name']+'='+ cookie['value'])
模拟登录 qq 空间
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://i.qq.com/')
# 切换 iframe
driver.switch_to.frame('login_frame')
# 等待2秒
time.sleep(2)
# 点击头像进行登录,获取登录之后的 cookie
driver.find_element(By.ID, 'img_out_1234567890').click()
# 等待2秒
time.sleep(2)
# 获取 cookie
cookies = driver.get_cookies()
# 打印出的数据为列表
# print(cookies)
# 获取的 cookie 需要处理
li = []
for cookie in cookies:
# 只需要每一组字典里面的 name value
li.append(cookie['name']+'='+cookie['value'])
# print(cookie)
# 处理好的 cookie
cookie = '; '.join(li)
print(cookie)
# 导入库
import requests
# 确定 url, 静态加载的
url = 'https://user.qzone.qq.com/1234567890'
# 设置请求头参数
head = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36',
'cookie': cookie
}
# 发请求,获取响应
res = requests.get(url, headers=head)
# 获取响应内容
# print(res.text)
# 写入文件
with open('qq.html', 'w', encoding='utf-8') as f:
f.write(res.text)
select 元素不能直接点击,因为点击后还需要选中元素,这时候 selenium 就专门为 select 标签提供了一个类。
from selenium.webdriver.support.ui import Select
将获取到的元素当成参数传到这个类中,创建这个对象后,就可以使用这个对象进行选择了。
案例
目标网站:https://news.sina.com.cn/
需求:选择娱乐版块,日期8号相关新闻
分析:
1、打开对应网站
2、点击下拉菜单,选择对应的版块内容
3、点击日期图标,选择的日期是8号
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 专门针对 Select 标签使用
from selenium.webdriver.support.ui import Select
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://news.sina.com.cn/')
# 解析,直接定位到 select 标签
el = driver.find_element(By.NAME, 'channel')
# print(el)
# 定位到娱乐的板块,实例化 select 对象
select_tag = Select(el)
# 定位下拉框
# 方法一
# select_tag.select_by_index(4) # 下标从0开始的
# 方法二
# select_tag.select_by_value('ent') # 根据 option 里面 value 属性定位的
# 方法三
select_tag.select_by_visible_text('娱乐') # text 指的是文本
# 等待1秒
time.sleep(1)
# 定位日期
driver.find_element(By.NAME, 'date').click()
# 选择8号
driver.find_element(By.XPATH, '//div[@id="dataView"]/div/table/tbody/tr[2]/td[2]').click()
页面中需要借助鼠标操作元素,那么这时候可以使用鼠标行为链类 ActionChains 来完成,比如现在要将鼠标移动到某个元素上并执行点击事件。
学习文档
actions = ActionChains(driver) # 实例化⼀个⿏标⾏为链的对象
actions.move_to_element(inputTag) # 将⿏标移动到元素的中间
actions.send_keys_to_element(inputTag,'python') # 将键发送到元素
actions.move_to_element(submitTag) # 将⿏标移动到元素的中间
actions.context_click() # 对元素执⾏上下⽂单击(右键单击)
actions.click(submitTag) # 单击⼀个元素
actions.perform() # 执⾏所有存储的操作
actions.lick_and_hold(element) # 点击但不松开⿏标
actions.double_click(element) # 双击
更多方法可参考
目标网站:https://passport.vip.com/login?src=https%3A%2F%2Fwww.vip.com%2F
需求:
1、加载网站
2、切换登录方式
3、输入账号密码,勾选协议,点击登录案例
4、鼠标移动到对应的验证码元素上面
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 导入鼠标行为链
from selenium.webdriver import ActionChains
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://passport.vip.com/login?src=https%3A%2F%2Fwww.vip.com%2F')
# 等待2秒
time.sleep(2)
# 切换登录方式 —— 1、定位元素 2、点击
driver.find_element(By.XPATH, '//div[@class="c-tab-nav "]/div[2]').click()
# 等待0.5秒
time.sleep(0.5)
# 输入用户名
driver.find_element(By.ID, 'J_login_name').send_keys('3424234234')
# 输入密码
driver.find_element(By.ID, 'J_login_pwd').send_keys('34234242')
# 等待0.5秒
time.sleep(0.5)
# 勾选协议
inputs = driver.find_element(By.ID, 'J_login_agree') # 定位到这个元素
# 通过 click 无法点击成功,可以采用 js 点击
driver.execute_script('arguments[0].click();', inputs)
# 登录
driver.find_element(By.ID, 'J_login_submit').click()
# 等待1秒
time.sleep(1)
# 定位图片
img = driver.find_element(By.CLASS_NAME, 'vipsc_qimg')
# 鼠标移动
actions = ActionChains(driver)
actions.move_to_element(img) # 移动到目标位置
# 提交行为链
actions.perform()
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 先模拟打开多个网站
driver.get('https://www.baidu.com') # 百度
time.sleep(3)
driver.execute_script('window.open("https://www.douban.com/")') # 豆瓣
time.sleep(3)
driver.execute_script('window.open("https://juejin.cn/")') # 掘金
time.sleep(3)
driver.execute_script('window.open("https://cloud.tencent.com/developer/ask/sof/1237007")') # 腾讯
time.sleep(3)
# 打印鼠标聚焦的 url
# print(driver.current_url)
# 切换窗口
driver.switch_to.window(driver.window_handles[-3])
'''
0:https://www.baidu.com
1:https://cloud.tencent.com/developer/ask/sof/1237007
2:https://juejin.cn/
3:https://www.douban.com/
-1:https://www.douban.com/
-2:https://juejin.cn/
-3:https://cloud.tencent.com/developer/ask/sof/1237007
'''
# 打印鼠标聚焦的 url
print(driver.current_url)
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://juejin.cn/')
# 等待2秒
time.sleep(2)
# 定位第二篇文章
driver.find_element(By.XPATH, '//div[@class="entry-list list"]/li[2]').click()
# 等待1秒
time.sleep(1)
# 切换窗口
driver.switch_to.window(driver.window_handles[1])
# 获取 elements 内容
te = driver.page_source
print(te)
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://music.163.com/#/discover/toplist')
# 等待2秒
time.sleep(2)
# 切换到 iframe
driver.switch_to.frame('g_iframe')
# 等待2秒
time.sleep(2)
# 拿到的是 elements 里面的内容,xpath 直接做解析就可以了
print(driver.page_source)
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://tieba.baidu.com/f?kw=%E8%8F%9C%E8%B0%B1&ie=utf-8&pn=11400')
# 等待2秒
time.sleep(2)
# # 获取网页源码
# html = driver.page_source
# # 打印源码
# print(html)
# # 看网页源码里是否存在下一页
# print(driver.page_source.find('下一页>'))
'''
如果没有查找到对应的字符,返回的是-1
如果查找到了,返回的是数字
'''
while True:
# 判断源码里存在下一页时
if driver.page_source.find('下一页>') != -1:
# 点击下一页的按钮
driver.find_element(By.CLASS_NAME, 'next').click()
# 等待1秒
time.sleep(1)
# 源码里不存在下一页时,跳出循环
else:
print('已经是最后一页了')
break
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://movie.douban.com/top250')
# 等待2秒
time.sleep(2)
# 获取网页源码
html = driver.page_source
# 等待1秒
time.sleep(1)
# 根据链接文本定位,点击‘后页>’
driver.find_element(By.LINK_TEXT, '后页>').click()
# 打印点击‘后页>’后的文本
print(driver.page_source)
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://movie.douban.com/top250')
# 等待2秒
time.sleep(2)
# 获取图片标签
img_tag = driver.find_element(By.XPATH, '//div[@class="pic"]/a/img')
# 获取属性值 —— 通过方法
print(img_tag.get_attribute('src'))
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 最大化窗口
driver.maximize_window()
# 加载网站
driver.get('https://movie.douban.com/top250')
# 等待2秒
time.sleep(2)
# 获取文本内容
div_tag = driver.find_element(By.XPATH, '//div[@class="hd"]').text
# 打印文本内容
print(div_tag)
# 内置库
import time
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 设置参数
options = webdriver.ChromeOptions()
# 设置无界面模式
options.add_argument('--headless')
# 传递参数
driver = webdriver.Chrome(options=options)
# 加载网站
driver.get('https://movie.douban.com/top250')
# 等待3秒
time.sleep(3)
# 获取图片标签
img_tag = driver.find_element(By.XPATH, '//div[@class="pic"]/a/img')
# 打印属性值
print(img_tag.get_attribute('src'))
time.sleep(时间)
调用 driver.implicitly_wait ,针对所有元素,设置等待时间,如果等待时间内加载出来,代码继续往下走,等待时间以内不断刷新看元素是否加载出来,超出则报出异常。
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 加载驱动
driver = webdriver.Chrome()
# 加载网站
driver.get('https://www.baidu.com')
# 显示等待,针对的全局元素
driver.implicitly_wait(10)
# 10秒内出现代码继续往下走
driver.find_element(By.ID, 'kw').send_keys('python')
# 等待10秒不出现就会报错
driver.find_element(By.ID, 'kw1').send_keys('python')
表明某个条件成立后才执行获取元素的操作,也可以在等待的时候指定一个最大的时间,如果超过这个时间那么就抛出一个异常。
# 显示等待,需要导入模块
# 导入 WebDriverWait 类
from selenium.webdriver.support.wait import WebDriverWait
# 导入 expected_conditions 模块并使用别名 EC
from selenium.webdriver.support import expected_conditions as EC
# 导入模块,加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 显示等待,需要导入模块
# 导入 WebDriverWait 类
from selenium.webdriver.support.wait import WebDriverWait
# 导入 expected_conditions 模块并使用别名 EC
from selenium.webdriver.support import expected_conditions as EC
# 加载驱动
driver = webdriver.Chrome()
# 加载网站
driver.get('https://www.baidu.com')
# 默认是0.5秒刷新一次
# 等待时间总共是10秒,每隔1秒刷新一次
# until 具体的条件内容
element = WebDriverWait(driver, 10, 1).until(
# 判断元素是否加载出来
EC.presence_of_element_located((By.ID, 'kw'))
)
element.send_keys('hello')
隐式等待针对的是全局元素,全局生效,代码简单,只用于查找元素。
显示等待有很多的判断条件,适用范围更广,只针对某一个元素。
目标网站: https://juejin.cn/
需求:爬取文章内容,保存为 txt 格式,文件名以文章标题命名
分析:
翻页方式:滑动滚动条进行加载内容
1、设置先滚动5次,先把下面的内容加载出来,获取元素
2、先获取所有 li 元素,遍历 li 元素,进行点击操作
# 内置库
import time
# 加载驱动
from selenium import webdriver
# 定位元素
from selenium.webdriver.common.by import By
# 正则
import re
class JuJin(object):
# 定义初始化方法
def __init__(self):
# 实例属性
# 加载驱动
self.driver = webdriver.Chrome()
# 窗口最大化
self.driver.maximize_window()
# 加载网站
self.driver.get('https://juejin.cn/')
# 解析数据
def parse_html(self):
# 等待2秒
time.sleep(2)
# 获取 li 里的所有数据
lis = self.driver.find_elements(By.XPATH, '//div[@class="entry-list list"]/li')
# 循环处理数据
for li in lis:
# 捕获异常
try:
# 等待2秒
time.sleep(2)
# 获取文章标题标签
a = li.find_element(By.CLASS_NAME, 'title')
# 点击标题标签进入详情页
self.driver.execute_script('arguments[0].click();', a)
# 切换窗口到内容页
self.driver.switch_to.window(self.driver.window_handles[1])
# 等待2秒
time.sleep(2)
# 获取标题标签
title = self.driver.find_elements(By.CLASS_NAME, 'article-title')
# 获取文章内容标签
contents = self.driver.find_elements(By.XPATH, '//div[@class="markdown-body cache"]/p')
# 判断标题是否是空列表,如果是空列表,说明文章是广告文章
if not title:
# 获取标题标签
title = self.driver.find_elements(By.XPATH, '//a[@class="title"]/span')
# 获取文章内容标签
contents = self.driver.find_elements(By.XPATH, '//div[@class="markdown-body"]/p')
# 获取标题文本
titles = title[0].text
# 正则表达式替换标题特殊字符
titles = re.sub(r'[,?/<>!: ()|"]', '', titles)
# 定义组装数据的变量
s = ''
# 获取数据为列表,需循环取出
for i in contents:
# 组装数据
s += i.text + '\n'
# 打印文章标题,内容
# print(titles)
# 保存数据
self.save_data(titles, s)
# 关闭当前窗口
self.driver.close()
# 切换窗口到列表页
self.driver.switch_to.window(self.driver.window_handles[0])
# 等待1秒
time.sleep(1)
except Exception as e:
# 打印异常
print(e)
print("没有文章内容")
# 关闭当前窗口
self.driver.close()
# 切换窗口到列表页
self.driver.switch_to.window(self.driver.window_handles[0])
# 保存数据
def save_data(self, title, contents):
# 创建 txt 文本文档
with open(f'掘金/{title}.txt', 'w', encoding='utf-8') as f:
# 文件写入
f.write(contents)
# 滚动方法
def slide(self, height):
# 滑动滚动条
self.driver.execute_script(
f'window.scrollTo(0,{height})'
)
# 处理主逻辑
def main(self):
# 获取窗口的高度
heights = self.driver.get_window_size()['height']
# 滑动次数
page = 1
# 滑动
while page <= 5:
self.slide(heights)
# 加高度
heights = heights + heights
# 滑动次数+1
page += 1
# 等待1秒
time.sleep(1)
# 解析数据
self.parse_html()
# 创建对象
data = JuJin()
# 调用 main 方法,开始执行主程序
data.main()
记录学习过程,欢迎讨论交流,尊重原创,转载请注明出处~