我的博客

leetcode 最多可以参加的会议数目 1353. Maximum Number of Events That Can Be Attended

目录
  1. 方法
  2. 代码

英文题目 中文题目

方法

贪心算法。使用优先队列,先从最小的一天开始安排。

策略是按照开始日期分组,每一组(也就是每天)只取一个结束时间最早的(也就是会议长度最短的)。然后这把一组其他的所有其他会议的开始时间修改为下一天,放入优先队列排序。

大致是在这样,但是可以有一些小的优化,比如结束日期就是当前处理的一天的会议就可以直接安排上。因为这个会议安排上一定不会影响后面别的会。

整体的贪心策略就是尽可能安排这一天开一个结束日期最早的会。

代码

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
import queue

class Solution:
def maxEvents(self, events: List[List[int]]) -> int:
q = queue.PriorityQueue()
for e in events:
q.put(e)
e = q.get()
cnt = 1
cd = e[0] + 1
while not q.empty():
e = q.get()
if e[0] == cd:
if e[1] >= cd:
cd += 1
cnt += 1
elif e[0] > cd:
cd = e[0] + 1
cnt += 1
else: # e[0] < cd
if e[1] == cd:
cd += 1
cnt += 1
if e[1] > cd:
e[0] = cd
q.put(e)
return cnt

leetcode 最后 K 个数的乘积 1352. Product of the Last K Numbers

目录
  1. 方法
  2. 代码

英文题目

中文题目

方法

第一可能查询的次数很多,区间也可能很大,如果每次查询都现算可能超时。但是每个数字都不大。

这时候就考虑不存数字本身,而是存连乘后的结果。例如输入的 a b c d。则存的是 a,a×b,a×b×c,a×b×c×d。这样。如果查询最后两个数相乘,就是倒数第一除以倒数第三就是 (a×b×c×d)/ (a×b)= c × d。这样每次查询只需要做一次除法,每次输入只需要做一次乘法。

用 python 的一个优势是,不用考虑整数溢出的问题。

这里还有一个问题没有处理,就是 0。当时没考虑到,但是样例救了我,跑样例除了除零错误。

对于 0 的思路是,每次遇到 0 直接清空列表,因为前面是啥都没用了,只要超过 0 的位置,前面的都是 0 了。

而相应的查询的地方,长度超过列表长度,直接返回 0,因为面前肯定是遇到 0 了。

再一个为了代码方便,直接列表第一个默认自带一个 1。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class ProductOfNumbers:

def __init__(self):
self.p = [1]

def add(self, num: int) -> None:
if num == 0:
self.p = [1]
else:
self.p.append(self.p[-1] * num)

def getProduct(self, k: int) -> int:
if k >= len(self.p): return 0
return self.p[-1] // self.p[-k-1]

leetcode 统计有序矩阵中的负数 1351. Count Negative Numbers in a Sorted Matrix

目录
  1. 方法
  2. 代码

英文题目 中文

方法

可以行、列都从后往前遍历,遇到非负数就 break。

但是这个数据很少,应该全部遍历也不会超时。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def countNegatives(self, grid: List[List[int]]) -> int:
n = len(grid)
m = len(grid[0])
c = 0
for i in range(n-1, -1, -1):
if grid[i][-1] >= 0: break
for j in range(m-1, -1, -1):
if grid[i][j] < 0:
c += 1
else:
break
return c

awk 随机抽样文件

目录
  1. 随机编号再排序
  2. 随机抽取

随机编号再排序

1
awk 'BEGIN{srand()} {print rand()"\t"$0}' input_file | sort -nk 1 | head -n line_num | awk -F "\t" '{print $2}'

随机抽取

1
2
3
4
#!/bin/bash
IN_FILE=$1
LINE_NUM=$2
awk -vN=${LINE_NUM} -vC="`wc -l ${IN_FILE}`" 'BEGIN{srand();while(n<N){i=int(rand()*C+1);if(!(i in a)){a[i]++;n++}}}NR in a' ${IN_FILE}

使用 awk、grep 等命令简单分析服务器 auth 日志 查找 ssh 暴力攻击

目录
  1. 简单分析日志内容
    1. 日志条数
    2. 查看所有密码认证成功记录
    3. 查看所有 auth 日志文件
    4. 使用 awk 简单汇总信息
    5. 把日志文件放到一起并解压
    6. 统计攻击者 ip
      1. 按攻击次数排序
      2. 保存到文件
      3. 查看攻击者 ip 地理位置
    7. 攻击者尝试的用户名
      1. 保存到文件
  2. 关闭 ssh 密码登录
  3. 更多细节
    1. 查看日志位置
      1. ubuntu 18.04
      2. CentOS 7.6
    2. 用到的命令简介
      1. awk
      2. sort

我们有一台服务器在公网上,密码比较弱,我记得一配好就关闭了 ssh 密码登录了,结果昨天偶然发现密码登录居然还开着。赶紧看了一下系统认证日志,发现已有十几万次错误的密码尝试了。

记录一下过程。

我这里是 ubuntu 系统 auth 日志默认在 /var/log/auth.log(有的系统默认在 /var/log/secure 但是格式可能不一样,想确定具体位置看文末更多细节)

还有文章提到的一些命令的使用方法简介也在文末更多细节中

简单分析日志内容

日志条数

1
wc -l /var/log/auth.log

66915 /var/log/auth.log

这里有 6 万多条记录。

1
grep "Failed password" /var/log/auth.log | wc -l

19128

密码验证失败有 19128 次

查看所有密码认证成功记录

1
grep "password" /var/log/auth.log | grep -v Failed | grep -v Invalid

Feb 14 09:59:50 server sshd[19695]: Accepted password for xxx from 59.xx.xx.xx port 40932 ssh2
Feb 14 10:00:32 server sshd[19929]: Accepted password for xxx from 59.xx.xx.xx port 40934 ssh2
Feb 14 10:07:29 server sshd[20121]: Accepted password for xxx from 59.xx.xx.xx port 40948 ssh2

只有三条都是我自己的 ip,说明最近应该没有别人登录成功过。

查看所有 auth 日志文件

1
ls /var/log/auth.log* -lht
-rw-r----- 1 syslog adm 7.5M Feb 15 09:40 /var/log/auth.log
-rw-r----- 1 syslog adm 7.6M Feb 10 14:25 /var/log/auth.log.1
-rw-r----- 1 syslog adm 1.1M Feb  2 14:25 /var/log/auth.log.2.gz
-rw-r----- 1 syslog adm 1.6M Jan 27 14:25 /var/log/auth.log.3.gz
-rw-r----- 1 syslog adm 927K Jan 19 14:25 /var/log/auth.log.4.gz

可以看到系统目前产生了 5 个日志文件了。刚刚看的 auth.log 文件只是最近的日志。

可以把这几个文件放到一起分析所有日志的内容。

使用 awk 简单汇总信息

方法比较朴素,先看按空白分割每一列是什么内容:

1
cat  /var/log/auth.log| grep "Failed password"|head -1 | awk '{while($i){print i, $i;i++}}'

Feb 10 06:25:02 server sshd[3792]: Failed password for root from 118.xx.xx.xx port 49074 ssh2
1 Feb
2 10
3 06:25:02
4 server
5 sshd[3792]:
6 Failed
7 password
8 for
9 root
10 from
11 118.25.39.242
12 port
13 49074
14 ssh2

1
cat  /var/log/auth.log| grep "Failed password"| grep invalid |head -1 | awk '{while($i){print i, $i;i++}}'

Feb 10 06:25:19 server sshd[3936]: Failed password for invalid user ubuntu from 118.xx.xx.xx port 49842 ssh2
1 Feb
2 10
3 06:25:19
4 server
5 sshd[3936]:
6 Failed
7 password
8 for
9 invalid
10 user
11 ubuntu
12 from
13 118.25.39.242
14 port
15 49842
16 ssh2

第 11 列是 ip,第 9 列是攻击者尝试的用户名。

把日志文件放到一起并解压

1
2
3
4
mkdir authlog
cd authlog
cp /var/log/auth.log* .
gunzip auth.log.*.gz

统计攻击者 ip

1
awk '{if($6=="Failed"&&$7=="password"){if($9=="invalid"){ips[$13]++;users[$11]++}else{users[$9]++;ips[$11]++}}}END{for(ip in ips){print ip, ips[ip]}}' auth.* | wc -l

3298

一共有 3298 个 ip 扫过这个服务器

按攻击次数排序

1
awk '{if($6=="Failed"&&$7=="password"){if($9=="invalid"){ips[$13]++;users[$11]++}else{users[$9]++;ips[$11]++}}}END{for(ip in ips){print ip, ips[ip]}}' auth.* | sort -k2 -rn | head

118.201.74.xx 23755
118.27.3.xx 11878
118.25.41.xx 11878
118.25.208.xx 11878
118.25.20.xx 11878
118.25.26.xx 11877
118.89.16.xx 11876
118.24.121.xx 10905
111.13.139.xx 5702
118.25.39.xx 5065

最多的一个 ip 攻击了 23755 次

保存到文件

1
awk '{if($6=="Failed"&&$7=="password"){if($9=="invalid"){ips[$13]++;users[$11]++}else{users[$9]++;ips[$11]++}}}END{for(ip in ips){print ip, ips[ip]}}' auth.* | sort -k2 -rn > ip.log

查看攻击者 ip 地理位置

这里我们使用了 ipip.net 的免费 api。ipip.net 很好用。(这免费 api 有调用次数限制)

看刚刚保存的 ip.log 文件里前 10 条

1
head -10 ip.log | awk '{print $1" ";system("curl http://freeapi.ipip.net/"$1);print("\n")}'

118.201.74.xxx
[“新加坡”,”新加坡”,””,””,”singtel.com”]

118.27.3.xxx
[“日本”,”东京都”,”东京”,””,”gmo.jp”]

118.25.41.xxx
[“中国”,”上海”,”上海”,””,”电信/联通/移动”]

118.25.208.xxx
[“中国”,”四川”,”成都”,””,”电信/联通/移动”]

118.25.20.xxx
[“中国”,”上海”,”上海”,””,”电信/联通/移动”]

118.25.26.xxx
[“中国”,”上海”,”上海”,””,”电信/联通/移动”]

118.89.16.xxx
[“中国”,”广东”,”广州”,””,”电信/联通/移动”]

118.24.121.xxx
[“中国”,”四川”,”成都”,””,”电信/联通/移动”]

111.13.139.xxx
[“中国”,”北京”,”北京”,””,”移动”]

118.25.39.xxx
[“中国”,”上海”,”上海”,””,”电信/联通/移动”]

攻击者尝试的用户名

查看条数

1
awk '{if($6=="Failed"&&$7=="password"){if($9=="invalid"){ips[$13]++;users[$11]++}else{users[$9]++;ips[$11]++}}}END{for(user in users){print user, users[user]}}' auth.* | sort -k2 -rn |wc -l

5627

它们试了 5000 多个用户名

1
awk '{if($6=="Failed"&&$7=="password"){if($9=="invalid"){ips[$13]++;users[$11]++}else{users[$9]++;ips[$11]++}}}END{for(user in users){print user, users[user]}}' auth.* | sort -k2 -rn | head

root 120711
admin 1443
test 426
user 397
postgres 245
oracle 241
ubuntu 221
guest 211
nagios 207
git 191

其中 root 试了 12 万次。但是我的 root 是 ubuntu 默认的随机密码,我就没有动过,我自己都不知道密码是啥。

而 admin 和 test 这些用户我的系统里都不存在。所以他绝大多数努力都是无用功。

保存到文件

1
awk '{if($6=="Failed"&&$7=="password"){if($9=="invalid"){ips[$13]++;users[$11]++}else{users[$9]++;ips[$11]++}}}END{for(user in users){print user, users[user]}}' auth.* | sort -k2 -rn > username.log

关闭 ssh 密码登录

vi /etc/ssh/sshd_config

找到

#PasswordAuthentication yes

改成

PasswordAuthentication no

然后重启 sshd

service sshdrestart (systemd 的命令是 systemctl reload sshd

更多细节

查看日志位置

一般来说 ubuntu 在 /var/log/auth.log centos 在 /var/log/secure 这两者格式不一样。

具体在什么位置是在 rsyslog 的配置文件里:/etc/rsyslog.conf 或者 /etc/rsyslog.d (当然你的系统必须是用 rsyslog 的才行,如果不是用这个就不在这里,使用命令 ps aux | grep "rsyslog" | grep -v "grep" 看系统里有没有 rsyslog 进程可以判断系统是否使用 rsyslog)

ubuntu 18.04

auth,authpriv.* /var/log/auth.log
具体位置是 /etc/rsyslog.d/50-default.conf

CentOS 7.6

# The authpriv file has restricted access.
authpriv.* /var/log/secure
具体位置是 /etc/rsyslog.conf

用到的命令简介

awk

强大的流编辑工具,有自己的一套语言。(功能强大,比 python 更加简洁)

system 函数用于在 awk 中执行 shell 命令。

我收集的一些常用的脚本:

awk输出两个文件不同的行

awk 随机抽样文件

sort

-n 作为数字进行排序

-k 指定排序的列

-r 参数倒序

另外还有

-t 指定分隔符,如果要指定 \t 可用 sort -t $'\t' 或者 -t'\<ctrl>v\<tab>'

-u 排序后去重

LAMBADA 数据集简介

目录
  1. 摘要

该数据集在 2016 年的论文 The LAMBADA dataset: Word prediction requiring a broad discourse context 中被提出。

摘要

我们引入LAMBADA,一个数据集,通过单词预测任务来评估用于文本理解的计算模型的能力。LAMBADA是一组叙述性段落,具有这样一个特点:如果人们接触到整个段落,他们能够猜出最后一个单词,但如果他们只看到目标单词前面的最后一个句子,就猜不出最后一个单词。要在LAMBADA上取得成功,计算模型不能简单地依赖于本地上下文,而必须能够在更广泛的讨论中跟踪信息。我们表明,LAMBADA代表了广泛的语言现象,并且在这个新颖的基准上,几种最先进的语言模型都没有达到1%以上的准确率。因此,我们提出LAMBADA作为一个具有挑战性的测试集,旨在鼓励开发能够真正理解自然语言文本中广泛语境的新模型。

论文地址:

https://arxiv.org/abs/1606.06031v1

http://aclweb.org/anthology/P/P16/P16-1144.pdf

http://www.paperweekly.site/papers/488

Leetcode 87. 扰乱字符串 scramble string

目录
  1. 解题思路
    1. 初始化
    2. 状态转移方程
  2. 代码

https://leetcode-cn.com/problems/scramble-string/

解题思路

dp[i][j][length] 代表 s1 从下标 i 开始,s2 从下标 j 开始,长度为 length 的两个字串是否可以通过扰乱得到,如果可以,值为 True
例如:

1
2
s1 = 'abcde'
s2 = 'deabc'

dp[0][2][3] 的值为 True。指的是 s1 的前三个字符扰乱后可以变成 s2 的后三个字符。

初始化

所有 length 为 1 的情况必须是那个字符相等。
所以初始化:dp 先初始化为全 False,然后两两比较 s1 s2 的每个字符,相等的赋值为 True。

1
2
3
4
5
6
7
dp = [[None for _ in range(l)] for _ in range(l)]
for i in range(l):
for j in range(l):
dp[i][j] = [False] * (min(l-i, l-j)+1)
for i in range(l):
for j in range(l):
dp[i][j][1] = s1[i] == s2[j]

状态转移方程

对与两个字符串是否可以通过扰乱互相转换,我们可以一层一层的看,最高层就是整个字符串,最底层是字符。每一层都可以把这个串拆成两个子串。

对于每一层可以归纳为两种情况:一是当前层的两个子串经过交换,第二种当前层未经过交换。

对于这每一种情况,都要检查每一种子串划分的方法

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def isScramble(self, s1: str, s2: str) -> bool:
l = len(s1)
if l != len(s2): return False
dp = [[None for _ in range(l)] for _ in range(l)]
for i in range(l):
for j in range(l):
dp[i][j] = [False] * (min(l-i, l-j)+1)
for i in range(l):
for j in range(l):
dp[i][j][1] = s1[i] == s2[j]
for length in range(2, l+1):
for i in range(l-length+1):
for j in range(l-length+1):
for sep in range(1, length): # 检查每一种子串划分的方法, sep 是分割的点
if dp[i][j][sep] and dp[i+sep][j+sep][length - sep]: # 当前层两个子串未经过交换的情况
dp[i][j][length] = True
break
if dp[i][j+length-sep][sep] and dp[i+sep][j][length-sep]: # 当前层的两个子串经过交换的情况
dp[i][j][length] = True
break
return dp[0][0][l]

Leetcode 5335. 参加考试的最大学生数 maximum students taking exam - 网络流解法

目录
  1. 解题思路
    1. 给每个点编号和与汇点、源点建立边的代码
    2. 找反向边
    3. 求最大流
  2. 代码

https://leetcode-cn.com/contest/weekly-contest-175/problems/maximum-students-taking-exam/

之前写过本题目状态压缩 dp 解法
状态压缩 dp 首先要检测 2^m 个状态是否合法,然后每一行在这些合法的状态中枚举出最佳解。时间复杂度高。
看到题解中有网络流解法原题解是 c++ 版,我写了 python 版。

解题思路

原方法使用了 Dinic 算法,我这用的更简单的 Ford-Fulkerson 算法。

然后建图的时候 python 会更加灵活,基本方法是:
额外定义一个源点,一个汇点。我的代码里给每个点编号了,源点固定是 0,汇点固定是 1。第一个 for 循环里会给图中,每个可以用的座椅从 2 开始编号。(cur 变量初始化为 2),然后在源点到所有偶数列的点连边。所有奇数列的点到汇点连边。(注意这个边是有向的)

image.png

给每个点编号和与汇点、源点建立边的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
edges = [[], []]
s = 0
t = 1
cur = 2
for i in range(n):
for j in range(m):
if seats[i][j] == '.':
edges.append([])
if j & 1 == 1: # 偶数
edges[s].append(cur)
else:
edges[cur].append(t)
seats[i][j] = cur
cur += 1

编号前:

1
2
3
["#",".","#","#",".","#"]
[".","#","#","#","#","."]
["#",".","#","#",".","#"]

编号后:

1
2
3
['#', 2, '#', '#', 3, '#']
[4, '#', '#', '#', '#', 5]
['#', 6, '#', '#', 7, '#']

编号完成并把源点连偶数列,奇数列连汇点,这个已经是一个二分图了。图是不联通的。
然后我们再把冲突(就是能看到别人答案的)座位两两连接,但是这个和谁能抄谁的没有关系,而必须是偶数列连接到奇数列。

简化以后的图是这样
image.png

找反向边

虽然边是单向的,但是算法中有两种操作:

  1. 增大边上的流(从一个点走正向边到另一个点)
  2. 减少边上的流(从一个点沿着反向边到另一个点)

所以还要把反向边找出来

代码很简单,就是把 edges 里的边颠倒过来。

1
2
3
4
5
6
edges2 = [[] for i in range(len(edges))]
for s, e in enumerate(edges):
if s == 0: continue
for i in e:
if i == 1: continue
edges2[i].append(s)

求最大流

一个 dfs 函数。每次寻找一条增广路径,直到找不到增广路径。

增广路径 就是一条从 s(编号 0) 到 t(编号 1) 的简单路径(简单路径就是无环)。
所以定义一个 vis 数组,标记是否到过某点,防止出现环。

1
vis = [False] * len(edges)

因为这里每条边的流量都是 1。所以我们只定义一个二维矩阵,可以表示任意两个点之间现在是否有流量。

1
flag = [[False] * len(edges) for _ in edges]

flag[i][j] 为 True 代表 i 到 j 有流量(当然得现有边才行),有流量就不能正向走,但可以反向走。没有流量则相反。

最后答案是 可用的座位数 - 最大流

代码

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
class Solution:
def maxStudents(self, seats: List[List[str]]) -> int:
n = len(seats)
m = len(seats[0])
edges = [[], []]
s = 0
t = 1
cur = 2
for i in range(n):
for j in range(m):
if seats[i][j] == '.':
edges.append([])
if j & 1 == 1: # 偶数
edges[s].append(cur)
else:
edges[cur].append(t)
seats[i][j] = cur
cur += 1
if cur == 2: return 0
for i in range(n):
for j in range(m):
if seats[i][j] != '#':
cur = seats[i][j]
if j + 1 < m and seats[i][j+1] != '#':
if j & 1 == 1:
edges[cur].append(seats[i][j+1])
else:
edges[seats[i][j+1]].append(cur)
if i > 0:
if j + 1 < m and seats[i-1][j+1] != '#':
if j & 1 == 1:
edges[cur].append(seats[i-1][j+1])
else:
edges[seats[i-1][j+1]].append(cur)
if j - 1 >= 0 and seats[i-1][j-1] != '#':
if j & 1 == 1:
edges[cur].append(seats[i-1][j-1])
else:
edges[seats[i-1][j-1]].append(cur)
#for e in enumerate(edges): print(e)
#for s in seats: print(s)
edges2 = [[] for i in range(len(edges))]
for s, e in enumerate(edges):
if s == 0: continue
for i in e:
if i == 1: continue
edges2[i].append(s)

flag = [[False] * len(edges) for _ in edges]
vis = [False] * len(edges)
p = []
ans = 0
def dfs(i):
#print(i)
if i == 1:
print('\t', p)
return True
for j, e in enumerate(edges[i]):
if vis[e]: continue
vis[e] = True
if flag[i][e] == False:
flag[i][e] = True
p.append(e)
r = dfs(e)
p.pop()
if r:
vis[e] = False
return r
flag[i][e] = False
vis[e] = False
for j, e in enumerate(edges2[i]):
if vis[e]: continue
vis[e] = True
# print(j, i)
if flag[e][i] == True:
flag[e][i] = False
p.append(e)
r = dfs(e)
p.pop()
if r:
vis[e] = False
return r
flag[e][i] = True
vis[e] = False
return False

while dfs(0):
cur -= 1
return cur - 1

hexo 标题特殊符号引发错误(冒号、引号、大括号)

目录

上一篇文章标题有一个 json 字典,结果报错了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ERROR Process failed: _posts/微信公众号开发错误解决-发送模板消息.md
YAMLException: incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line at line 1, column 94:
... ot;:&quot;data format error hint: [xrlw6a07614125]&quot;} 发送模板消息
^
at generateError (/home/xxxxxxx/MyBlog/node_modules/js-yaml/lib/js-yaml/loader.js:167:10)
at throwError (/home/xxxxxxx/MyBlog/node_modules/js-yaml/lib/js-yaml/loader.js:173:9)
at readBlockMapping (/home/xxxxxxx/MyBlog/node_modules/js-yaml/lib/js-yaml/loader.js:1027:9)
at composeNode (/home/xxxxxxx/MyBlog/node_modules/js-yaml/lib/js-yaml/loader.js:1359:12)
at readDocument (/home/xxxxxxx/MyBlog/node_modules/js-yaml/lib/js-yaml/loader.js:1519:3)
at loadDocuments (/home/xxxxxxx/MyBlog/node_modules/js-yaml/lib/js-yaml/loader.js:1575:5)
at Object.load (/home/xxxxxxx/MyBlog/node_modules/js-yaml/lib/js-yaml/loader.js:1596:19)
at parseYAML (/home/xxxxxxx/MyBlog/node_modules/hexo-front-matter/lib/front_matter.js:80:21)
at parse (/home/xxxxxxx/MyBlog/node_modules/hexo-front-matter/lib/front_matter.js:56:12)
at /home/xxxxxxx/MyBlog/node_modules/hexo/lib/plugins/processor/post.js:51:20
at tryCatcher (/home/xxxxxxx/MyBlog/node_modules/bluebird/js/release/util.js:16:23)
at Promise._settlePromiseFromHandler (/home/xxxxxxx/MyBlog/node_modules/bluebird/js/release/promise.js:509:35)
at Promise._settlePromise (/home/xxxxxxx/MyBlog/node_modules/bluebird/js/release/promise.js:569:18)
at Promise._settlePromise0 (/home/xxxxxxx/MyBlog/node_modules/bluebird/js/release/promise.js:614:10)
at Promise._settlePromises (/home/xxxxxxx/MyBlog/node_modules/bluebird/js/release/promise.js:694:18)
at Promise._fulfill (/home/xxxxxxx/MyBlog/node_modules/bluebird/js/release/promise.js:638:18)

搜了一些解决方法,大多说可以使用HTML字符实体代替,但是问题是我渲染出来的 html 页面里面这些字符实体不会转换成对应的字符而是保持了原样,所以这个方法并不可行

我的解决方法是:使用单引号把标题引起来
如这样就好了:

1
2
3
title: '微信公众号开发错误解决 {"errcode":47001,"errmsg":"data format error hint: \[xrlw6a07614125\]"} 发送模板消息'
categories:
- 技术

附一些 HTML字符实体

1
2
3
4
5
6
" &quot;
[ &#91;
] &#93;
{ &#123;
} &#125;
: &#58;

这个地方有个列表但不全,例如冒号就没有。

这里更全。

微信公众号开发错误解决 {"errcode":47001,"errmsg":"data format error hint: [xrlw6a07614125]"} 发送模板消息

目录

发送模板消息的时候出现错误,python3,使用 requests 发送 post 请求。

1
{"errcode":47001,"errmsg":"data format error hint: [xrlw6a07614125]"}

问题是 post 的 data 要 json.dumps 一下就好了。

1
2
3
data['touser'] = t.openid
url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' + get_access_token()
r = requests.post(url, data)

最后一句改为

1
r = requests.post(url, json.dumps(data))