0%

架构

mvc

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
your-project/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/yourname/project/
│ │ │ ├── controller/ # 控制器层,处理HTTP请求
│ │ │ │ └── UserController.java
│ │ │ ├── service/ # 业务逻辑层接口
│ │ │ │ └── UserService.java
│ │ │ ├── service/impl/ # 业务逻辑层实现
│ │ │ │ └── UserServiceImpl.java
│ │ │ ├── dao/ # 数据访问层
│ │ │ │ └── UserDao.java
│ │ │ └── config/ # 配置类(Spring配置)
│ │ │ └── WebConfig.java
│ │ └── resources/
│ │ ├── application.properties # 配置文件
│ │ └── views/ # 视图文件夹(JSP、Thymeleaf等)
│ │ └── user.jsp
└── pom.xml # Maven配置文件
  • Model 负责数据和业务逻辑
  • View 负责界面显示
  • Controller 负责处理请求和协调Model与View
1
2
3
4
5
6
7
用户点击按钮

Controller 接收请求 -> 调用 Model 获取数据

Controller 选择对应 View

View 渲染并返回给用户

拦截器

SpringCloud

远程调用,注册中心,网关都是在单体项目变成分布式项目后对出现的问题的解决.

远程调用:

分布式项目 数据库只能连接一个项目,不同项目之间有耦合, 所以要远程调用

注册中心:分布式项目有多个服务器,端口写死维护困难,所以要注册中心

网关:分布式项目因为有不同项目,前端只发一个json.需要网关创建路由,就知道访问哪里了.

远程调用:拆分成很多单体项目,不同项目之间通过 http请求 完成数据业务交换

截屏2025-10-28 15.06.07.png

Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送。

注册RestTemplate到Spring容器

调用RestTemplate的API发送请求,常见方法有:

getForObject:发送Get请求并返回指定类型对象

PostForObject:发送Post请求并返回指定类型对象

put:发送PUT请求

delete:发送Delete请求

exchange:发送任意类型请求,返回ResponseEntity

image.png

可以用OpenFeign + Nacos简化开发

image.png

连接池(OK HTTP)

因为可能出现不同服务需要同样的Client,导致重复编码,所以可以抽取相同的Client

image.png

注册中心(nacos)

v2-057c9593e1f0a317d1c68b1ba7ecad13_1440w.jpg.png

服务提供方可能不止一个服务器,

这时就需要有一个注册中心为服务消费方找到要调用的端口

远程调用(openfeign)

使用步骤:

在服务消费者里引入 spring-cloud-starter-openfeign。

在启动类加 @EnableFeignClients。

定义一个接口,写上 @FeignClient(name = “服务名”),就能像本地方法一样调用远程服务。

负载均衡

DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用

心跳机制

网关

路由、转发、身份验证

可以从注册中心拉取端口

前端->网关->注册中心->分布式后端

解决前端对微服务难以维护,有了网关,前端就可以像单体式架构一样直接调用网关了

但是!!后端开发仍然需要配置路由规则,让json找到需要的服务

路由

Gateway 收到请求

  • 请求被包装成一个 ServerWebExchange 对象。

    路由匹配阶段

  • Gateway 遍历所有 Route。

  • 调用每个 Route 的 predicate。

  • 这里的 Path=/api/user/** 断言返回 true → 匹配成功。

路由命中

  • Gateway 确定目标 URI 为 lb://user-service。
  • 创建过滤器链(包括全局和局部过滤器)。

执行前置过滤器(pre)

  • StripPrefix=1 会将路径从 /api/user/list 变成 /user/list。
  • 然后继续。

请求被转发

微服务处理并返回响应

  • user-service 返回 JSON 数据。

执行后置过滤器(post)

  • 比如加响应头、记录日志等。

Gateway 将结果返回客户端

路由断言:规定请求是否命中某条路由

路由过滤器:请求转发前后对请求/响应做修改、增强或拦截

image.png

网关登陆校验的原理就是在pre设置一个过滤器,就可以在网关转发之前做校验

自定义过滤器

登录校验

image.png

1.gateway -> 微服务:

修改登录校验拦截器的处理逻辑,保存用户到请求头

hm-common中拦截器保存登录用户,保存到ThreadLocal

拦截器应该只在微服务中,gateway可以用SpringMVC来排除

2.微服务 -> 微服务:

image.png

通过OpenFeign在hm-api的拦截器传递用户信息

image.png

配置管理(Nacos)

微服务重复配置过多,维护成本高

业务配置经常变动,每次修改都要重启

服务网关路由配置写死,如果变更要重启网关

配置共享

image.png

配置热更新

image.png

TODO 动态路由

微服务保护(雪崩)

1.请求限流

2.线程隔离

3.服务熔断

4.失败处理fallback

Sentinel

分布式事务

微服务下,事务回滚无法完成

seata

高并发的解决方法

XA 模式(数据库层两阶段)

事务锁,强一致性

AT 模式(Seata UndoLog)

TCC 模式(业务层补偿)

异步通讯

作用:

1.异步

2.解耦

RabbitMQ

image.png

交换机(Exchange)

Fanout:广播,将消息交给所有绑定到交换机的队列。

Direct:订阅,基于RoutingKey(路由key)发送给订阅了消息的队列

Topic:通配符订阅,与Direct类似,只不过RoutingKey可以使用通配符

Headers:头匹配,基于MQ的消息头匹配,用的较少。

s-pay-mall业务流程

类的静态方法没法设置对象状态.可以用类生成对象,设置对象状态.这就是实例化.

每次都要实例化有 多层嵌套、销毁、多实例 的麻烦.所以IoC通过:

  1. 让类进入容器: 类加@Component,或者配置类加@Bean
  2. 给属性赋值: 基本类型和String 使用 @Value , 其他的 使用 @Autowired (注入方式多用构造函数)
  3. 调用动态方法

此外,IoC的优势有:

IOC 容器是 AOP 的基础.通过容器管理 Bean,可以在 Bean 方法执行前后加增强(事务、日志、权限校验).

如果对象不在容器里,就无法自动代理和增强

登录校验

商品交易

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
graph TD
%% 定义节点样式,模拟字符图的方框感
classDef box fill:#fff,stroke:#333,stroke-width:1px,align:left;

%% 1. 支付宝服务器
Node1["<b>支付宝服务器</b><br/>回调请求: HTTPS/HTTP"]:::box

%% 连接线 1
Node1 -->|"访问公网 IP:8080"| Node2

%% 2. 云服务器 FRPS
Node2["<b>云服务器 FRPS</b><br/>镜像: fatedier/frps<br/> bind_port: 7000 (FRPC 建立隧道)<br/> remotePort: 8080 (公网访问端口)"]:::box

%% 连接线 2
Node2 -->|"TCP 隧道<br/>(remotePort 8080 -> FRPC localPort 8099)"| Node3

%% 3. macOS Docker FRPC
Node3["<b>macOS Docker FRPC</b><br/>镜像: fatedier/frpc<br/>localIP: 10.153.129.175 (宿主机 IP)<br/>localPort: 8099 (SpringBoot 端口)"]:::box

%% 连接线 3
Node3 -->|"访问宿主机 SpringBoot"| Node4

%% 4. 宿主机 SpringBoot
Node4["<b>宿主机 SpringBoot</b><br/>端口: 8099<br/>Docker 映射可选"]:::box

%% 连接线 4
Node4 -->|"返回响应"| Node5

%% 5. 返回路径
Node5["FRPC -> FRPS -> 支付宝"]:::box

具体代码分析

鉴权

请求示例:

1
2
3
4
timestamp = 1000000
nonce = 123456
signature = sha1(token, timestamp, nonce)
echostr = ABC

解码 token在网站设置

1
SignatureUtil.check(token, signature, timestamp, nonce)

为什么需要同时使用 timestamp 与 nonce?

随机数 nonce :保证请求在并发场景下唯一

nonce 的意义在于避免签名冲突。
在实际系统中,同一秒内可能出现大量并发请求。
如果没有 nonce,所有同一秒内的请求会共享相同的 timestamp,导致 signature 可能重复。

因此 nonce 用于确保:

  • 同一秒内的请求也具有不同签名;
  • 内容相同的多个请求仍能被区分。

需要注意的是:
nonce 本身不包含时效性。攻击者可以截获一次合法请求(含 nonce),在之后任意时间重新发送。如果服务器不记录 nonce,将无法分辨这是旧请求。

时间戳 timestamp :判断请求是否在有效期内

即便 nonce 很随机,但无法判断该请求是“刚刚发送的”还是“很久以前的”。
timestamp 则解决了这一问题。

服务器常见的做法是:

  • 若 timestamp 与当前时间差距过大(例如超过五分钟),则认为该请求为过期请求并拒绝处理。

因此 timestamp 的核心作用是防止重放攻击,即阻止攻击者对旧请求进行重复发送。

为什么不能只使用 timestamp

timestamp 精度通常为“秒”。
在实际业务中,同一秒可能出现多条请求。
若只依赖 timestamp,将无法区分同一秒内的多条合法请求,从而可能导致签名冲突。

nonce 的存在就是为了解决这一问题。

  • 公众号的事件推送常出现并发;
  • 同一秒内可能由多名用户触发多条消息;
  • 官方服务器可能批量推送多条记录。

因此,真实环境中无法依赖 timestamp 单独提供区分能力,所以 nonce 必须保留。

登陆

img

开发者设置 Token 固定 验证微信服务器调用你接口的合法性 永久

access_token 会变 调用微信 API(发消息、生成二维码等) 2 小时

1
2
3
4
5
6
7
8
9
private static final String BASE_URL = "https://api.weixin.qq.com/";

@Bean
public Retrofit retrofit(){
return new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(JacksonConverterFactory.create())
.build();
}

这个@Bean的作用是:

把一个配置好的 Retrofit 客户端交给 Spring 容器管理,然后可以在项目中通过依赖注入直接使用。

1
2
@Autowired
private Retrofit retrofit;

实例化:

  • 依赖对象状态(比如 base URL、converter、token 等)。
  • 需要先生成实例,才能调用方法。
  • 调用实例方法时,Retrofit 内部会生成 HTTP 请求并发出去。

问题1:

根据你之前提供的环境:

  • IDEA 2025.1
  • JDK 1.8
  • Maven 3.8.x
  • Lombok 1.18.30

这种组合在某些 JDK 1.8 上仍会触发 TypeTag :: UNKNOWN。原因主要是 Javac 内部版本过旧,无法正确解析枚举/Builder

观察网站

查看源代码只有网站外观设计后,发现F12抓包后名为[practice]的文件内有“label”和”questionBh”两个题目号
练习题目首页
点进题目后再点击查看答案,抓包的文件”data”中就有完整的答案
点进一道题目,并点击参考答案
点开第二次抓包的标头,发现url是http://[一些域名]={questionBh}
点开第二次抓包的标头
我们可以通过 label -> questionBh -> answer

写代码

先进行 label -> questionBh 的步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import requests
import re
import csv

url = f'http://[一些域名]/api/exam/question/practice'

headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0"
}
resp = requests.get(url,headers=headers)

obj = re.compile(r'"label":"((?P<num>.*?))(?P<none>.*?)questionBh":"(?P<web>.*?)"',re.S)
result = obj.finditer(resp.text)

for i in result:
print(i.group("num","web"))

resp.close()

再对得到的格式进行一些调整,并变成csv格式,示例如下

1
2
3
num,questionBh
5,02ac8e3a76404d668bea36a31c90fc7b
*,********************************

再进行 questionBh -> answer 的步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
import re
import csv


query= input("输入你想搜索的东西\n")
url = f'http://[一些域名]/api/exam/question/getAnswer?questionBh={query}&studentId=2024011666'

headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0"
}
resp = requests.get(url,headers=headers)

obj = re.compile(r'"data":"(?P<ans>.*?)"}',re.S)
result = obj.finditer(resp.text)


整合代码,通过csv文件串联起来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import csv
import requests
import re


def load_mapping(file_path):
mapping = {}
with open(file_path, mode='r', encoding='utf-16be') as file:
reader = csv.reader(file)
next(reader)
for row in reader:
num, questionBh = row
mapping[num] = questionBh
return mapping

def get_question_bh(num, mapping):
return mapping.get(num, "Not Found")


file_path = 'mapping.csv'

k = input("输入题号:")


mapping = load_mapping(file_path)

question_bh = get_question_bh(k, mapping)
print(f"生成的 questionBh: {question_bh}")

k = f"{question_bh}"


url = f'http://[一些域名]/api/exam/question/getAnswer?questionBh={k}'

headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0"
}
resp = requests.get(url,headers=headers)

obj = re.compile(r'"data":"(?P<ans>.*?)"}',re.S)
result = obj.finditer(resp.text)

使用 ast 解析转义字符

笔者在学习中发现,使用 ast.literal_eval() 解析转义字符可以消除多余字符的影响,省去了复制到ai转换的麻烦

1
2
3
4
5
6
7
8
9
10
11
ans_list = [i.group("ans") for i in result]

# 将列表中的所有内容连接成一个字符串
a = ''.join(ans_list)

# 使用 ast.literal_eval() 安全地解析转义字符
escaped_text = ast.literal_eval(f'"""{a}"""')
print(escaped_text)


resp.close()

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import csv
import requests
import re
import ast

def load_mapping(file_path):
mapping = {}
with open(file_path, mode='r', encoding='utf-16be') as file:
reader = csv.reader(file)
next(reader) # 跳过标题行
for row in reader:
num, questionBh = row
mapping[num] = questionBh
return mapping

def get_question_bh(num, mapping):
return mapping.get(num, "Not Found")

# 示例输入
file_path = 'mapping.csv'

k = input("输入题号:")

# 加载对应关系
mapping = load_mapping(file_path)

# 获取对应的 questionBh
question_bh = get_question_bh(k, mapping)
print(f"生成的 questionBh: {question_bh}")

k = f"{question_bh}"

url = f'http://[一些域名]//api/exam/question/getAnswer?questionBh={k}'

headers = {
"user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0"
}
resp = requests.get(url,headers=headers)

obj = re.compile(r'"data":"(?P<ans>.*?)"}',re.S)
result = obj.finditer(resp.text)

ans_list = [i.group("ans") for i in result]

# 将列表中的所有内容连接成一个字符串
a = ''.join(ans_list)

# 使用 ast.literal_eval() 安全地解析转义字符
escaped_text = ast.literal_eval(f'"""{a}"""')
print(escaped_text)

resp.close()

结果

生成结果如下图,经过修改后可以直接复制到提交窗口使用啦
生成结果

参考

[bilibili]Python爬虫教程 通过视频,我学习了抓包方法,re,正则表达式,最简单的通信原理
在线正则表达式测试

基于 requests 的 HTTP 协议逆向方案,直接调用后台接口。

1. 数据加载模块 (data_loader.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import json
from typing import List, Dict, Any

class DataLoader:
"""
负责从文件加载和验证数据 [cite: 137]
"""
def __init__(self, file_path: str):
self.file_path = file_path

def load_data(self) -> List[Dict[str, Any]]:
"""
加载并返回原始数据列表 [cite: 137]
"""
try:
with open(self.file_path, 'r', encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
print(f"❌ 错误:文件未找到,请确保 '{self.file_path}' 存在。")
return []
except json.JSONDecodeError:
print(f"❌ 错误:'{self.file_path}' 文件不是有效的 JSON 格式。")
return []
except Exception as e:
print(f"❌ 错误:加载文件时发生未知错误: {e}")
return []

def filter_valid_questions(self, data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
过滤出状态为 200 且包含详情的有效题目 [cite: 139]
"""
return [
item for item in data
if item.get('status') == 200 and item.get('detail')
]

2. 业务逻辑模块 (question_service.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
from typing import List, Dict, Any

class QuestionService:
"""
提供题目查询和格式化服务 [cite: 153]
"""

def __init__(self, questions: List[Dict[str, Any]]):
self.questions = questions

def format_question_output(self, q_data: Dict[str, Any], show_answer: bool = False) -> str:
"""格式化单个题目输出 [cite: 153]"""
question = q_data['detail']['question']
knowledge = q_data['detail'].get('knowledgePoint', {})
test_cases = q_data['detail'].get('questionTestList', [])

output = [
"=" * 60,
f"ID: {question['id']}",
f"标题: {question['title']}",
f"类型: {question['type']} | 难度: {question['difficulty']}",
f"知识点: {knowledge.get('name', 'N/A')}",
"-" * 60,
"📝 题目内容:",
question['content'].strip(),
f"\n测试用例 ({len(test_cases)}个):"
]

for idx, case in enumerate(test_cases[:3]):
output.append(f" - 输入: {case.get('input', '无')} | 预期输出: {case.get('output', '无')}")

if show_answer:
output.append("\n💡 标准答案:")
output.append(question['answer'].strip())

output.append("=" * 60)
return "\n".join(output)

def find_by_id(self, question_id: str) -> Dict[str, Any]:
for item in self.questions:
if item['id'] == question_id:
return item
return None

def find_by_type(self, q_type: str) -> List[Dict[str, Any]]:
return [
item for item in self.questions
if item['detail']['question']['type'] == q_type
]

def find_by_difficulty(self, difficulty: str) -> List[Dict[str, Any]]:
return [
item for item in self.questions
if item['detail']['question']['difficulty'] == difficulty
]

def search_by_keyword(self, keyword: str) -> List[Dict[str, Any]]:
keyword = keyword.lower()
results = []
for item in self.questions:
question = item['detail']['question']
title = question['title'].lower()
content = question['content'].lower()
if keyword in title or keyword in content:
results.append(item)
return results

def get_statistics(self) -> tuple:
"""返回(类型集合, 难度集合) [cite: 159]"""
types = set()
difficulties = set()
for item in self.questions:
question = item['detail']['question']
types.add(question['type'])
difficulties.add(question['difficulty'])
return sorted(list(types)), sorted(list(difficulties))

3. 主程序入口 (main.py)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
from data_loader import DataLoader
from question_service import QuestionService

class QuestionAnalyzerApp:
def __init__(self, file_path: str):
# 1. 加载数据
loader = DataLoader(file_path)
raw_data = loader.load_data()
valid_questions = loader.filter_valid_questions(raw_data)

print(f"✅ 数据加载成功。找到 {len(raw_data)} 条记录,其中 {len(valid_questions)} 条有效。")

# 2. 初始化服务
self.service = QuestionService(valid_questions)

def run(self):
if not self.service.questions:
print("🛑 数据集为空,程序退出。")
return

while True:
self._print_menu()
choice = input("请输入操作编号 (1-6): ").strip()

if choice == '1':
self._handle_find_by_id()
elif choice == '2':
self._handle_statistics()
elif choice == '3':
self._handle_find_by_type()
elif choice == '4':
self._handle_find_by_difficulty()
elif choice == '5':
self._handle_search()
elif choice == '6':
print("👋 感谢使用,程序已退出。")
break
else:
print("⚠️ 无效的输入,请重新输入编号 (1-6)。")

input("\n按 Enter 键继续...")

def _print_menu(self):
print("\n" + "=" * 50)
print("📚 题目数据分析器 v3.0 (Modular) - 请选择操作:")
print("=" * 50)
print("1. 🔍 按 ID 查找题目和答案")
print("2. 📊 统计与列出所有类型/难度")
print("3. 🔢 按题目类型查找 (预览)")
print("4. ⭐ 按题目难度查找 (预览)")
print("5. 🔎 关键字搜索 (标题/内容)")
print("6. ❌ 退出程序")
print("-" * 50)

def _handle_find_by_id(self):
q_id = input("请输入要查找的题目 ID: ").strip()
show_ans = input("是否显示标准答案? (y/n): ").lower() == 'y'
item = self.service.find_by_id(q_id)
if item:
print(f"\n🔍 找到题目 (ID: {q_id}):")
print(self.service.format_question_output(item, show_ans))
else:
print(f"❌ 未找到 ID 为 '{q_id}' 的题目。")

def _handle_statistics(self):
types, difficulties = self.service.get_statistics()
print("\n📊 数据集统计:")
print(f" - 所有题目类型: {', '.join(types)}")
print(f" - 所有题目难度: {', '.join(difficulties)}")

def _handle_find_by_type(self):
q_type = input("请输入要查找的题目类型: ").strip()
self._search_and_print(self.service.find_by_type, q_type, f"类型为 '{q_type}'")

def _handle_find_by_difficulty(self):
difficulty = input("请输入要查找的题目难度: ").strip()
self._search_and_print(self.service.find_by_difficulty, difficulty, f"难度为 '{difficulty}'")

def _handle_search(self):
keyword = input("请输入要搜索的关键字: ").strip()
self._search_and_print(self.service.search_by_keyword, keyword, f"包含关键字 '{keyword}'")

def _search_and_print(self, func, arg, desc):
limit = input("请输入限制显示的数量 (默认5): ")
limit = int(limit) if limit.isdigit() else 5

results = func(arg)
print(f"\n🔍 找到 {len(results)} 个{desc}的题目。显示前 {min(len(results), limit)} 个。")

for item in results[:limit]:
print(self.service.format_question_output(item, show_answer=False))

if __name__ == "__main__":
app = QuestionAnalyzerApp('ans.json')
app.run()

get

import requests
query= input(“输入你想搜索的东西\n”)
url = f’https://www.sogou.com/web?query={query}

headers = {
“user-agent”:”Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0”
}
resp = requests.get(url,headers=headers)

print(resp)
print(resp.text)
resp.close()

post

import requests

url = “https://fanyi.baidu.com/sug

word=input(“输入想翻译的单词”)
dat={
“kw”:word
}

resp = requests.post(url,data=dat)
print(resp.json())
resp.close()

re

import re
import csv

findall 很小的文件可以用

lst=re.findall(r”\d+”,”my10086”)
print(lst)

finditer 迭代器?最常用

it=re.finditer(r”\d+”,”my10086”)
for i in it:
print(i.group())

search只找到一个结果就返回

#search返回是match对象,拿数据要.group()
lst=re.search(r”\d+”,”my10086”)
print(lst.group())

match必须从头匹配

s = re.match(r”\d+”,”my10086”)
print(s.group())

预加载正则表达式

obj = re.compile(r”\d+”)
it = obj.finditer(“1234”)
for i in it:
print(i.group())

it = obj.finditer(“5678”)
for i in it:
print(i.group())

查找限定组

obj = re.compile(“xxx.?xxx(?P.?)”,re.S)
result = obj.finditer(s)
#取所有组
for i in result:
print(i.group())
#取某个组 #(?P正则)
for i in result:
print(i.group(“name”))

感谢在搭建blog时参考的文章

(zhihu)Hexo+Next主题搭建个人博客+优化全过程
(个人)hexo 博客安装教程 胎教级
(github)live2d models
(个人)Hexo NexT 魔改系列之二 ── 搜索篇
(个人)一次完整的Hexo写作流程
(贴吧)本地访问正常但部署到GitHub上访问就出问题
(cnds)Hexo 搭建个人博客(八)NexT 侧边栏配置
(cnds)Hexo-Next 主题博客个性化配置超详细,超全面(两万字)
(个人)一次完整的Hexo写作流程
(个人)Hexo NexT 魔改系列之二 ── 搜索篇
(个人)Hexo+Next 搭建个人博客 (添加网页音乐播放器)

今后的想完成的美化

暂无