Python逆向爬虫之selenimu动态渲染页面爬虫,非常详细
Python逆向爬虫之selenimu动态渲染页面爬虫,非常详细
Selenium 是最广泛使用的开源 Web UI(用户界面)自动化测试套件之一。Selenium 支持的语言包括C#,Java,Perl,PHP,Python 和 Ruby。目前,Selenium Web 驱动程序最受 Python 和 C#欢迎。 Selenium 测试脚本可以使用任何支持的编程语言进行编码,并且可以直接在大多数现代 Web 浏览器中运行。在爬虫领域 selenium 同样是一把利器,能够解决大部分的网页的反爬问题。
一、安装 selenium
pip install selenium
安装完 selenium
之后,并不能立即使用,我们还需要下载各种浏览器的驱动才可以。
注意:部分浏览器驱动需要通过其他渠道下载才能下载。如果没有其他渠道下载的朋友,可以加微信:Alvin0918,博主帮您下载
1.1 Chrome 浏览器驱动安装
在 Chrome 浏览器地址栏输入:chrome://settings/help
下载离当前浏览器版本的最相近的版本。
根据不同的电脑类型下载驱动。
下载完成之后,解压到任何一个路径中,然后添加环境变量即可。
1.2 测试浏览器驱动
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://www.baidu.com/")
如果打开了新的浏览器,就表示安装成功。
二、定位
前面我们已经了解到 selenium 是用来操作浏览器的,所以这一小节我们主要用来了解 selenium 是如何操作 Chrome 浏览器。
2.1 id 定位
根据标签的 ID 属性来定位需要操作的内容。
标签的 id
具有唯一性,就像人的身份证,假设有个 a
标签如下。
<a id="baidu" href="https://www.baidu.com"></a>
我们可以通过 id
定位到它,由于 id
的唯一性,我们可以不用管其他的标签的内容。
from selenium.webdriver.common.by import By web.find_element(By.ID, "baidu")
2.2 name 定位
name
指定标签的名称,在页面中可以不唯一。假设有个 meta
标签如下
<meta name="description" content="CSDN是全球知名中文IT技术交流平台,创建于1999年,包含原创博客、精品问答、职业培训、技术论坛、资源下载等产品服务,提供原创、优质、完整内容的专业IT技术开发社区.">
我们可以使用 find_element
定位到 meta
标签。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/") web.find_element(By.NAME, "description")
2.3 class 定位
class
指定标签的类名,在页面中可以不唯一。
<div class=" active-blog"></div>
我们可以使用 By.CLASS_NAME
定位到 div
标签。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/") web.find_element(By.CLASS_NAME, "active-blog")
2.4 标签定位
每个 tag
往往用来定义一类功能,所以通过 tag
来识别某个元素的成功率很低,每个页面一般都用很多相同的 tag
。
<div></div>
我们可以使用 By.TAG_NAME
定位到 div
标签。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/") web.find_element(By.TAG_NAME, "active-blog")
2.5 xpath 定位
xpath
是一种在 XML
文档中定位元素的语言,它拥有多种定位方式。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/") web.find_element(By.XPATH, '//*[@id="floor-blog-index_747"]/div/div[1]/div[1]/div[1]/div/a/span')
2.6 css 定位
CSS
使用选择器来为页面元素绑定属性,它可以较为灵活的选择控件的任意属性,一般定位速度比 xpath
要快,但使用起来略有难度。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/") web.find_element(By.CSS_SELECTOR, '.blog-text')
2.7 文本定位
link
专门用来定位文本链接,假如要定位下面这一标签。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700/article/details/119549172") web.find_element(By.LINK_TEXT, 'K8S系列第九篇(持久化存储,emptyDir、hostPath、PV/PVC)')
2.8 模糊定位
partial_link
翻译过来就是“部分链接”,对于有些文本很长,这时候就可以只指定部分文本即可定位,同样使用刚才的例子。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700/article/details/119549172") web.find_element(By.PARTIAL_LINK_TEXT, 'K8S系列第九篇')
三、浏览器控制
控制浏览器的大小及其操作。
3.1 设置浏览器窗口大小
webdriver
提供 set_window_size()
方法来修改浏览器窗口的大小。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By web = Chrome(executable_path="./chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700/article/details/119549172") web.set_window_size(100, 150) web.find_element(By.PARTIAL_LINK_TEXT, 'K8S系列第九篇')
3.2 浏览器全屏
webdriver
提供 maximize_window()
方法来设置浏览器窗口全屏。
web.maximize_window()
3.3 浏览器前进&后退
webdriver
提供 back
和 forward
方法来实现页面的后退与前进。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome import time web = Chrome(executable_path="chromedriver.exe") web.get("https://blog.csdn.net/") time.sleep(2) web.get("https://blog.csdn.net/qq_33261700") time.sleep(2) web.back() time.sleep(2) web.forward()
3.4 浏览器刷新
在一些特殊情况下我们可能需要刷新页面来获取最新的页面数据,这时我们可以使用 refresh()
来刷新当前页面。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome import time web = Chrome(executable_path="chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700?type=blog") time.sleep(2) web.refresh()
3.5 浏览器窗口切换
在很多时候我们都需要用到窗口切换,比如:当我们点击注册按钮时,它一般会打开一个新的标签页,但实际上代码并没有切换到最新页面中,这时你如果要定位注册页面的标签就会发现定位不到,这时就需要将实际窗口切换到最新打开的那个窗口。我们先获取当前各个窗口的句柄,这些信息的保存顺序是按照时间来的,最新打开的窗口放在数组的末尾,这时我们就可以定位到最新打开的那个窗口了。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By import time web = Chrome(executable_path="chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700?type=blog") web.maximize_window() web.find_element(By.XPATH, '//*[@id="userSkin"]/div[2]/div/div[2]/div[1]/div[2]/div/article[1]/a').click() windows = web.window_handles web.switch_to.window(windows[0])
3.6 输入内容
在浏览器中使用 send_keys()
输入搜索内容。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By import time web = Chrome(executable_path="chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700?type=blog") time.sleep(1) keys = web.find_element(By.XPATH, '//*[@id="toolbar-search-input"]') time.sleep(1) keys.send_keys('selenimu') time.sleep(1) web.find_element(By.XPATH, '//*[@id="toolbar-search-button"]').click()
3.7 还有其他操作
方法 | 描述 |
---|---|
clear() |
清除文本内容 |
is_displayed() |
判断该元素是否可见 |
get_attribute() |
获取标签属性值 |
size |
返回元素的尺寸 |
text |
返回元素文本 |
四、鼠标控制
在webdriver 中,鼠标操作都封装在ActionChains类中,常见方法如下:
方法 | 描述 |
---|---|
click() |
单击左键 |
context_click() |
单击右键 |
double_click() |
双击 |
drag_and_drop() |
拖动 |
move_to_element() |
鼠标悬停 |
perform() |
执行所有ActionChains中存储的动作 |
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains import time web = Chrome(executable_path="chromedriver.exe") web.get(url="https://www.zhipin.com/shanghai/") time.sleep(1) keys = web.find_element(By.XPATH, '//*[@id="main"]/div/div[1]/div/div[1]/dl[1]') time.sleep(1) move = ActionChains(web).move_to_element(keys).perform()
五、键盘操作
webdriver
中 Keys
类几乎提供了键盘上的所有按键方法,我们可以使用 send_keys + Keys
实现输出键盘上的组合按键如 “Ctrl + C”、“Ctrl + V” 等。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys import time web = Chrome(executable_path="chromedriver.exe") web.get(url="https://www.zhipin.com/shanghai/") # Ctrl + c web.find_element(By.XPATH, '').send_keys(Keys.CONTROL, "c") # Ctrl + v web.find_element(By.XPATH, '').send_keys(Keys.CONTROL, "v") # 回车 web.find_element(By.XPATH, '').send_keys(Keys.ENTER) # F1 web.find_element(By.XPATH, '').send_keys(Keys.F1) # 空格 web.find_element(By.XPATH, '').send_keys(Keys.SPACE) # tab web.find_element(By.XPATH, '').send_keys(Keys.TAB) # ESC键 web.find_element(By.XPATH, '').send_keys(Keys.ESCAPE) # ALT web.find_element(By.XPATH, '').send_keys(Keys.ALT) # SHIFT web.find_element(By.XPATH, '').send_keys(Keys.SHIFT) # 向下 web.find_element(By.XPATH, '').send_keys(Keys.ARROW_DOWN) # 向左 web.find_element(By.XPATH, '').send_keys(Keys.ARROW_LEFT) # 向右 web.find_element(By.XPATH, '').send_keys(Keys.ARROW_RIGHT) # 向上 web.find_element(By.XPATH, '').send_keys(Keys.ARROW_UP)
六、设置元素等待
很多页面都使用 ajax
技术,页面的元素不是同时被加载出来的,为了防止定位这些尚在加载的元素报错,可以设置元素等来增加脚本的稳定性。webdriver
中的等待分为 显式等待 和 隐式等待。
6.1 显式等待
设置一个超时时间,每个一段时间就去检测一次该元素是否存在,如果存在则执行后续内容,如果超过最大时间(超时时间)则抛出超时异常(TimeoutException
)。显示等待需要使用 WebDriverWait
,同时配合 until
或 not until
。下面详细讲解一下。
- driver:浏览器驱动
- timeout:超时时间,单位秒
- poll_frequency:每次检测的间隔时间,默认为0.5秒
- ignored_exceptions:指定忽略的异常,如果在调用 until 或 until_not 的过程中抛出指定忽略的异常,则不中断代码,默认忽略的只有 NoSuchElementException 。
- method:指定预期条件的判断方法,在等待期间,每隔一段时间调用该方法,判断元素是否存在,直到元素出现。until_not 正好相反,当元素消失或指定条件不成立,则继续执行后续代码
- message: 如果超时,抛出 TimeoutException ,并显示 message 中的内容
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time web = Chrome(executable_path="chromedriver.exe") el = WebDriverWait(web, 5, 0.5).until( expected_conditions.presence_of_element_located((By.ID, 'kw')), message="超时了。。。" )
6.2 隐士等待
隐式等待也是指定一个超时时间,如果超出这个时间指定元素还没有被加载出来,就会抛出 NoSuchElementException 异常。
除了抛出的异常不同外,还有一点,隐式等待是全局性的,即运行过程中,如果元素可以定位到,它不会影响代码运行,但如果定位不到,则它会以轮询的方式不断地访问元素直到元素被找到,若超过指定时间,则抛出异常。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time web = Chrome(executable_path="chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700?type=blog") web.implicitly_wait(5) start = time.time() try: web.find_element(By.ID, 'kw') except Exception as e: print(e) print(time.time() - start)
七、find_elements 和 find_element
在 selenium
之前的版本中,定位元素是有 8 种方式,而 selenium 4.0
以后的版本中变成了find_elements 和 find_element 两种。
其中 find_elements 和 find_element 之间的区别也非常的简单,find_elements 是定位多个元素的而 find_elemen 是只能定位一个元素。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time web = Chrome(executable_path="chromedriver.exe") web.get(url="https://blog.csdn.net/qq_33261700?type=blog") for item in web.find_elements(By.XPATH, '//*[@id="userSkin"]/div[2]/div/div[2]/div[1]/div[2]/div/article'): print(item.find_element(By.XPATH, "./a/div[1]/h4").text)
八、上传 & 下载文件
常见的 web 页面的上传,一般使用 input
标签或是插件(JavaScript
、Ajax
),对于 input
标签的上传,可以直接使用 send_keys(路径)
来进行上传。
8.1 上传文件
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <input type="file"> </body> </html>
下面通过 xpath
定位 input
标签,然后使用 send_keys(str(file_path)
上传文件。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time web = Chrome(executable_path="chromedriver.exe") web.get(url="file:///D:/python/爬虫/hello.html") web.find_element(By.NAME, 'upload').send_keys(str('D:/python/爬虫/hello.html'))
8.2 下载文件
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome, ChromeOptions from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time, os current_directory = os.path.dirname(os.path.abspath(__file__)) prefs = { 'profile.default_content_settings.popups': 0, 'download.default_directory': str(current_directory) } option = ChromeOptions() option.add_experimental_option('prefs', prefs) web = Chrome(executable_path="chromedriver.exe", options=option) web.get(url="https://image.baidu.com/search/index?ct=201326592&tn=baiduimage&word=%E9%A3%8E%E6%99%AF&pn=3&spn=0&ie=utf-8&oe=utf-8&cl=2&lm=-1&fr=&se=&sme=&cs=2984023968%2C2695166758&os=1902941552%2C2392086484&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F1115%2F092621094155%2F210926094155-8-1200.jpg%26refer%3Dhttp%3A%2F%2Fimg.jj20.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Dauto%3Fsec%3D1660909844%26t%3D18a8b8c697e65c79c5dd86ccaf3b506e&di=7108135681917976577&tt=1&is=0%2C0&adpicid=0&gsm=1e&dyTabStr=MCwzLDYsMSw0LDUsNywyLDgsOQ%3D%3D") web.find_element(By.XPATH, '//*[@id="mediacy-toolbar"]/span[1]').click() web.switch_to.window(web.window_handles[-1]) web.find_element(By.TAG_NAME, 'html').send_keys('thisisunsafe')
九、cookies
cookies
是识别用户登录与否的关键,爬虫中常常使用 selenium + requests
实现 cookie
持久化,即先用 selenium
模拟登陆获取 cookie
,再通过 requests
携带 cookie
进行请求。
webdriver 提供 cookies 的几种操作:读取、添加删除。
- get_cookies:以字典的形式返回当前会话中可见的 cookie 信息。
- get_cookie(name):返回 cookie 字典中 key == name 的 cookie 信息。
- add_cookie(cookie_dict):将 cookie 添加到当前会话中。
- delete_cookie(name):删除指定名称的单个 cookie。
- delete_all_cookies():删除会话范围内的所有 cookie。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome, ChromeOptions from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time, os current_directory = os.path.dirname(os.path.abspath(__file__)) web = Chrome(executable_path="chromedriver.exe") web.get("https://www.baidu.com") print(web.get_cookies()) print(web.get_cookie('domain'))
十、使用 JS
在使用 selenimu 的时候,难免需要操作 JS,下面我们实操一下 selenimu 调用 JS。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome, ChromeOptions from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time, os current_directory = os.path.dirname(os.path.abspath(__file__)) web = Chrome(executable_path="chromedriver.exe") web.get("https://www.baidu.com") web.execute_script('alert("Hello World!")')
十一、隐藏浏览器指纹
selenium 对于部分网站来说十分强大,但它也不是万能的,实际上,selenium 启动的浏览器,有几十个特征可以被网站检测到,轻松的识别出你是爬虫。
不相信?接着往下看,首先你手动打开浏览器输入https://bot.sannysoft.com/,在网络无异常的情况下,显示应该如下:
同样的,我们使用 selenium 打开该网站。
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome web = Chrome(executable_path="chromedriver.exe") web.get("https://bot.sannysoft.com/")
我们可以看到的是,如果我们爬取的网站真的想阻止也是非常简单的。那么是不是说 selenium 就不能使用呢?并不是这样的。如果我们想隐藏的足够好,就需要 stealth.min.js
这个文件。
11.1 生成 stealth.min.js
生成 stealth.min.js 首先需要安装 nodejs。
npx extract-stealth-evasions
11.2 测试
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome web = Chrome(executable_path="chromedriver.exe") with open('stealth.min.js', 'r') as f: js = f.read() web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": js }) web.get("https://bot.sannysoft.com/")
通过 stealth.min.js
的隐藏,可以看到这次使用无头浏览器特征基本都以隐藏,已经十分接近人工打开浏览器了。
十二、selenium 模拟登录
# -*- coding: utf-8 -*- from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions import time, ddddocr from PIL import Image def runLogin(): """ 开始登录 :return: """ url = "https://weibo.com/login.php" web = Chrome(executable_path="chromedriver.exe") with open('stealth.min.js', 'r') as f: js = f.read() web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": js }) web.get(url=url) web.maximize_window() element = WebDriverWait(web, 10, 0.5).until( expected_conditions.presence_of_element_located((By.XPATH, '//*[@id="loginname"]')), message="超时了。。。" ) web.find_element(By.XPATH, '//*[@id="loginname"]').send_keys('用户名') web.find_element(By.XPATH, '//*[@id="pl_login_form"]/div/div[3]/div[2]/div/input').send_keys('密码') code_img_name = downloadCodeImg(web) code = IdentifyPictures(code_img_name) # 输入识别出来的验证码 web.find_element(By.XPATH, '//*[@id="pl_login_form"]/div/div[3]/div[3]/div/input').send_keys(code) # 开始登录 web.find_element(By.XPATH, '//*[@id="pl_login_form"]/div/div[3]/div[6]/a').click() def IdentifyPictures(code_img_name): """ 识别验证码 :param code_img_name: :return: """ det = ddddocr.DdddOcr(old=True) with open(code_img_name, 'rb') as f: img = f.read() res = det.classification(img) return res def downloadCodeImg(web): """ 保存图形验证码 :param web: :return: """ element = WebDriverWait(web, 10, 0.5).until( expected_conditions.presence_of_element_located((By.XPATH, '//*[@id="pl_login_form"]/div/div[3]/div[3]/a/img')), message="超时了。。。" ) time.sleep(1) # 对当前页面进行截图保存 web.save_screenshot('page.png') # 定位验证码图片的位置 code_img_ele = web.find_element(By.XPATH, '//*[@id="pl_login_form"]/div/div[3]/div[3]/a/img') # 获取验证码左上角的坐标x,y location = code_img_ele.location # 获取验证码图片对应的长和宽 size = code_img_ele.size # 左上角和右下角的坐标 rangle = ( int(location['x']), int(location['y']), int((location['x'] + size['width'])), int((location['y'] + size['height'])) ) i = Image.open('./page.png') code_img_name = './code_img.png' # crop根据rangle元组内的坐标进行裁剪 frame = i.crop(rangle) frame.save(code_img_name) return code_img_name def main(): runLogin() if __name__ == '__main__': main()
# -*- coding: utf-8 -*- # Author:小阳爱技术 import time from selenium.webdriver import Chrome from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.action_chains import ActionChains import requests import base64 from PIL import Image class HandleCode: """ B站自动登录 """ CODE_IMG_PATH = "./code.png" BIG_IMG = "./all.png" def __init__(self): self.img_label = None self.code_coordinate = {} self.web = self.create_chrome() def create_chrome(self): """ 创建浏览器对象 :return: """ # 初始化一个浏览器参数对象 options = Options() options.add_experimental_option("excludeSwitches", ["enable-automation"]) # 去掉了webdriver痕迹 options.add_argument("--disable-blink-features=AutomationControlled") # 指定浏览器分辨率 options.add_argument('window-size=1920x1080') with open("./stealth.min.js", 'r') as f: js = f.read() # 创建一个浏览器对象 web = Chrome(executable_path="./chromedriver.exe", options=options) # 设置一个伪装 web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", { "source": js }) # 隐式等待浏览器渲染完成,sleep是强制等待 web.implicitly_wait(10) # 浏览器内容缩放 67% # web.execute_script("document.body.style.zoom='0.35'") # 浏览器窗口最大化 web.maximize_window() return web def open_chrome_bilibili(self): """ 打开 哔哩哔哩 登录页面 :return: """ self.web.get("https://passport.bilibili.com/login") def get_username_password(self): """ 获取用户名密码 :return: """ return { "username": "15517127859", "password": "Chen18790070830!" } def send_username_password(self): """ 输入用户名密码 :return: """ username_and_password = self.get_username_password() self.web.find_element(By.ID, 'login-username').send_keys(username_and_password['username']) self.web.find_element(By.ID, 'login-passwd').send_keys(username_and_password['password']) def login(self): """ 点击登录 :return: """ self.web.find_element(By.XPATH, '//*[@id="geetest-wrap"]/div/div[5]/a[1]').click() def get_img_base64(self, url): """ 获取 base64 图片 :param url: :return: """ with open(url, 'rb') as f: base64_data = base64.b64encode(f.read()) return base64_data def identification_verification_code(self, typeid=27) -> dict: """ 快识别 :param typeid: :return: """ username_password = { "username": "15517127859", "password": "15517127859" } with open(self.CODE_IMG_PATH, 'rb') as f: base64_data = base64.b64encode(f.read()) b64 = base64_data.decode() data = { "username": username_password['username'], "password": username_password['password'], "typeid": typeid, "image": b64 } resp = requests.post("https://api.ttshitu.com/predict", json=data) if resp.status_code == 200: return resp.json() else: return {'success': True, 'code': '0', 'message': 'success'} def save_screenshot(self): """ 保存图片 :return: """ time.sleep(5) # 截取当前整个页面 self.web.save_screenshot(self.BIG_IMG) def get_coordinates(self): """ 获取验证码坐标对象 :return: """ self.img_label = self.web.find_element(By.CSS_SELECTOR, 'body > div.geetest_panel.geetest_wind > div.geetest_panel_box.geetest_no_logo.geetest_panelshowclick > div.geetest_panel_next > div > div') def get_section(self): """ 计算验证码的左右上下横切面坐标 :return: """ left = self.img_label.location['x'] top = self.img_label.location['y'] right = self.img_label.location['x'] + self.img_label.size['width'] down = self.img_label.location['y'] + self.img_label.size['height'] self.code_coordinate = { "left": left, "top": top, "right": right, "down": down, "width": self.img_label.size['width'], "height": self.img_label.size['height'] } print(self.code_coordinate) # 根据坐标切割图片 im = Image.open(self.BIG_IMG) im = im.crop((left, top, right, down)) im.save(self.CODE_IMG_PATH) def disassemble_coordinate(self, coordinate): """ 拆解坐标,并点击 :param coordinate: 111,222|333,444 :return: """ result_list = coordinate.split('|') for result in result_list: x = result.split(',')[0] y = result.split(',')[1] print(x, y) self.click_code(x, y) time.sleep(2) self.confirm_code() def click_code(self, x, y): """ 根据坐标点击 :param x: :param y: :return: """ action = ActionChains(self.web) action.move_to_element_with_offset( self.img_label, int(x) - 130, int(y) - 162 ).click().perform() def confirm_code(self): """ 确认登录 :return: """ self.web.find_element(By.XPATH, '//a[@class="geetest_commit"]').click() if __name__ == '__main__': # 初始化 code = HandleCode() # 打开B站 code.open_chrome_bilibili() # 用户名和密码 code.send_username_password() # 点击登录按钮 code.login() # 保存整个页面 code.save_screenshot() # 获取验证码 Element 对象 code.get_coordinates() # 切割验证码 code.get_section() # 超级鹰验证码识别 identification_img = code.identification_verification_code() # 判断验证码识别结果 if int(identification_img['code']) == 0: # 根据坐标去点击 code.disassemble_coordinate(identification_img['data']['result']) # 再次点击登录按钮进行登录,但是登录验证需要验证码验证结果,所以需要有所延时 time.sleep(2) # code.login() else: print(identification_img['err_str'])
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)