当前位置: 首页 > news >正文

使用Python发送PDD直播间弹幕(协议算法分析)

文章目录

  • 1. 写在前面
  • 2. 接口分析
  • 3. 算法还原

【🏠作者主页】:吴秋霖
【💼作者介绍】:擅长爬虫与JS加密逆向分析!Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python与爬虫领域研究与开发工作!
【🌟作者推荐】:对爬虫领域以及JS逆向分析感兴趣的朋友可以关注《爬虫JS逆向实战》《深耕爬虫领域》
未来作者会持续更新所用到、学到、看到的技术知识!包括但不限于:各类验证码突防、爬虫APP与JS逆向分析、RPA自动化、分布式爬虫、Python领域等相关文章

作者声明:文章仅供学习交流与参考!严禁用于任何商业与非法用途!否则由此产生的一切后果均与作者无关!如有侵权,请联系作者本人进行删除!

1. 写在前面

  研究分析的缘由则是一个读者找到作者。大致的一个情况就是他的朋友做直播带货但是前期小主播的话流量会比较少,而游客或者用户的浏览点击进入到直播间如果能够发送评论弹幕消息的话,在某种程度来说是可以增加一个热度的。而且最重要的就是对于新手主播来说,如果没有用户观众的互动是非常枯燥的(也会尴尬),毕竟一直对着空气介绍是需要情绪价值助力的~

2. 接口分析

这里我们直接抓包分析/proxy/api/api/fanta/chat/push接口,这个接口是发送弹幕消息的接口,接口的请求参数如下所示:

comment_params = {"pdduid": "5061621833"
}
data = {"show_id": "20240716_71745644_01","anchor_id": 71745644,"message": "我来啦~~","room_id": "71745644","roomId": "71745644","_live_share_token": "CRQ46EEGIEMXATU2NPB2SDCKSMTR6KUGMHUCO7CSBFRYQO4KQY3GMYAWO7RVISCC4ZETPSP2PYHEVHBFONKUNTBXA7K52H4SM7PUGX56BOTUOYYVZTU6FYKYXFQLPQI6","anti_content": "" # 动态加密
}

想要发送弹幕,主要需要解决的一个参数就是anti_content,开头是0as的,但是如果你是加了轨迹的0aq也是可以发送的,差不多600多位吧!

在这里插入图片描述

下图是作者使用多个Token通过加密算法调用接口发送的弹幕消息测试。Cookie信息的话其实是不需要放全的,验证只需要PDDAccessToken字段即可,但是必须是登录状态的!

在这里插入图片描述

加密算法需要传递必要的参数生成,这里可以直接使用Py封装一个动态替换的方法,如下:

import execjsdef generate_anti_content(self):try:with open("./anti_content_v2.0.js", "rb") as f:js_code = f.read()node = execjs.get()js_code = js_code.decode("gbk", 'ignore').replace("{cookie}", self.cookie_str).replace("{href}", self.referer).replace("{ua}", self.user_agent)if self.cookie_str not in js_code or self.user_agent not in js_code:raise Exception("未替换成功")ctx = node.compile(js_code)return ctx.call('get_anti_content')except Exception as e:logger.error(f"生成Anti-Content失败: {e}")raise

其实有时候对于算法的使用场景很多人需求都不一样,Py直接调用的活着HTML内甚至是易语言调用,通用最好的方案就是起一个API服务,这样在使用场景上不需要瘦到语言的限制,作者也是使用JS实现了一个API服务,直接传参拿到加密参数,如下所示:

const fs = require('fs');
const http = require('http');
const url = require('url');const server = http.createServer((req, res) => {const { query } = url.parse(req.url, true);const cookie = query.cookie || '';const href = query.href || '';const ua = query.ua || '';fs.readFile('./anti_content_v2.0.js', 'utf-8', (err, data) => {if (err) {res.writeHead(500, { 'Content-Type': 'text/plain' });res.end('Internal Server Error');return;}const replacedCode = data.replace("{cookie}", cookie).replace("{href}", href).replace("{ua}", ua);eval(replacedCode);const result = get_anti_content();res.writeHead(200, { 'Content-Type': 'application/json' });res.end(JSON.stringify(result));});
});const PORT = 3000;
server.listen(PORT, () => {console.log(`Server running at http://localhost:${PORT}/`);
});

3. 算法还原

这里我们采用的补环境的方式去快速实现算法,通过上面Py动态传递cookie、ua、refer到JS算法中,补环境头如下所示:

window = global;
const random = function (min, max) {return Math.floor(Math.random() * (max - min + 1) + min)
};
window._event_map = new Map;
window.DeviceMotionEvent = class DeviceMotionEvent {
}
window.DeviceOrientationEvent = class DeviceOrientationEvent {
}
window.chrome = {}
window.DeviceMotionEvent = class {constructor() {debugger}
}
window.document = new class {constructor() {this.cookie = "{cookie}";this.onmousewheel = "";}get referrer() {return ""}addEventListener(e, t) {window._event_map.set(e, t)}getElementById() {}get ontouchstart() {return null}get documentElement() {return {scrollTop: 12}}}
window.history = new class {back() {};
}
history.back.toString = e => "function back() { [native code] }"
document.getElementById.toString = e => "native code"
window.localStorage = new class {getItem() {return null}setItem() {}
}
window.location = new class {get href() {return '{href}'}get port() {return ""}
}
window.navigator = new class {hasOwnProperty() {return false}get languages() {return ["zh-CN", "zh"]}get plugins() {return {length: 4}}get userAgent() {return "{ua}"}
}
window.outerHeight = 1040;
window.outerWidth = 1920;
window.screen = new class {get availHeight() {return 1040}get availWidth() {return 1920}
}

接下来扣取完整的JS算法,JS代码的话还是Webpack打包的。依赖eDaA模块。它重新导出了fbeZ模块!然后再8oxB模块导出了一些异步任务调度相关的功能。完整的JS算法结构如下:

!function(e) {function t(t) {for (var n, o, c = t[0], u = t[1], i = t[2], l = 0, d = []; l < c.length; l++)o = c[l],Object.prototype.hasOwnProperty.call(a, o) && a[o] && d.push(a[o][0]),a[o] = 0;for (n in u)Object.prototype.hasOwnProperty.call(u, n) && (e[n] = u[n]);for (s && s(t); d.length; )d.shift()();return f.push.apply(f, i || [])}function r() {for (var e, t = 0; t < f.length; t++) {for (var r = f[t], n = !0, o = 1; o < r.length; o++) {var u = r[o];0 !== a[u] && (n = !1)}n && (f.splice(t--, 1),e = c(c.s = r[0]))}return e}var n = {}, o = {0: 0}, a = {0: 0}, f = [];function c(t) {if (n[t])return n[t].exports;var r = n[t] = {i: t,l: !1,exports: {}}, o = !0;try {e[t].call(r.exports, r, r.exports, c),o = !1} finally {o && delete n[t]}return r.l = !0,r.exports}window._require_ = c;c.e = function(e) {var t = [];o[e] ? t.push(o[e]) : 0 !== o[e] && {8: 1}[e] && t.push(o[e] = new Promise((function(t, r) {for (var n = "static/css/" + ({8: "styles",58: "9f4ea93f",59: "a21cddf3",60: "flvjs"}[e] || e) + "." + {8: "f47ac1ea",58: "31d6cfe0",59: "31d6cfe0",60: "31d6cfe0",139: "31d6cfe0",140: "31d6cfe0"}[e] + ".chunk.css", o = c.p + n, a = document.getElementsByTagName("link"), f = 0; f < a.length; f++) {var u = (l = a[f]).getAttribute("data-href") || l.getAttribute("href");if ("stylesheet" === l.rel && (u === n || u === o))return t()}var i = document.getElementsByTagName("style");for (f = 0; f < i.length; f++) {var l;if ((u = (l = i[f]).getAttribute("data-href")) === n || u === o)return t()}var s = document.createElement("link");s.rel = "stylesheet",s.type = "text/css",s.onload = t,s.onerror = function(t) {var n = t && t.target && t.target.src || o, a = new Error("Loading CSS chunk " + e + " failed.\n(" + n + ")");a.request = n,r(a)},s.href = o,document.getElementsByTagName("head")[0].appendChild(s)})).then((function() {o[e] = 0})));var r = a[e];if (0 !== r)if (r)t.push(r[2]);else {var n = new Promise((function(t, n) {r = a[e] = [t, n]}));t.push(r[2] = n);var f, u = document.createElement("script");u.charset = "utf-8",u.timeout = 120,c.nc && u.setAttribute("nonce", c.nc),u.src = function(e) {return c.p + "static/chunks/" + ({8: "styles",58: "9f4ea93f",59: "a21cddf3",60: "flvjs"}[e] || e) + "." + {8: "4738d83962c38e9c33b5",58: "a953a84ef62f54e12779",59: "4fc845ca7e3eb5a22c62",60: "320dd134ef7f119c4f58",139: "f14f12c553fb100c3cde",140: "82aa74f0d0b2e38b0dbe"}[e] + ".js"}(e);var i = new Error;f = function(t) {u.onerror = u.onload = null,clearTimeout(l);var r = a[e];if (0 !== r) {if (r) {var n = t && ("load" === t.type ? "missing" : t.type), o = t && t.target && t.target.src;i.message = "Loading chunk " + e + " failed.\n(" + n + ": " + o + ")",i.name = "ChunkLoadError",i.type = n,i.request = o,r[1](i)}a[e] = void 0}};var l = setTimeout((function() {f({type: "timeout",target: u})}), 12e4);u.onerror = u.onload = f,document.head.appendChild(u)}return Promise.all(t)},c.m = e,c.c = n,c.d = function(e, t, r) {c.o(e, t) || Object.defineProperty(e, t, {enumerable: !0,get: r})},c.r = function(e) {"undefined" !== typeof Symbol && Symbol.toStringTag && Object.defineProperty(e, Symbol.toStringTag, {value: "Module"}),Object.defineProperty(e, "__esModule", {value: !0})},c.t = function(e, t) {if (1 & t && (e = c(e)),8 & t)return e;if (4 & t && "object" === typeof e && e && e.__esModule)return e;var r = Object.create(null);if (c.r(r),Object.defineProperty(r, "default", {enumerable: !0,value: e}),2 & t && "string" != typeof e)for (var n in e)c.d(r, n, function(t) {return e[t]}.bind(null, n));return r},c.n = function(e) {var t = e && e.__esModule ? function() {return e.default}: function() {return e};return c.d(t, "a", t),t},c.o = function(e, t) {return Object.prototype.hasOwnProperty.call(e, t)},c.p = "",c.oe = function(e) {throw console.error(e),e};var u = window.webpackJsonp = window.webpackJsonp || [], i = u.push.bind(u);u.push = t,u = u.slice();for (var l = 0; l < u.length; l++)t(u[l]);var s = i;"undefined" === typeof window ? global.APP_VERSION = "0b8a7369" : "undefined" === typeof global && (window.APP_VERSION = "0b8a7369")
}({"eDaA": function(t, e, n) {t.exports = n("fbeZ")},"fbeZ": function(t, e, n) {...}.call(this, n("8oxB")) // 算法小千行"8oxB": function(t, e) {var n, r, o = t.exports = {};function i() {throw new Error("setTimeout has not been defined")}function a() {throw new Error("clearTimeout has not been defined")}function s(t) {if (n === setTimeout)return setTimeout(t, 0);if ((n === i || !n) && setTimeout)return n = setTimeout,setTimeout(t, 0);try {return n(t, 0)} catch (e) {try {return n.call(null, t, 0)} catch (e) {return n.call(this, t, 0)}}}!function() {try {n = "function" === typeof setTimeout ? setTimeout : i} catch (t) {n = i}try {r = "function" === typeof clearTimeout ? clearTimeout : a} catch (t) {r = a}}();var l, c = [], u = !1, d = -1;function f() {u && l && (u = !1,l.length ? c = l.concat(c) : d = -1,c.length && p())}function p() {if (!u) {var t = s(f);u = !0;for (var e = c.length; e; ) {for (l = c,c = []; ++d < e; )l && l[d].run();d = -1,e = c.length}l = null,u = !1,function(t) {if (r === clearTimeout)return clearTimeout(t);if ((r === a || !r) && clearTimeout)return r = clearTimeout,clearTimeout(t);try {r(t)} catch (e) {try {return r.call(null, t)} catch (e) {return r.call(this, t)}}}(t)}}function h(t, e) {this.fun = t,this.array = e}function g() {}o.nextTick = function(t) {var e = new Array(arguments.length - 1);if (arguments.length > 1)for (var n = 1; n < arguments.length; n++)e[n - 1] = arguments[n];c.push(new h(t,e)),1 !== c.length || u || s(p)},h.prototype.run = function() {this.fun.apply(null, this.array)},o.title = "browser",o.browser = !0,o.env = {},o.argv = [],o.version = "",o.versions = {},o.on = g,o.addListener = g,o.once = g,o.off = g,o.removeListener = g,o.removeAllListeners = g,o.emit = g,o.prependListener = g,o.prependOnceListener = g,o.listeners = function(t) {return []},o.binding = function(t) {throw new Error("process.binding is not supported")},o.cwd = function() {return "/"},o.chdir = function(t) {throw new Error("process.chdir is not supported")},o.umask = function() {return 0}},
})

JS代码基于动态加载模块的机制。可以通过封装下面的入口函数,window.require被用作模块加载器,require函数的实现是通过上面的c(t)函数完成,它主要负责根据模块的ID加载模块,如下所示:

function get_anti_content() {var j = window._require_.n(window._require_("eDaA"))var e = {serverTime: new Date().getTime()};return new j.a(e).messagePack()
}

相关文章:

使用Python发送PDD直播间弹幕(协议算法分析)

文章目录 1. 写在前面2. 接口分析3. 算法还原 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…...

1056. Mice and Rice (25)-PAT甲级真题

当时没想到可以用队列来做&#xff0c;就傻傻的模拟了&#xff0c;用cur存当前轮的id&#xff0c;这个id对应的是order的下标&#xff0c;这里有个求rank的技巧就是当前轮没有晋级的rank为&#xff08;当前轮的组数1&#xff09; 模拟&#xff1a; #include<bits/stdc.h&g…...

色轮在数据可视化中的应用

在数据可视化中&#xff0c;色彩的运用不仅仅是为了美观&#xff0c;更是为了传达信息、区分数据和提升图表的易读性。本文探讨色轮及其色彩公式的应用&#xff0c;帮助大家更好地运用色彩来提升数据可视化的效果。 1、色轮的基础概念 色轮是一个用于表示颜色之间关系的图形工…...

编程-设计模式 8:组合模式

设计模式 8&#xff1a;组合模式 定义与目的 定义&#xff1a;组合模式又称为部分-整体模式&#xff0c;它允许你将对象组合成树形结构来表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。目的&#xff1a;该模式的主要目的是将多个对象…...

c语言指针(8.11)

那这样p和*p记录的地址不一样了吗&#xff1f; 不&#xff0c;p 和 *p 记录的地址在某种意义上是“相同”的&#xff0c;但它们在类型和使用方式上有所不同。 p 的地址&#xff1a;p 是一个指针&#xff0c;它本身存储了一个地址&#xff0c;这个地址是二维数组 arr 的第一行&a…...

加密技术的发展

加密是一种用于保护数据安全的技术&#xff0c;通过将原始信息&#xff08;明文&#xff09;转换为一种不可读的形式&#xff08;密文&#xff09;&#xff0c;确保只有拥有正确解密密钥的人才能访问其真实内容。加密技术在现代社会中被广泛应用于各种场景&#xff0c;包括但不…...

编程-设计模式 22:策略模式

设计模式 22&#xff1a;策略模式 定义与目的 定义&#xff1a;策略模式定义了一系列算法&#xff0c;并将每一个算法封装起来&#xff0c;使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。目的&#xff1a;该模式的主要目的是将一组相关的算法封装成一系列可…...

kafka 将log4j的项目升级到log4j2

kafka版本是kafka_2.11-2.0.0&#xff0c;由于引用的log4j有漏洞&#xff0c;而升级kafka可能影响比较大&#xff0c;所以更新log4j包的版本。 参考的是将log4j的项目升级到log4j2 主要步骤如下&#xff1a; cd kafka的目录 cd libs rm -f slf4j-log4j12-1.7.25.jar rm -f …...

【CSP2019 模拟赛】Time

题目描述&#xff1a; 小 A 现在有一个长度为 &#x1d45b; 的序列 {&#x1d465;&#x1d456;}&#xff0c;但是小 A 认为这个序列不够优美。 小 A 认为一个序列是优美的&#xff0c;当且仅当存在 &#x1d458; ∈ [1, &#x1d45b;]&#xff0c;满足&#xff1a; &#…...

二叉树相关的算法题

二叉树相关的算法题 单值二叉树 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;[1,1,1,1,1,null,1] 输出&#xff1a;t…...

Unity URP 曲面细分学习笔记

学百人时遇到了曲面着色器的内容&#xff0c;有点糊里糊涂&#xff0c;于是上知乎找到了两篇大佬的文章 Unity URP 曲面细分 和 Unity曲面细分笔记&#xff0c;本文只是自己做学习记录使用 1.曲面细分与镶嵌 曲面细分或细分曲面&#xff08;Subdivision surface&#xff09;是…...

每天五分钟深度学习pytorch:训练神经网络模型的基本步骤

本文重点 本文个人认为是本专栏最重要的章节内容之一,前面我们学习了pytorch中的基本数据tensor,后面我们就将学学习深度学习模型的内容了,在学习之前,我们先来看一下我们使用pytorch框架训练神经网络模型的基本步骤,然后我们下面就将这些步骤分解开来,详细学习。 代码…...

【langchain学习】使用缓存优化langchain中的LLM调用性能:内存、SQLite与Redis的对比

在处理语言模型(LLM)调用时,特别是在需要多次执行相同请求的情况下,缓存机制能够显著提升系统的性能。本文通过对比内存缓存(InMemoryCache)、SQLite缓存(SQLiteCache)和Redis缓存(RedisCache),探讨了如何在Langchain中使用这些缓存机制来优化LLM调用的性能。 代码…...

spring boot 集成EasyExcel

EasyExcel 是一个基于 Java 的快速、简洁的 Excel 处理工具&#xff0c;它能够在不用考虑性能和内存等因素的情况下&#xff0c;快速完成 Excel 的读写功能。 首先&#xff0c;需要在 Spring Boot 项目中引入 EasyExcel 依赖。在 pom.xml 文件中添加以下依赖&#xff1a; <d…...

获取对象中第一个存在的值

在JavaScript中&#xff0c;要从一个对象中获取第一个存在的&#xff08;非undefined、非null、非空数组等&#xff09;值&#xff0c;你可以使用Object.values()方法结合Array.prototype.find()方法。以下是一个示例代码&#xff0c;演示如何实现这一点&#xff1a; const ob…...

Python学习笔记----集合与字典

1. 字符串、列表和元组的元素都是按下标顺序排列&#xff0c;可通过下 标直接访问&#xff0c;这样的数据类型统称为序列。 其中&#xff0c;字符串和元组中的元素不能修改&#xff0c;而列表中的元素可以修改。 集合 1. 与元组和列表类似&#xff0c;Set &#xff08;集合&a…...

c# 排序、强转枚举

List<Tuple<double,int>> mm中doble从小到大排序 mm本身排序 在C#中&#xff0c;如果你有一个List<Tuple<double, int>>类型的集合mm&#xff0c;并且你想要根据Tuple中的double值&#xff08;即第一个元素&#xff09;从小到大进行排序&#xff0c;同…...

“华为杯”第十六届中国研究生数学建模竞赛-C题:视觉情报信息分析

目录 摘 要: 一、问题重述 二、模型假设 三、符号说明 四、问题一分析与求解 4.1 问题一分析 4.2 模型建立 4.2.1 位置变换模型建立 4.2.4 多平面转换模型建立 4.3 模型求解 4.3.1 问题一图 1 结果 4.3.2 问题一图 2 结果 4.3.3 问题一图 3 结果 4.3.4 问题一图 4 结果 4.4 模…...

html+css+js网页设计 找法网2个页面(带js)ui还原度百分之90

htmlcssjs网页设计 找法网2个页面&#xff08;带js&#xff09;ui还原度百分之90 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑…...

018 | backtrader回测反转策略

什么是反转策略&#xff1f; 反转策略&#xff08;Reversal Strategy&#xff09;是一种试图捕捉市场价格趋势逆转的交易策略。与趋势跟随策略不同&#xff0c;反转策略的核心理念是“物极必反”&#xff0c;即价格在经过一段时间的单边趋势后&#xff0c;往往会出现逆转的机会…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

Razor编程中@Html的方法使用大全

文章目录 1. 基础HTML辅助方法1.1 Html.ActionLink()1.2 Html.RouteLink()1.3 Html.Display() / Html.DisplayFor()1.4 Html.Editor() / Html.EditorFor()1.5 Html.Label() / Html.LabelFor()1.6 Html.TextBox() / Html.TextBoxFor() 2. 表单相关辅助方法2.1 Html.BeginForm() …...

STM32---外部32.768K晶振(LSE)无法起振问题

晶振是否起振主要就检查两个1、晶振与MCU是否兼容&#xff1b;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容&#xff08;CL&#xff09;与匹配电容&#xff08;CL1、CL2&#xff09;的关系 2. 如何选择 CL1 和 CL…...