Selenium
1. selenium概述
我们在抓取一些普通网页的时候requests基本上是可以满足的. 但是, 如果遇到一些特殊的网站. 它的数据是经过加密的. 但是呢, 浏览器却能够正常显示出来. 那我们通过requests抓取到的内容可能就不是我们想要的结果了
简单介绍一下selenium, 它本身是一个自动化测试的工具. 可以启动一个全新的浏览器.并从浏览器中提取到你想要的内容. 随着各种网站的反爬机制的出现. selenium越来越受到各位爬sir的喜爱. selenium最大的缺点其实就一个, 慢! 你想啊. 他要启动一个第三方的软件(浏览器), 并且还要等待浏览器把数据渲染完毕. 这个过程必然是很耗时的. 所以它慢.
2. selenium安装
pip install selenium
它与其他库不同的地方是他要启动你电脑上的浏览器, 这就需要一个驱动程序来辅助.
chrome驱动地址: http://chromedriver.storage.googleapis.com/index.html
根据你电脑的不同自行选择吧.  win64选win32即可.
然后关键的来了. 把你下载的浏览器驱动放在python解释器所在的文件夹.
3.selenium案例boss数据抓取
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys  # 所有按键的指令
import time
web = Chrome()
web.get("https://www.zhipin.com/beijing/")
# 找到那个框框
# web.find_element_by_id()  # 过时的方案. 新版本中可能不在支持
# web.find_element_by_xpath()
# 当你使用find_xxxx如果找不到东西. 它会报错. 有可能不是下面代码的问题. 而是浏览器没有加载完成.
input_el = web.find_element(By.XPATH, '//*[@id="wrap"]/div[3]/div/div[1]/div[1]/form/div[2]/p/input')  # type: selenium.webdriver.remote.webelement.WebElement
print(type(input_el))
input_el.send_keys("python", Keys.ENTER)  # 输入回车
time.sleep(1)
# 剩下的事情. 抓数据就完了
li_list = web.find_elements(By.XPATH, "//div[@class='job-list']/ul/li")
for li in li_list:
    # selenium用的不是一个标准的xpath语法规则
    # 最后一项不可以是@xxx, text()
    a = li.find_element(By.XPATH, ".//span[@class='job-name']/a")
    name = a.text  # 直接    节点.text
    href = a.get_property("href")  # @href
    price = li.find_element(By.XPATH, ".//span[@class='red']")
    print(name, href, price.text)
    a.click()  # 点击
    time.sleep(5)
    # 如果弹出了新窗口. 那么你需要把程序调整到新窗口里. 才能开始采集数据. 否则会报错.
    web.switch_to.window(web.window_handles[-1])   # 跳转到最后一个窗口
    details = web.find_element(By.XPATH, "//div[@class='job-detail']").text
    print(details)
    print("================")
    # 关闭当前窗口
    web.close()  # 关闭了新窗口之后. selenium需要手动调整窗口到原来的窗口上
    web.switch_to.window(web.window_handles[0]) # 切换回来
# 向框框输入内容, 敲回车
# 没有名为 selenium.web driver 的模块;selenium is not a package  。怎么办
4.iframe切换
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
web = Chrome()
web.get("http://www.wbdy.tv/play/42491_1_1.html")
# 找到那个iframe
iframe = web.find_element(By.XPATH, '//iframe[@id="mplay"]')
web.switch_to.frame(iframe)
val = web.find_element(By.XPATH, '//input[@class="dplayer-comment-input"]').get_attribute("placeholder")
print(val)
# 调整回上层结构
web.switch_to.parent_frame()
xxx = web.find_element(By.XPATH, '/html/body/div[2]/div[3]/div[2]/div/div[2]/h2').text
print(xxx)
5.无头浏览器
我们已经基本了解了selenium的基本使用了. 但是呢, 不知各位有没有发现, 每次打开浏览器的时间都比较长. 这就比较耗时了. 我们写的是爬虫程序. 目的是数据. 并不是想看网页. 那能不能让浏览器在后台跑呢? 答案是可以的.
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options # 专门处理无头方式
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select  # 专门用来处理下拉框的
import time
opt = Options()
opt.add_argument("--headless")  # 无头
opt.add_argument('--disable-gpu')  # 禁用GPU
opt.add_argument("--window-size=4000,1600")  # 设置窗口大小
web = Chrome(options=opt)  # 创建浏览器
web.get("https://www.endata.com.cn/BoxOffice/BO/Year/index.html")
table = web.find_element(By.ID, "TableList")
print(table.text)
# 找到下拉框
sel = web.find_element(By.ID, "OptionDate")
sel = Select(sel)
for option in sel.options:
    o = option.text
    sel.select_by_visible_text(o)
    time.sleep(2)
    # 切换年份
    table = web.find_element(By.ID, "TableList")
    print(table.text)
6.超级鹰使用
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import requests
from hashlib import md5
class Chaojiying_Client(object):
    def __init__(self, username, password, soft_id):
        self.username = username
        password = password.encode('utf8')
        self.password = md5(password).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }
    def PostPic(self, im, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
                          headers=self.headers)
        return r.json()
    def PostPic_base64(self, base64_str, codetype):
        """
        im: 图片字节
        codetype: 题目类型 参考 http://www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
            'file_base64': base64_str
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, headers=self.headers)
        return r.json()
    def ReportError(self, im_id):
        """
        im_id:报错题目的图片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()
# 创建浏览器
web = Chrome()
# 获取页面
web.get("https://www.chaojiying.com/user/login")
# 输入用户名
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[1]/input").send_keys("账号")
# 输入密码
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[2]/input").send_keys("密码")
# 截屏图片
img = web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/div/img")
bs = img.screenshot_as_png  # 返回的是字节
# 超级鹰的账号,密码,软件id
chaojiying = Chaojiying_Client('超级鹰账号', '超级鹰密码', '934639')
# 图片字节与超级鹰的识别码
dic = chaojiying.PostPic(bs, 1004)
# 获取识别结果
code = dic['pic_str']
# 填入获取的识别码
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[3]/input").send_keys(code)
# 提交登录
web.find_element(By.XPATH, "/html/body/div[3]/div/div[3]/div[1]/form/p[4]/input").click()
6.图鉴的使用
1.requsets方式模拟登录图鉴
import json
import requests
def base64_api(uname, pwd, img, typeid):
    data = {"username": uname, "password": pwd, "typeid": typeid, "image": img}
    result = json.loads(requests.post("http://api.ttshitu.com/predict", json=data).text)
    if result['success']:
        return result["data"]["result"]
    else:
        return result["message"]
session = requests.session()
session.headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36"
}
session.get("http://www.ttshitu.com/login.html?spm=null")
# 拿取图片验证码
url = "http://admin.ttshitu.com/captcha_v2?_=1654512026648"
resp = session.get(url)
img = resp.json()['img']  # 拿到图片
imgId = resp.json()['imgId']  # 拿到ID
# 账号密码
name = 账号
password = 密码
# 识别码
erify_code = base64_api("图鉴账号", "图鉴密码", img, 1)
# 模拟登录
urls = "http://admin.ttshitu.com/common/api/login/user"
data = {
    "captcha": erify_code,
    "developerFlag": False,
    "imgId": imgId,
    "needCheck": True,
    "password": password,
    "userName": name
}
resp = session.post(urls, json=data)  # 如果给了json参数. 自动的帮你转化和处理. 以及请求头的处理
print(resp.text)
2.selenium模拟bilibili登录
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.chrome.options import Options
import time
import base64
import json
import requests
def base64_api(uname, pwd, img, typeid):
    # 官方的案例. 是把一张图片. 处理成了base64
    with open(img, 'rb') as f:
        base64_data = base64.b64encode(f.read())
        b64 = base64_data.decode()
    data = {"username": uname, "password": pwd, "typeid": typeid, "image": b64}
    result = json.loads(requests.post("http://api.ttshitu.com/predict", json=data).text)
    if result['success']:
        return result["data"]["result"]
    else:
        return result["message"]
option = Options()
# 可以去掉显示的那个"自动化工具xxxx"
option.add_experimental_option('excludeSwitches', ['enable-automation'])
# 它可以取消掉webdriver
option.add_argument('--disable-blink-features=AutomationControlled')
web = Chrome(options=option)
# 获取首页
web.get("https://www.bilibili.com/")
web.implicitly_wait(5)  # 软等待
web.maximize_window()  # 窗口最大化
# 首页登录点击
web.find_element(By.XPATH, '//div[@class="header-login-entry"]/span').click()
# 用户输入
web.find_element(By.XPATH, '//*[@class="bili-mini-account"]/input').send_keys("账号")
# 密码输入
web.find_element(By.XPATH, '//*[@class="bili-mini-password"]/div/input').send_keys("密码")
time.sleep(2)
# 点击登录
web.find_element(By.XPATH, '//div[@class="universal-btn login-btn"]').click()
# 获取截屏
img = web.find_element(By.XPATH, '//*[@class="geetest_widget geetest_medium_fontsize"]')
# 存储图片到文件中
img.screenshot("tu.png")
# 图鉴识别图片
result = base64_api(uname='图鉴账号', pwd='图鉴密码', img="tu.png", typeid=27)
print(result)
# 坐标切分
res = result.split("|")
# 循环取出切分,并转成数字类型
for rs in res:
    x, y = rs.split(",")
    x = int(x)
    y = int(y)
    # 找到截图的那个位置的左上角, 横向移动xxx, 纵向移动xxx, 点击, 提交
    # 事件链, 动作链  一系列的操作
    ActionChains(web).move_to_element_with_offset(img, xoffset=x, yoffset=y).click().perform()
    time.sleep(0.5)
time.sleep(0.5)
# 登录
web.find_element(By.XPATH, '//div[@class="geetest_commit_tip"]').click()
7.关于等待
在selenium中有三种等待方案
- 
time.sleep() 这个没啥说的. 就是干等. 不论元素是否加载出来. 都要等 
- 
web.implicitly_wait(10) 这个比上面那个人性化很多. 如果元素加载出来了. 就继续. 如果没加载出来. 此时会等待一段时间. 注意, 此设置是全局设置. 一次设置后. 后面的加载过程都按照这个来. (爬虫用的会多一些) 
- 
WebDriverWait 这个比较狠. 单独等一个xxxx元素. 如果出现了. 就过, 如果不出现. 超时后, 直接报错. 
ele = WebDriverWait(web, 10, 0.5).until(
        EC.presence_of_element_located((By.XPATH, "/html/body/div[5]/div[2]/div[1]/div/div"))
    )
8.Selenium可以获取到Elements的结构代码
import time
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from lxml import etree
def get_page_source(url):
    web.get(url)
    web.find_element(By.XPATH, "//*[@class='video-list row']")
    time.sleep(3)
    page_source = web.page_source  # 获取page_source, 也就是elements中的代码
    return page_source
if __name__ == '__main__':
    web = Chrome()
    web.implicitly_wait(10)  # 设置一个等待
    for i in range(1, 6):
        page = i
        o = page * 36
        url = f"https://search.bilibili.com/all?keyword=%E5%87%A4%E5%87%B0%E8%8A%B1%E5%BC%80%E7%9A%84%E8%B7%AF%E5%8F%A3&from_source=webtop_search&spm_id_from=333.1007?page={page}&o={o}"
        ps = get_page_source(url)
        tree = etree.HTML(ps)
        txt = tree.xpath('//*[@class="video-list row"]//text()')  # 自行细化就好
        print(txt)
        print(f"======={url}============================")
        time.sleep(1)
9.屏蔽网站监测是否是自动测试软件方案
from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options
option = Options()
# 可以去掉显示的那个"自动化工具xxxx"
option.add_experimental_option('excludeSwitches', ['enable-automation'])
# 它可以取消掉webdriver
option.add_argument('--disable-blink-features=AutomationControlled')
web = Chrome(options=option)
10.12306自动抢票
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# 谷歌浏览器
import time
from selenium.webdriver import Chrome
# 导入等待对象模块
from selenium.webdriver.support.wait import WebDriverWait
# 导入条件判断模块
from selenium.webdriver.support import expected_conditions as EC
# 导入查询元素模块
from selenium.webdriver.common.by import By
# 导入options
from selenium.webdriver.chrome.options import Options
# 导入事件链
from selenium.webdriver.common.action_chains import ActionChains
# 导入处理下拉框的
from selenium.webdriver.support.select import Select
# 实例化
option = Options()
# 可以去掉显示的那个"自动化工具xxxx"
option.add_experimental_option('excludeSwitches', ['enable-automation'])
# 它可以取消掉webdriver
option.add_argument('--disable-blink-features=AutomationControlled')
web = Chrome(options=option)
# 获取全屏
web.maximize_window()
# 等待
web.implicitly_wait(5)
linktypeid = "dc"
fs = "上海,SHH"
ts = "福州,FZS"
date = "2022-06-18"
flag = "N,N,Y"
chechi = "D3135"
window = "1F"
# 获取火车票页面
base_url = "https://kyfw.12306.cn/otn/leftTicket/init?linktypeid={}&fs={}&ts={}&date={}&flag={}"
url = base_url.format(linktypeid, fs, ts, date, flag)
web.get(url)
# 等待超时时间
wait = WebDriverWait(web, 10, 0.5)
# 通过时间判定选择点击预订
# 寻找 tr 标签中 属性id值以 ticket_ 开头的数据
# 获取每个预定id
tr_list = wait.until(EC.visibility_of_all_elements_located((By.XPATH, '//tr[starts-with(@id,"ticket_")]')))
for tr in tr_list:
    train = tr.find_element(By.CLASS_NAME, "number").text
    tr.find_element(By.XPATH, '//tr/td/a[@class="btn72"]').click()
    break
# 点击账号(注意因为是异步加载的所有需要显性等待)
wait.until(EC.visibility_of_element_located((By.LINK_TEXT, "账号登录"))).click()
# 账号密码
dic = {
    "name": "账号",
    "password": "密码"
}
# 输入用户名和密码
web.find_element(By.XPATH, "//*[@id='J-userName']").send_keys(dic["name"])
web.find_element(By.XPATH, "//*[@id='J-password']").send_keys(dic["password"])
web.find_element(By.XPATH, "//*[@id='J-login']").click()
time.sleep(2)
# 粗糙处理滑块,获取滑块的按钮
btn = web.find_element(By.ID, "nc_1_n1z")
# 保持按住按钮,移动x轴300,y不动,移动到后松开,提交
ActionChains(web).click_and_hold(btn).move_by_offset(xoffset=300, yoffset=0).release().perform()
time.sleep(1)
# 点击选择人物
wait.until(EC.visibility_of_element_located((By.ID, "normalPassenger_0"))).click()
# 点击选择儿童票(默认成人票)
# children = wait.until(EC.visibility_of_element_located((By.ID, "ticketType_1")))
# sel = Select(children)
# for line in sel.options:
#     o = line.text
#     if o == "儿童票":
#         # 点击儿童票
#         wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="ticketType_1"]/option[2]'))).click()
#         break
# # 疫情信息确认
# wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="dialog_xsertcj_ok"]'))).click()
time.sleep(2)
# 一等座选择(默认二等座)
# seat = wait.until(EC.visibility_of_element_located((By.ID, "seatType_1")))
# sel_seat = Select(seat)
# for sa in sel_seat.options:
#     s = sa.text
#     t = s[0:]
#     if s == t:
#         # 拿到一等座
#         wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="seatType_1"]/option[2]'))).click()
#         break
# 点击确认订单
wait.until(EC.visibility_of_element_located((By.ID, 'submitOrder_id'))).click()
time.sleep(2)
# 点击确认购买
wait.until(EC.visibility_of_element_located((By.XPATH, '//*[@id="qr_submit_id"]'))).click()
11.selenium的cookies处理方式
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
import time
url = "https://www.17k.com/"
web = Chrome()
web.implicitly_wait(5)
web.get(url)
web.maximize_window()
# 进入登录
web.find_element(By.XPATH, '//*[@id="header_login_user"]/a[1]').click()
iframe = web.find_element(By.XPATH, "/html/body/div[20]/div/div[1]/iframe")
web.switch_to.frame(iframe)
time.sleep(1)
# 输入用户名密码
web.find_element(By.XPATH, '/html/body/form/dl/dd[2]/input').send_keys("16538989670")
web.find_element(By.XPATH, '/html/body/form/dl/dd[3]/input').send_keys("q6035945")
# 协议
web.find_element(By.XPATH, '//*[@id="protocol"]').click()  # 协议
# 登录
web.find_element(By.XPATH, '/html/body/form/dl/dd[5]/input').click()
# 登录成功之后. 睡眠一下下.
time.sleep(1)
# 记录cookie
cookies = web.get_cookies()  # 加载的cookie是浏览器上的cookie 所以, 包括了服务器返回的cookie和js执行加载的cookie
# 假设cookie准备给requests使用的话.
cookie = {}
for item in cookies:
    name = item['name']
    value = item['value']
    cookie[name] = value
# 后面的requests就可以直接使用cookie了
import requests
resp = requests.get("https://user.17k.com/ck/author/shelf?page=1&appKey=2406394919", cookies=cookie)
print(resp.text)
总结, selenium的使用方案一般是:
- 
涉及登录. 验证码不想搞. 可以考虑用selenium完成登录. 然后提取cookie. 最后用requests发送真正的请求. 
- 
涉及频繁的校验验证(例如boss). 直接用selenium提取页面源代码. 叫给lxml处理. 

 
                
             
         浙公网安备 33010602011771号
浙公网安备 33010602011771号