当前位置: 首页 > 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;往往会出现逆转的机会…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

【前端异常】JavaScript错误处理:分析 Uncaught (in promise) error

在前端开发中&#xff0c;JavaScript 异常是不可避免的。随着现代前端应用越来越多地使用异步操作&#xff08;如 Promise、async/await 等&#xff09;&#xff0c;开发者常常会遇到 Uncaught (in promise) error 错误。这个错误是由于未正确处理 Promise 的拒绝&#xff08;r…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)

旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据&#xff01;该数据集源自2025年4月发表于《地理学报》的论文成果…...