文件说明

  • gdtv_to_epg.log 日志文件
  • epg.xml XMLTV格式文件
  • gdtv_to_epg.py 电子节目表抓取
  • git_commit.sh 日志提交脚本文件
  • /etc/init.d/gdtv_to_epg 开机执行脚本
  • /etc/rc.local 开机执行脚本

改清华源

vi /etc/opkg/distfeeds.conf

1
2
3
4
5
6
src/gz openwrt_core http://mirrors.tuna.tsinghua.edu.cn/lede/releases/23.05.4/targets/ramips/mt7620/packages
src/gz openwrt_base http://mirrors.tuna.tsinghua.edu.cn/lede/releases/23.05.4/packages/mipsel_24kc/base
src/gz openwrt_luci http://mirrors.tuna.tsinghua.edu.cn/lede/releases/23.05.4/packages/mipsel_24kc/luci
src/gz openwrt_packages http://mirrors.tuna.tsinghua.edu.cn/lede/releases/23.05.4/packages/mipsel_24kc/packages
src/gz openwrt_routing http://mirrors.tuna.tsinghua.edu.cn/lede/releases/23.05.4/packages/mipsel_24kc/routing
src/gz openwrt_telephony http://mirrors.tuna.tsinghua.edu.cn/lede/releases/23.05.4/packages/mipsel_24kc/telephony

安装配置git

1
2
3
4
5
6
7
8
opkg update
opkg install git
opkg install openssh-keygen
opkg install openssh-client
cd /www
git init
ssh-keygen
ssh -T git@gitee.com

抓取源码

/www/gdtv_to_epg.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import datetime
import requests
import warnings
import logging
from bs4 import BeautifulSoup as bs
from xml.etree.ElementTree import Element, SubElement, ElementTree

# 日志配置
logging.basicConfig(
filename="/www/gdtv_to_epg.log", # 日志文件路径
level=logging.INFO, # 日志级别
format="%(asctime)s - %(levelname)s - %(message)s",
filemode="w" # 每次运行脚本时覆盖日志
)

# 定义请求头,模拟浏览器访问
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}

# 忽略 XMLParsedAsHTMLWarning
warnings.filterwarnings('ignore')

# 获取广东电视台频道列表
def get_channels_gdtv():
logging.info("开始获取频道列表...")
url = 'http://epg.gdtv.cn/f/1.xml'
try:
res = requests.get(url, headers=headers, timeout=8)
res.encoding = 'utf-8'
soup = bs(res.text, 'html.parser')
contents = soup.select('channel')
channels = []
for content in contents:
channel_id = content.attrs['id']
name = content.ctitle.text.strip()
channels.append({'id': channel_id, 'name': name})
logging.info(f"获取到 {len(channels)} 个频道")
return channels
except requests.exceptions.RequestException as e:
logging.error(f"获取频道列表失败: {e}")
return []

# 处理节目标题中的 HTML 实体和标签
def clean_html(text):
text = bs(text, "html.parser").get_text()
return text.replace('<br />', '\n').replace('<br>', '\n').strip()

# 获取指定日期范围内的节目表
def get_epgs_gdtv(channel_id, start_date, end_date):
logging.info(f"开始抓取频道 {channel_id} 的节目表...")
epgs = []
current_date = start_date
while current_date <= end_date:
url = f'http://epg.gdtv.cn/f/{channel_id}/{current_date.strftime("%Y-%m-%d")}.xml'
try:
res = requests.get(url, headers=headers, timeout=8)
res.encoding = 'utf-8'
soup = bs(res.text, 'html.parser')
epgs_contents = soup.select('content')

for epga in epgs_contents:
starttime = datetime.datetime.fromtimestamp(int(epga.attrs['time1']))
endtime = datetime.datetime.fromtimestamp(int(epga.attrs['time2']))
title = clean_html(epga.get_text())
epgs.append({
'title': title,
'start': starttime,
'end': endtime
})
logging.info(f"完成抓取频道 {channel_id} {current_date} 的节目表,共 {len(epgs)} 条节目")
except requests.exceptions.RequestException as e:
logging.error(f"抓取频道 {channel_id} {current_date} 的节目表失败: {e}")
current_date += datetime.timedelta(days=1)
return epgs

# 创建 XMLTV 文件
def create_epg_xml(channels, epg_data, output_file):
logging.info("开始生成 XMLTV 文件...")
tv = Element('tv')

for channel in channels:
channel_element = SubElement(tv, 'channel', id=channel['id'])
display_name = SubElement(channel_element, 'display-name')
display_name.text = channel['name']

for channel_id, programs in epg_data.items():
for program in programs:
programme = SubElement(tv, 'programme', start=program['start'].strftime('%Y%m%d%H%M%S +0800'),
stop=program['end'].strftime('%Y%m%d%H%M%S +0800'),
channel=channel_id)
title = SubElement(programme, 'title')
title.text = program['title']

tree = ElementTree(tv)
tree.write(output_file, encoding='utf-8', xml_declaration=True)
logging.info(f"XMLTV 文件生成完成: {output_file}")

# 主函数
if __name__ == "__main__":
today = datetime.datetime.now().date()
start_date = today - datetime.timedelta(days=6)
end_date = today + datetime.timedelta(days=2)

try:
logging.info("脚本开始执行")

channels = get_channels_gdtv()
if not channels:
logging.error("没有获取到频道列表,脚本将结束执行。")
exit(1) # 如果未获取到频道列表,则退出

epg_data = {}
for channel in channels:
channel_id = channel['id']
epg_data[channel_id] = get_epgs_gdtv(channel_id, start_date, end_date)

output_file = "/www/epg.xml"
create_epg_xml(channels, epg_data, output_file)

logging.info(f"完成!EPG 文件已保存为 {output_file}")
except Exception as e:
logging.error(f"脚本执行过程中发生错误: {e}")


开机脚本

/etc/rc.local

1
2
3
4
5
6
# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

ntpd -q -p pool.ntp.org

exit 0

/etc/init.d/gdtv_to_epg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/sh /etc/rc.common
# OpenWrt init script for gdtv_to_epg

START=99
USE_PROCD=1

start_service() {
# 第一个任务,延迟 180 秒执行
procd_open_instance
procd_set_param command /bin/sh -c "sleep 180 && /usr/bin/python /www/gdtv_to_epg.py"
procd_set_param network 1
procd_close_instance

# 第二个任务,延迟 480 秒执行
procd_open_instance
procd_set_param command /bin/sh -c "sleep 480 && /bin/sh /www/git_commit.sh"
procd_set_param network 1
procd_close_instance
}

提交脚本

/www/git_commit.sh

1
2
3
4
5
6
#!/bin/sh
export PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
cd /www || exit 1 # 进入 Git 仓库的工作目录
git add . # 添加所有修改
git commit -m "更新日志" # 提交修改
git push origin master # 推送到远程仓库

日志文件

/www/gdtv_to_epg.log

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
2025-01-02 18:02:36,843 - INFO - 脚本开始执行
2025-01-02 18:02:36,846 - INFO - 开始获取频道列表...
2025-01-02 18:02:37,149 - INFO - 获取到 15 个频道
2025-01-02 18:02:37,154 - INFO - 开始抓取频道 1 的节目表...
2025-01-02 18:02:37,423 - INFO - 完成抓取频道 1 2024-12-27 的节目表,共 28 条节目
2025-01-02 18:02:37,686 - INFO - 完成抓取频道 1 2024-12-28 的节目表,共 58 条节目
2025-01-02 18:02:37,948 - INFO - 完成抓取频道 1 2024-12-29 的节目表,共 88 条节目
2025-01-02 18:02:38,193 - INFO - 完成抓取频道 1 2024-12-30 的节目表,共 117 条节目
2025-01-02 18:02:38,451 - INFO - 完成抓取频道 1 2024-12-31 的节目表,共 146 条节目
2025-01-02 18:02:38,688 - INFO - 完成抓取频道 1 2025-01-01 的节目表,共 173 条节目
2025-01-02 18:02:38,909 - INFO - 完成抓取频道 1 2025-01-02 的节目表,共 202 条节目
2025-01-02 18:02:39,124 - INFO - 完成抓取频道 1 2025-01-03 的节目表,共 230 条节目
2025-01-02 18:02:39,383 - INFO - 完成抓取频道 1 2025-01-04 的节目表,共 261 条节目
2025-01-02 18:02:39,387 - INFO - 开始抓取频道 2 的节目表...
2025-01-02 18:02:39,559 - INFO - 完成抓取频道 2 2024-12-27 的节目表,共 14 条节目
2025-01-02 18:02:39,701 - INFO - 完成抓取频道 2 2024-12-28 的节目表,共 24 条节目
2025-01-02 18:02:39,856 - INFO - 完成抓取频道 2 2024-12-29 的节目表,共 35 条节目
2025-01-02 18:02:40,033 - INFO - 完成抓取频道 2 2024-12-30 的节目表,共 50 条节目
2025-01-02 18:02:40,201 - INFO - 完成抓取频道 2 2024-12-31 的节目表,共 63 条节目
2025-01-02 18:02:40,358 - INFO - 完成抓取频道 2 2025-01-01 的节目表,共 74 条节目
2025-01-02 18:02:40,538 - INFO - 完成抓取频道 2 2025-01-02 的节目表,共 88 条节目
2025-01-02 18:02:40,716 - INFO - 完成抓取频道 2 2025-01-03 的节目表,共 102 条节目
2025-01-02 18:02:40,902 - INFO - 完成抓取频道 2 2025-01-04 的节目表,共 113 条节目
2025-01-02 18:02:40,905 - INFO - 开始抓取频道 6 的节目表...
2025-01-02 18:02:41,234 - INFO - 完成抓取频道 6 2024-12-27 的节目表,共 44 条节目
2025-01-02 18:02:41,558 - INFO - 完成抓取频道 6 2024-12-28 的节目表,共 88 条节目
2025-01-02 18:02:41,888 - INFO - 完成抓取频道 6 2024-12-29 的节目表,共 133 条节目
2025-01-02 18:02:42,221 - INFO - 完成抓取频道 6 2024-12-30 的节目表,共 177 条节目
2025-01-02 18:02:42,532 - INFO - 完成抓取频道 6 2024-12-31 的节目表,共 221 条节目
2025-01-02 18:02:42,873 - INFO - 完成抓取频道 6 2025-01-01 的节目表,共 266 条节目
2025-01-02 18:02:43,201 - INFO - 完成抓取频道 6 2025-01-02 的节目表,共 310 条节目
2025-01-02 18:02:43,537 - INFO - 完成抓取频道 6 2025-01-03 的节目表,共 355 条节目
2025-01-02 18:02:44,060 - INFO - 完成抓取频道 6 2025-01-04 的节目表,共 399 条节目
2025-01-02 18:02:44,064 - INFO - 开始抓取频道 4 的节目表...
2025-01-02 18:02:44,279 - INFO - 完成抓取频道 4 2024-12-27 的节目表,共 20 条节目
2025-01-02 18:02:44,487 - INFO - 完成抓取频道 4 2024-12-28 的节目表,共 40 条节目
2025-01-02 18:02:44,694 - INFO - 完成抓取频道 4 2024-12-29 的节目表,共 62 条节目
2025-01-02 18:02:44,915 - INFO - 完成抓取频道 4 2024-12-30 的节目表,共 84 条节目
2025-01-02 18:02:45,122 - INFO - 完成抓取频道 4 2024-12-31 的节目表,共 104 条节目
2025-01-02 18:02:45,580 - INFO - 完成抓取频道 4 2025-01-01 的节目表,共 122 条节目
2025-01-02 18:02:45,796 - INFO - 完成抓取频道 4 2025-01-02 的节目表,共 137 条节目
2025-01-02 18:02:45,983 - INFO - 完成抓取频道 4 2025-01-03 的节目表,共 151 条节目
2025-01-02 18:02:46,180 - INFO - 完成抓取频道 4 2025-01-04 的节目表,共 166 条节目
2025-01-02 18:02:46,183 - INFO - 开始抓取频道 3 的节目表...
2025-01-02 18:02:46,442 - INFO - 完成抓取频道 3 2024-12-27 的节目表,共 24 条节目
2025-01-02 18:02:46,742 - INFO - 完成抓取频道 3 2024-12-28 的节目表,共 56 条节目
2025-01-02 18:02:47,028 - INFO - 完成抓取频道 3 2024-12-29 的节目表,共 86 条节目
2025-01-02 18:02:47,346 - INFO - 完成抓取频道 3 2024-12-30 的节目表,共 118 条节目
2025-01-02 18:02:47,665 - INFO - 完成抓取频道 3 2024-12-31 的节目表,共 152 条节目
2025-01-02 18:02:47,990 - INFO - 完成抓取频道 3 2025-01-01 的节目表,共 181 条节目
2025-01-02 18:02:48,302 - INFO - 完成抓取频道 3 2025-01-02 的节目表,共 212 条节目
2025-01-02 18:02:48,540 - INFO - 完成抓取频道 3 2025-01-03 的节目表,共 236 条节目
2025-01-02 18:02:48,781 - INFO - 完成抓取频道 3 2025-01-04 的节目表,共 269 条节目
2025-01-02 18:02:48,785 - INFO - 开始抓取频道 14 的节目表...
2025-01-02 18:02:48,940 - INFO - 完成抓取频道 14 2024-12-27 的节目表,共 18 条节目
2025-01-02 18:02:49,153 - INFO - 完成抓取频道 14 2024-12-28 的节目表,共 41 条节目
2025-01-02 18:02:49,379 - INFO - 完成抓取频道 14 2024-12-29 的节目表,共 66 条节目
2025-01-02 18:02:49,566 - INFO - 完成抓取频道 14 2024-12-30 的节目表,共 84 条节目
2025-01-02 18:02:49,744 - INFO - 完成抓取频道 14 2024-12-31 的节目表,共 101 条节目
2025-01-02 18:02:49,931 - INFO - 完成抓取频道 14 2025-01-01 的节目表,共 119 条节目
2025-01-02 18:02:50,117 - INFO - 完成抓取频道 14 2025-01-02 的节目表,共 136 条节目
2025-01-02 18:02:50,326 - INFO - 完成抓取频道 14 2025-01-03 的节目表,共 154 条节目
2025-01-02 18:02:50,546 - INFO - 完成抓取频道 14 2025-01-04 的节目表,共 177 条节目
2025-01-02 18:02:50,550 - INFO - 开始抓取频道 8 的节目表...
2025-01-02 18:02:50,736 - INFO - 完成抓取频道 8 2024-12-27 的节目表,共 18 条节目
2025-01-02 18:02:50,936 - INFO - 完成抓取频道 8 2024-12-28 的节目表,共 38 条节目
2025-01-02 18:02:51,162 - INFO - 完成抓取频道 8 2024-12-29 的节目表,共 60 条节目
2025-01-02 18:02:51,470 - INFO - 完成抓取频道 8 2024-12-30 的节目表,共 79 条节目
2025-01-02 18:02:51,697 - INFO - 完成抓取频道 8 2024-12-31 的节目表,共 98 条节目
2025-01-02 18:02:51,900 - INFO - 完成抓取频道 8 2025-01-01 的节目表,共 117 条节目
2025-01-02 18:02:52,087 - INFO - 完成抓取频道 8 2025-01-02 的节目表,共 136 条节目
2025-01-02 18:02:52,293 - INFO - 完成抓取频道 8 2025-01-03 的节目表,共 156 条节目
2025-01-02 18:02:52,397 - INFO - 完成抓取频道 8 2025-01-04 的节目表,共 156 条节目
2025-01-02 18:02:52,401 - INFO - 开始抓取频道 13 的节目表...
2025-01-02 18:02:52,679 - INFO - 完成抓取频道 13 2024-12-27 的节目表,共 34 条节目
2025-01-02 18:02:52,966 - INFO - 完成抓取频道 13 2024-12-28 的节目表,共 70 条节目
2025-01-02 18:02:53,260 - INFO - 完成抓取频道 13 2024-12-29 的节目表,共 105 条节目
2025-01-02 18:02:53,368 - INFO - 完成抓取频道 13 2024-12-30 的节目表,共 105 条节目
2025-01-02 18:02:53,482 - INFO - 完成抓取频道 13 2024-12-31 的节目表,共 105 条节目
2025-01-02 18:02:53,592 - INFO - 完成抓取频道 13 2025-01-01 的节目表,共 105 条节目
2025-01-02 18:02:53,707 - INFO - 完成抓取频道 13 2025-01-02 的节目表,共 105 条节目
2025-01-02 18:02:53,821 - INFO - 完成抓取频道 13 2025-01-03 的节目表,共 105 条节目
2025-01-02 18:02:53,923 - INFO - 完成抓取频道 13 2025-01-04 的节目表,共 105 条节目
2025-01-02 18:02:53,927 - INFO - 开始抓取频道 17 的节目表...
2025-01-02 18:02:54,080 - INFO - 完成抓取频道 17 2024-12-27 的节目表,共 10 条节目
2025-01-02 18:02:54,230 - INFO - 完成抓取频道 17 2024-12-28 的节目表,共 19 条节目
2025-01-02 18:02:54,385 - INFO - 完成抓取频道 17 2024-12-29 的节目表,共 28 条节目
2025-01-02 18:02:54,549 - INFO - 完成抓取频道 17 2024-12-30 的节目表,共 39 条节目
2025-01-02 18:02:54,712 - INFO - 完成抓取频道 17 2024-12-31 的节目表,共 50 条节目
2025-01-02 18:02:54,881 - INFO - 完成抓取频道 17 2025-01-01 的节目表,共 61 条节目
2025-01-02 18:02:55,054 - INFO - 完成抓取频道 17 2025-01-02 的节目表,共 72 条节目
2025-01-02 18:02:55,216 - INFO - 完成抓取频道 17 2025-01-03 的节目表,共 82 条节目
2025-01-02 18:02:55,653 - INFO - 完成抓取频道 17 2025-01-04 的节目表,共 90 条节目
2025-01-02 18:02:55,657 - INFO - 开始抓取频道 16 的节目表...
2025-01-02 18:02:55,832 - INFO - 完成抓取频道 16 2024-12-27 的节目表,共 19 条节目
2025-01-02 18:02:56,003 - INFO - 完成抓取频道 16 2024-12-28 的节目表,共 39 条节目
2025-01-02 18:02:56,222 - INFO - 完成抓取频道 16 2024-12-29 的节目表,共 60 条节目
2025-01-02 18:02:56,411 - INFO - 完成抓取频道 16 2024-12-30 的节目表,共 79 条节目
2025-01-02 18:02:56,721 - INFO - 完成抓取频道 16 2024-12-31 的节目表,共 97 条节目
2025-01-02 18:02:56,931 - INFO - 完成抓取频道 16 2025-01-01 的节目表,共 113 条节目
2025-01-02 18:02:57,164 - INFO - 完成抓取频道 16 2025-01-02 的节目表,共 129 条节目
2025-01-02 18:02:57,337 - INFO - 完成抓取频道 16 2025-01-03 的节目表,共 144 条节目
2025-01-02 18:02:57,503 - INFO - 完成抓取频道 16 2025-01-04 的节目表,共 157 条节目
2025-01-02 18:02:57,506 - INFO - 开始抓取频道 18 的节目表...
2025-01-02 18:02:57,657 - INFO - 完成抓取频道 18 2024-12-27 的节目表,共 9 条节目
2025-01-02 18:02:57,809 - INFO - 完成抓取频道 18 2024-12-28 的节目表,共 17 条节目
2025-01-02 18:02:57,944 - INFO - 完成抓取频道 18 2024-12-29 的节目表,共 25 条节目
2025-01-02 18:02:58,106 - INFO - 完成抓取频道 18 2024-12-30 的节目表,共 36 条节目
2025-01-02 18:02:58,258 - INFO - 完成抓取频道 18 2024-12-31 的节目表,共 46 条节目
2025-01-02 18:02:58,409 - INFO - 完成抓取频道 18 2025-01-01 的节目表,共 56 条节目
2025-01-02 18:02:58,559 - INFO - 完成抓取频道 18 2025-01-02 的节目表,共 66 条节目
2025-01-02 18:02:58,709 - INFO - 完成抓取频道 18 2025-01-03 的节目表,共 76 条节目
2025-01-02 18:02:58,858 - INFO - 完成抓取频道 18 2025-01-04 的节目表,共 84 条节目
2025-01-02 18:02:58,861 - INFO - 开始抓取频道 7 的节目表...
2025-01-02 18:02:59,074 - INFO - 完成抓取频道 7 2024-12-27 的节目表,共 21 条节目
2025-01-02 18:02:59,295 - INFO - 完成抓取频道 7 2024-12-28 的节目表,共 42 条节目
2025-01-02 18:02:59,487 - INFO - 完成抓取频道 7 2024-12-29 的节目表,共 60 条节目
2025-01-02 18:02:59,711 - INFO - 完成抓取频道 7 2024-12-30 的节目表,共 82 条节目
2025-01-02 18:02:59,927 - INFO - 完成抓取频道 7 2024-12-31 的节目表,共 105 条节目
2025-01-02 18:03:00,098 - INFO - 完成抓取频道 7 2025-01-01 的节目表,共 125 条节目
2025-01-02 18:03:00,264 - INFO - 完成抓取频道 7 2025-01-02 的节目表,共 143 条节目
2025-01-02 18:03:00,345 - INFO - 完成抓取频道 7 2025-01-03 的节目表,共 143 条节目
2025-01-02 18:03:00,413 - INFO - 完成抓取频道 7 2025-01-04 的节目表,共 143 条节目
2025-01-02 18:03:00,417 - INFO - 开始抓取频道 33 的节目表...
2025-01-02 18:03:00,659 - INFO - 完成抓取频道 33 2024-12-27 的节目表,共 32 条节目
2025-01-02 18:03:00,901 - INFO - 完成抓取频道 33 2024-12-28 的节目表,共 61 条节目
2025-01-02 18:03:01,111 - INFO - 完成抓取频道 33 2024-12-29 的节目表,共 88 条节目
2025-01-02 18:03:01,359 - INFO - 完成抓取频道 33 2024-12-30 的节目表,共 122 条节目
2025-01-02 18:03:01,599 - INFO - 完成抓取频道 33 2024-12-31 的节目表,共 155 条节目
2025-01-02 18:03:01,883 - INFO - 完成抓取频道 33 2025-01-01 的节目表,共 187 条节目
2025-01-02 18:03:02,150 - INFO - 完成抓取频道 33 2025-01-02 的节目表,共 224 条节目
2025-01-02 18:03:02,384 - INFO - 完成抓取频道 33 2025-01-03 的节目表,共 256 条节目
2025-01-02 18:03:02,461 - INFO - 完成抓取频道 33 2025-01-04 的节目表,共 256 条节目
2025-01-02 18:03:02,465 - INFO - 开始抓取频道 31 的节目表...
2025-01-02 18:03:02,541 - INFO - 完成抓取频道 31 2024-12-27 的节目表,共 3 条节目
2025-01-02 18:03:02,657 - INFO - 完成抓取频道 31 2024-12-28 的节目表,共 12 条节目
2025-01-02 18:03:02,746 - INFO - 完成抓取频道 31 2024-12-29 的节目表,共 15 条节目
2025-01-02 18:03:02,833 - INFO - 完成抓取频道 31 2024-12-30 的节目表,共 18 条节目
2025-01-02 18:03:02,923 - INFO - 完成抓取频道 31 2024-12-31 的节目表,共 21 条节目
2025-01-02 18:03:03,015 - INFO - 完成抓取频道 31 2025-01-01 的节目表,共 24 条节目
2025-01-02 18:03:03,098 - INFO - 完成抓取频道 31 2025-01-02 的节目表,共 27 条节目
2025-01-02 18:03:03,195 - INFO - 完成抓取频道 31 2025-01-03 的节目表,共 30 条节目
2025-01-02 18:03:03,280 - INFO - 完成抓取频道 31 2025-01-04 的节目表,共 33 条节目
2025-01-02 18:03:03,284 - INFO - 开始抓取频道 32 的节目表...
2025-01-02 18:03:03,697 - INFO - 完成抓取频道 32 2024-12-27 的节目表,共 66 条节目
2025-01-02 18:03:04,106 - INFO - 完成抓取频道 32 2024-12-28 的节目表,共 127 条节目
2025-01-02 18:03:04,523 - INFO - 完成抓取频道 32 2024-12-29 的节目表,共 193 条节目
2025-01-02 18:03:04,884 - INFO - 完成抓取频道 32 2024-12-30 的节目表,共 248 条节目
2025-01-02 18:03:05,241 - INFO - 完成抓取频道 32 2024-12-31 的节目表,共 303 条节目
2025-01-02 18:03:05,587 - INFO - 完成抓取频道 32 2025-01-01 的节目表,共 355 条节目
2025-01-02 18:03:05,926 - INFO - 完成抓取频道 32 2025-01-02 的节目表,共 408 条节目
2025-01-02 18:03:06,290 - INFO - 完成抓取频道 32 2025-01-03 的节目表,共 465 条节目
2025-01-02 18:03:06,734 - INFO - 完成抓取频道 32 2025-01-04 的节目表,共 516 条节目
2025-01-02 18:03:06,738 - INFO - 开始生成 XMLTV 文件...
2025-01-02 18:03:09,085 - INFO - XMLTV 文件生成完成: /www/epg.xml
2025-01-02 18:03:09,114 - INFO - 完成!EPG 文件已保存为 /www/epg.xml

终端访问

1
http://192.168.1.1/epg.xml

重启直至日志提交完成,全程约需10分钟。

旧站点 数年前部署在github上,hexo发布后是编译过的html文件,源文件.md和配置文件等并不上传到github。年代久远,之前电脑及文件均已无法找到,无法再更新,重新部署记录。

基本思路

  • github新建一个私有仓库,备份源文件等;
  • github新建一个公开仓库,发布博客文件。

准备工作

  • 密钥ssh-keygen -t rsa -b 4096 -C “your_email@example.com
  • 登录github,Repositories,heiok.github.io改名为oldsite,Settings,Pages,Custom domain,删除域名绑定;
  • 新建私有仓库hexo,新建公开仓库heiok.github.io(域名绑定);
  • 测试ssh -T git@github.com

博客发布

  • 安装 npm install -g cnpm –registry=https://registry.npmmirror.com

  • 插件 cnpm install hexo-deployer-git –save

  • 设置 hexo/_config.yml

    1
    2
    3
    4
    5
    6
    # Deployment
    ## Docs: https://hexo.io/docs/one-command-deployment
    deploy:
    type: git
    repo: git@github.com:heiok/heiok.github.io.git
    branch: main
  • 发布

    1
    2
    3
    hexo clean   // 清除缓存
    hexo g // 生成静态网页
    hexo d // 开始部署

备份推送

如果远程仓库已经有内容,先拉取远程内容解决冲突后再推送:

1
2
3
4
5
6
7
git pull origin main --rebase
git init
git remote add origin git@github.com:heiok/hexo.git
git checkout -b main
git add .
git commit -m "blog source files backup"
git push origin main

旧站处理

站点改名为oldsite后,原站点CSS样式需要处理。

1
2
3
4
5
6
7
git clone git@github.com:heiok/oldsite.git
find . -name "*.html" -exec sed -i 's|href="/css|href="/oldsite/css|g' {} \;
find . -name "*.html" -exec sed -i 's|src="/js|src="/oldsite/js|g' {} \;
find . -name "*.html" -exec sed -i 's|href="/\([^o][^/]*\)|href="/oldsite/\1|g' {} \;
find . -name "*.html" -exec sed -i 's|href="/"|href="/oldsite"|' {} \;
find . -name "*.html" -exec sed -i 's|src="/images/|src="/oldsite/images/|g' {} \;
find . -name "*.html" -exec sed -i 's|content="/images/|content="/oldsite/images/|g' {} \;

获取广东电视台节目表 环境依赖Python3 + Django4 + Nginx + uWSGI,用于部署WEB项目站点展示节目列表、节目列表下载预览或提供节目列表API接口使用,过于繁杂,以下代码基于Python3抓取官方节目表后直接生成需要的epg.xml,方便部署在OpenWRT路由器上使用。

获取代码

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
96
97
98
99
100
101
import datetime
import requests
import warnings
from bs4 import BeautifulSoup as bs
from xml.etree.ElementTree import Element, SubElement, ElementTree

# 定义请求头,模拟浏览器访问
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36'
}

# 忽略 XMLParsedAsHTMLWarning
warnings.filterwarnings('ignore')

# 获取广东电视台频道列表
def get_channels_gdtv():
url = 'http://epg.gdtv.cn/f/1.xml' # 获取广东电视台频道列表的XML文件
res = requests.get(url, headers=headers)
res.encoding = 'utf-8'
soup = bs(res.text, 'html.parser')
contents = soup.select('channel')
channels = []

for content in contents:
channel_id = content.attrs['id']
name = content.ctitle.text.strip() # 使用 strip() 清理可能的空格和换行
channels.append({'id': channel_id, 'name': name})
return channels

# 处理节目标题中的 HTML 实体和标签
def clean_html(text):
# 将 HTML 实体转换为普通字符(例如 &amp; -> &)
text = bs(text, "html.parser").get_text()
return text.replace('<br />', '\n').replace('<br>', '\n').strip()

# 获取指定日期范围内的节目表
def get_epgs_gdtv(channel_id, start_date, end_date):
epgs = []
current_date = start_date
while current_date <= end_date:
url = f'http://epg.gdtv.cn/f/{channel_id}/{current_date.strftime("%Y-%m-%d")}.xml'
res = requests.get(url, headers=headers, timeout=8)
res.encoding = 'utf-8'
soup = bs(res.text, 'html.parser')
epgs_contents = soup.select('content')

for epga in epgs_contents:
starttime = datetime.datetime.fromtimestamp(int(epga.attrs['time1']))
endtime = datetime.datetime.fromtimestamp(int(epga.attrs['time2']))
title = clean_html(epga.get_text())
epgs.append({
'title': title,
'start': starttime,
'end': endtime
})
current_date += datetime.timedelta(days=1)
return epgs

# 创建 XMLTV 文件
def create_epg_xml(channels, epg_data, output_file):
tv = Element('tv')

# 添加频道信息
for channel in channels:
channel_element = SubElement(tv, 'channel', id=channel['id'])
display_name = SubElement(channel_element, 'display-name')
display_name.text = channel['name']

# 添加节目表
for channel_id, programs in epg_data.items():
for program in programs:
programme = SubElement(tv, 'programme', start=program['start'].strftime('%Y%m%d%H%M%S +0800'),
stop=program['end'].strftime('%Y%m%d%H%M%S +0800'),
channel=channel_id)
title = SubElement(programme, 'title')
title.text = program['title']

# 保存为文件
tree = ElementTree(tv)
tree.write(output_file, encoding='utf-8', xml_declaration=True)

# 主函数
if __name__ == "__main__":
today = datetime.datetime.now().date()
start_date = today - datetime.timedelta(days=6) # 六天前
end_date = today + datetime.timedelta(days=2) # 两天后

print("获取频道列表...")
channels = get_channels_gdtv()

epg_data = {}
print("抓取节目表...")
for channel in channels:
channel_id = channel['id']
epg_data[channel_id] = get_epgs_gdtv(channel_id, start_date, end_date)

print("生成 XMLTV 文件...")
output_file = "epg.xml"
create_epg_xml(channels, epg_data, output_file)
print(f"完成!EPG 文件已保存为 {output_file}")

执行代码

1
2
3
4
5
[root@localhost IPTV]# python gdtv_to_epg.py
获取频道列表...
抓取节目表...
生成 XMLTV 文件...
完成!EPG 文件已保存为 epg.xml

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment

  1. CCTV-1综合
  2. CCTV-2财经
  3. CCTV-3综艺测试
  4. CCTV-4中文国际
  5. CCTV-5体育测试
  6. CCTV-6电影(测试)
  7. CCTV-7国防军事
  8. CCTV-8电视剧(测试)
  9. CCTV-9纪录
  10. CCTV-10科教
  11. CCTV-11戏曲
  12. CCTV-12社会与法
  13. CCTV-13新闻
  14. CCTV-14少儿
  15. CCTV-15音乐
  16. CGTN
  17. CCTV-17
  18. CCTV-1高清
  19. CCTV5+体育高清
  20. CCTV-2高清
  21. CCTV-3高清测试
  22. CCTV-4高清
  23. CCTV-5高清测试
  24. CCTV-6高清测试
  25. CCTV-7国防军事高清
  26. CCTV-8高清测试
  27. CCTV-9高清
  28. CCTV-10高清
  29. CCTV-11高清
  30. CCTV-12高清
  31. CCTV-13高清
  32. CCTV-14少儿高清
  33. CCTV-15高清
  34. CCTV-17高清
  35. 广东卫视
  36. 广东珠江
  37. 广东新闻
  38. 广东民生
  39. 广东体育
  40. 大湾区卫视
  41. 广东影视
  42. 广东少儿
  43. 广东4K超高清 宽色域AVS2
  44. 广东移动
  45. 南方购物
  46. 岭南戏曲
  47. 广东卫视高清
  48. 广东体育高清
  49. 广东珠江高清
  50. 大湾区卫视高清
  51. 广东影视高清
  52. 广东民生高清
  53. 广东少儿高清
  54. 广东新闻高清
  55. 岭南戏曲高清
  56. 广东珠江超清
  57. VSPN电竞
  58. 精选频道1
  59. 经济科教
  60. 嘉佳卡通
  61. 现代教育
  62. 卡酷动画
  63. 金鹰卡通
  64. 炫动卡通
  65. CETV-1
  66. 山东教育
  67. 财富天下
  68. CETV-2
  69. CETV-4
  70. 经济科教高清
  71. 嘉佳卡通高清
  72. 现代教育高清
  73. 金鹰纪实高清
  74. 上海纪实高清
  75. IPTV谍战剧场
  76. IPTV相声小品
  77. IPTV野外
  78. IPTV法治
  79. 爱电影
  80. 经典电影
  81. 爱大剧
  82. 热播剧场
  83. 爱综艺
  84. 爱体育
  85. 少儿动画
  86. 魅力时尚
  87. 收视指南
  88. 汽摩
  89. 老故事
  90. 国学频道
  91. 兵器科技高清
  92. 电视指南高清
  93. 风云足球高清
  94. 央视台球高清
  95. 高尔夫网球高清
  96. 女性时尚高清
  97. 世界地理高清
  98. 怀旧剧场高清
  99. 风云剧场高清
  100. 央视精品高清
  101. 第一剧场高清
  102. 风云音乐高清
  103. 湖南卫视
  104. 浙江卫视
  105. 江苏卫视
  106. 东方卫视
  107. 安徽卫视
  108. 北京卫视
  109. 天津卫视
  110. 山东卫视
  111. 江西卫视
  112. 深圳卫视
  113. 湖北卫视
  114. 辽宁卫视
  115. 黑龙江卫视
  116. 贵州卫视
  117. 四川卫视
  118. 河南卫视
  119. 云南卫视
  120. 重庆卫视
  121. 河北卫视
  122. 东南卫视
  123. 广西卫视
  124. 吉林卫视
  125. 陕西卫视
  126. 山西卫视
  127. 内蒙古卫视
  128. 青海卫视
  129. 海南卫视
  130. 宁夏卫视
  131. 西藏卫视
  132. 新疆卫视
  133. 甘肃卫视
  134. 兵团卫视
  135. 厦门卫视
  136. 三沙卫视
  137. 湖南卫视高清
  138. 浙江卫视高清
  139. 北京卫视高清
  140. 深圳卫视高清
  141. 东方卫视高清
  142. 福建东南卫视高清
  143. 海南卫视高清

前期准备工作

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
[root@localhost ~]# cat /etc/redhat-release
Rocky Linux release 9.4 (Blue Onyx)

[root@localhost demoCA]# openssl version
OpenSSL 3.0.7 1 Nov 2022 (Library: OpenSSL 3.0.7 1 Nov 2022)

[root@localhost ~]# pwd
/root

[root@localhost ~]# mkdir -p demoCA


[root@localhost ~]# cp /etc/pki/tls/openssl.cnf .


[root@localhost ~]# vi openssl.cnf

[ CA_default ]

dir = /root/demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
#unique_subject = no # Set to 'no' to allow creation of
# several certs with same subject.
new_certs_dir = $dir/newcerts # default place for new certs.

certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crlnumber = $dir/crlnumber # the current crl number
# must be commented out to leave a V1 CRL
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem# The private key


[ policy_match ]
countryName = optional
stateOrProvinceName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional


[root@localhost ~]# cd demoCA/


#root@localhost demoCA]# mkdir newcerts private


[root@localhost demoCA]# chmod 700 private


[root@localhost demoCA]# touch index.txt


[root@localhost demoCA]# echo 01 > serial


[root@localhost demoCA]# tree -L 1
.
├── index.txt
├── newcerts
├── private
└── serial

2 directories, 2 files


newcerts:存放 CA 已签署颁发过的数字证书

private:存放 CA 的私钥

serial:存放证书序列号,可自定义第一张证书的序号(e.g. 0001),之后每新建一张证书,序列号会自动加1

index.txt:存放证书信息

生成CA证书RSA私钥

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost demoCA]# openssl genrsa -passout pass:google -des3 -out ~/demoCA/private/cakey.pem 2048


[root@localhost demoCA]# tree -L 2
.
├── index.txt
├── newcerts
├── private
│   └── cakey.pem
└── serial

2 directories, 3 files

生成CA的根证书公钥

CA证书签名请求 & 对证书进行自签名

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
[root@localhost demoCA]# openssl req -x509 -passin pass:google -new -nodes -key ~/demoCA/private/cakey.pem -days 3650 -s
ubj "/C=CN/ST=GuangDong/L=GZ/O=Approach/CN=www.heiok.com" -out cacert.pem


[root@localhost demoCA]# tree -L 2
.
├── cacert.pem
├── index.txt
├── newcerts
├── private
│   └── cakey.pem
└── serial

2 directories, 4 files


[root@localhost demoCA]# cat cacert.pem
-----BEGIN CERTIFICATE-----
MIIDkzCCAnugAwIBAgIUV3Ql0iptpwoskmLaWoWD2kMmkgQwDQYJKoZIhvcNAQEL
BQAwWTELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nRG9uZzELMAkGA1UEBwwC
R1oxETAPBgNVBAoMCEFwcHJvYWNoMRYwFAYDVQQDDA13d3cuaGVpb2suY29tMB4X
DTI0MTIyNTA4MjkwOVoXDTM0MTIyMzA4MjkwOVowWTELMAkGA1UEBhMCQ04xEjAQ
BgNVBAgMCUd1YW5nRG9uZzELMAkGA1UEBwwCR1oxETAPBgNVBAoMCEFwcHJvYWNo
MRYwFAYDVQQDDA13d3cuaGVpb2suY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEA1/hreZ4fTBB4rf3JX1KmnFp5F+JicASvYGgoGsyhYAiHsD5AWvqF
rbuolZqtK6qpxMcFhP7HKyvVM/gtbtWvcdfQFsvcdBOHifKDjnerGRcI6sThY7jg
qI4fJRjNN7wOTpFMS1J6BxUx9x9LMP6NPnhKUqmWqvhExOGOCxqKGkZdgNp1gPri
sWJVqZdoX6hDfbe1n4qkVT6j322rt6+dU+52T8sBkTVc+9dZx67d0d6nhUdl6TnI
Z7qNPmAr7F17IwWHAcGks/qp4izVHCjKljlEg+jFMb1Su1sMQRjyFn1pkAroS8iH
vbhuW6tfX/c8WH/Rry6FlJlvQIcUaSdFKQIDAQABo1MwUTAdBgNVHQ4EFgQUkMyf
n8Vx4qeP/S9cdZ6Du4CuibUwHwYDVR0jBBgwFoAUkMyfn8Vx4qeP/S9cdZ6Du4Cu
ibUwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAIjDmxvVLuJGA
4A8I7QXIhSmFPG2WPems70C2N6hXeybMVN9wGFHp7v9m23Xe83J7wogZMbspRR+Z
tDML/s51P2JQOTQ91eJ6bj21gWQa/Guhb2H8fQtEoXONYfIfIZMIYL2o66GkPOFy
QCeWS3NJdgwDRNcDXIxguZAA0PMnKZkYT5B0G4K0wN9oEqE3+2ZjpF70Wnpo5aWp
1ZbgZFEAhvv6f1Qjc+4GeCVq92b98qXbrkGcim9Vb6ZLhdXFCcJZyxjDJsIBkEW6
OudF00OwP6OWSI5DN4bniXpHcJQQ15CPZdEiGpsHA6Ac6+fb+ENiodbwfdR/eJxX
0g1ALTFR3A==
-----END CERTIFICATE-----

查看 CA 证书的内容

CA证书已包含与密钥文件对应的公钥信息

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
[root@localhost demoCA]# openssl x509 -in cacert.pem -text -noout
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
57:74:25:d2:2a:6d:a7:0a:2c:92:62:da:5a:85:83:da:43:26:92:04
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = CN, ST = GuangDong, L = GZ, O = Approach, CN = www.heiok.com
Validity
Not Before: Dec 25 08:29:09 2024 GMT
Not After : Dec 23 08:29:09 2034 GMT
Subject: C = CN, ST = GuangDong, L = GZ, O = Approach, CN = www.heiok.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d7:f8:6b:79:9e:1f:4c:10:78:ad:fd:c9:5f:52:
a6:9c:5a:79:17:e2:62:70:04:af:60:68:28:1a:cc:
a1:60:08:87:b0:3e:40:5a:fa:85:ad:bb:a8:95:9a:
ad:2b:aa:a9:c4:c7:05:84:fe:c7:2b:2b:d5:33:f8:
2d:6e:d5:af:71:d7:d0:16:cb:dc:74:13:87:89:f2:
83:8e:77:ab:19:17:08:ea:c4:e1:63:b8:e0:a8:8e:
1f:25:18:cd:37:bc:0e:4e:91:4c:4b:52:7a:07:15:
31:f7:1f:4b:30:fe:8d:3e:78:4a:52:a9:96:aa:f8:
44:c4:e1:8e:0b:1a:8a:1a:46:5d:80:da:75:80:fa:
e2:b1:62:55:a9:97:68:5f:a8:43:7d:b7:b5:9f:8a:
a4:55:3e:a3:df:6d:ab:b7:af:9d:53:ee:76:4f:cb:
01:91:35:5c:fb:d7:59:c7:ae:dd:d1:de:a7:85:47:
65:e9:39:c8:67:ba:8d:3e:60:2b:ec:5d:7b:23:05:
87:01:c1:a4:b3:fa:a9:e2:2c:d5:1c:28:ca:96:39:
44:83:e8:c5:31:bd:52:bb:5b:0c:41:18:f2:16:7d:
69:90:0a:e8:4b:c8:87:bd:b8:6e:5b:ab:5f:5f:f7:
3c:58:7f:d1:af:2e:85:94:99:6f:40:87:14:69:27:
45:29
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
90:CC:9F:9F:C5:71:E2:A7:8F:FD:2F:5C:75:9E:83:BB:80:AE:89:B5
X509v3 Authority Key Identifier:
90:CC:9F:9F:C5:71:E2:A7:8F:FD:2F:5C:75:9E:83:BB:80:AE:89:B5
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
22:30:e6:c6:f5:4b:b8:91:80:e0:0f:08:ed:05:c8:85:29:85:
3c:6d:96:3d:e9:ac:ef:40:b6:37:a8:57:7b:26:cc:54:df:70:
18:51:e9:ee:ff:66:db:75:de:f3:72:7b:c2:88:19:31:bb:29:
45:1f:99:b4:33:0b:fe:ce:75:3f:62:50:39:34:3d:d5:e2:7a:
6e:3d:b5:81:64:1a:fc:6b:a1:6f:61:fc:7d:0b:44:a1:73:8d:
61:f2:1f:21:93:08:60:bd:a8:eb:a1:a4:3c:e1:72:40:27:96:
4b:73:49:76:0c:03:44:d7:03:5c:8c:60:b9:90:00:d0:f3:27:
29:99:18:4f:90:74:1b:82:b4:c0:df:68:12:a1:37:fb:66:63:
a4:5e:f4:5a:7a:68:e5:a5:a9:d5:96:e0:64:51:00:86:fb:fa:
7f:54:23:73:ee:06:78:25:6a:f7:66:fd:f2:a5:db:ae:41:9c:
8a:6f:55:6f:a6:4b:85:d5:c5:09:c2:59:cb:18:c3:26:c2:01:
90:45:ba:3a:e7:45:d3:43:b0:3f:a3:96:48:8e:43:37:86:e7:
89:7a:47:70:94:10:d7:90:8f:65:d1:22:1a:9b:07:03:a0:1c:
eb:e7:db:f8:43:62:a1:d6:f0:7d:d4:7f:78:9c:57:d2:0d:40:
2d:31:51:dc

生成服务器证书密钥文件 & 证书签名请求

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
[root@localhost demoCA]# openssl req -utf8 -newkey rsa:2048 -nodes -keyout client.key -subj "/C=CN/ST=广东省/O=广东省服
务器/CN=广东省服务器" -out client.csr

[root@localhost demoCA]# tree -L 2
.
├── cacert.pem
├── client.csr
├── client.key
├── index.txt
├── newcerts
├── private
│   └── cakey.pem
└── serial

2 directories, 6 files

[root@localhost demoCA]# cat client.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICoDCCAYgCAQAwWzELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCeW5v+S4nOecgTEb
MBkGA1UECgwS5bm/5Lic55yB5pyN5Yqh5ZmoMRswGQYDVQQDDBLlub/kuJznnIHm
nI3liqHlmagwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCyuYovU3Vh
MmZfOZlYYy6SFJq8Xk7ZznhZRI8eTerfe08tqH2L/XWXPnlZWbOrvRME+Njmi6Wv
ZaXyNiWb39GRkxVRMBWYU7OLE8aUdowznTtFeJAnm/gHZhfmUaIMp3R+zyLs5POy
ZJ0o4cD3YVk+ep7FN+uPefrWgc4u3QXZoX011JrCwgP4hMYL7qgytXfQ0Y045oU6
zciKI05XeAlT9+rtiM4k1ee6DZY8GvH5UoNEwC7onDK2BhM9iIu82OscVrgaKNrJ
yDgoUa/8h8I6Ar9a+7w/3/lT+wnq9pWmek2WqNCb4DsystOurHZQCL7orn3z6wCa
L9TntnUL0cbzAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAKTZwHQsRaFPexJoc
rR+qklzdGScCEYJbiB6qk6Q+UFa8l0yO/xnKXQ0iCCbwZ2MtUM2Bw6d/95hlIaq6
ENDrtrGSK2BkUNvocr+IM1YJHdG/jPFyKJanY6AlokQEo03pfuGgRQMlnOu2ZQls
/2EFPBv6CVwidgTY2cqZHGw3cicoPiZ39ikj07P+V+uira1YqUSkyV+xf8M3G5vK
luHBKq71DvJNH+LFJawOpILWReAhIXaxT+6lI17JqCiH9Rs0Srw7/V76Ks6sLpo8
eZr/WxsOCzDvztyhyyiB1QJj5cjnl6wU/g3NhHvo7JjFqobOH86Pa72v53OJ1lHI
Idh1Iw==
-----END CERTIFICATE REQUEST-----

CA签发服务器证书

根据 openssl.cnf 加载CA证书并集合服务器证书签名请求文件来完成签名

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
[root@localhost demoCA]# openssl ca -passin pass:google -config ~/openssl.cnf -in client.csr -out client_cert.pem -batch

Using configuration from /root/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: Dec 25 08:55:30 2024 GMT
Not After : Dec 25 08:55:30 2025 GMT
Subject:
countryName = CN
stateOrProvinceName = \U5E7F\U4E1C\U7701
organizationName = \U5E7F\U4E1C\U7701\U670D\U52A1\U5668
commonName = \U5E7F\U4E1C\U7701\U670D\U52A1\U5668
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
E2:AF:5A:FB:F3:B8:25:2C:62:D9:3B:09:EA:1A:65:0E:3E:12:54:18
X509v3 Authority Key Identifier:
90:CC:9F:9F:C5:71:E2:A7:8F:FD:2F:5C:75:9E:83:BB:80:AE:89:B5
Certificate is to be certified until Dec 25 08:55:30 2025 GMT (365 days)

Write out database with 1 new entries
Data Base Updated

[root@localhost demoCA]# tree -L 3
.
├── cacert.pem
├── client_cert.pem
├── client.csr
├── client.key
├── index.txt
├── index.txt.attr
├── index.txt.old
├── newcerts
│   └── 01.pem
├── private
│   └── cakey.pem
├── serial
└── serial.old

2 directories, 11 files

[root@localhost demoCA]# ll
total 36
-rw-r--r--. 1 root root 1302 Dec 25 16:29 cacert.pem
-rw-r--r--. 1 root root 4443 Dec 25 16:55 client_cert.pem
-rw-r--r--. 1 root root 989 Dec 25 16:42 client.csr
-rw-------. 1 root root 1704 Dec 25 16:42 client.key
-rw-r--r--. 1 root root 225 Dec 25 16:55 index.txt
-rw-r--r--. 1 root root 21 Dec 25 16:55 index.txt.attr
-rw-r--r--. 1 root root 0 Dec 25 15:59 index.txt.old
drwxr-xr-x. 2 root root 20 Dec 25 16:55 newcerts
drwx------. 2 root root 23 Dec 25 16:21 private
-rw-r--r--. 1 root root 3 Dec 25 16:55 serial
-rw-r--r--. 1 root root 3 Dec 25 15:59 serial.old


[root@localhost demoCA]# cat client_cert.pem
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 1 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=CN, ST=GuangDong, L=GZ, O=Approach, CN=www.heiok.com
Validity
Not Before: Dec 25 08:55:30 2024 GMT
Not After : Dec 25 08:55:30 2025 GMT
Subject: C=CN, ST=\xE5\xB9\xBF\xE4\xB8\x9C\xE7\x9C\x81, O=\xE5\xB9\xBF\xE4\xB8\x9C\xE7\x9C\x81\xE6\x9C\x8D\xE5\x8A\xA1\xE5\x99\xA8, CN=\xE5\xB9\xBF\xE4\xB8\x9C\xE7\x9C\x81\xE6\x9C\x8D\xE5\x8A\xA1\xE5\x99\xA8
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:b2:b9:8a:2f:53:75:61:32:66:5f:39:99:58:63:
2e:92:14:9a:bc:5e:4e:d9:ce:78:59:44:8f:1e:4d:
ea:df:7b:4f:2d:a8:7d:8b:fd:75:97:3e:79:59:59:
b3:ab:bd:13:04:f8:d8:e6:8b:a5:af:65:a5:f2:36:
25:9b:df:d1:91:93:15:51:30:15:98:53:b3:8b:13:
c6:94:76:8c:33:9d:3b:45:78:90:27:9b:f8:07:66:
17:e6:51:a2:0c:a7:74:7e:cf:22:ec:e4:f3:b2:64:
9d:28:e1:c0:f7:61:59:3e:7a:9e:c5:37:eb:8f:79:
fa:d6:81:ce:2e:dd:05:d9:a1:7d:35:d4:9a:c2:c2:
03:f8:84:c6:0b:ee:a8:32:b5:77:d0:d1:8d:38:e6:
85:3a:cd:c8:8a:23:4e:57:78:09:53:f7:ea:ed:88:
ce:24:d5:e7:ba:0d:96:3c:1a:f1:f9:52:83:44:c0:
2e:e8:9c:32:b6:06:13:3d:88:8b:bc:d8:eb:1c:56:
b8:1a:28:da:c9:c8:38:28:51:af:fc:87:c2:3a:02:
bf:5a:fb:bc:3f:df:f9:53:fb:09:ea:f6:95:a6:7a:
4d:96:a8:d0:9b:e0:3b:32:b2:d3:ae:ac:76:50:08:
be:e8:ae:7d:f3:eb:00:9a:2f:d4:e7:b6:75:0b:d1:
c6:f3
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
E2:AF:5A:FB:F3:B8:25:2C:62:D9:3B:09:EA:1A:65:0E:3E:12:54:18
X509v3 Authority Key Identifier:
90:CC:9F:9F:C5:71:E2:A7:8F:FD:2F:5C:75:9E:83:BB:80:AE:89:B5
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
0a:e7:5a:c0:f6:b5:14:f6:c4:6a:b8:d3:8b:fc:d9:c0:a6:7c:
22:e2:1b:93:db:55:89:44:7a:c3:47:e5:ba:5c:87:27:a9:32:
5b:a7:26:b7:e5:da:52:c8:ca:9e:3d:bc:64:7e:ff:81:78:68:
00:b0:23:1b:8b:9c:73:60:18:bd:7d:2d:5f:d6:41:20:cd:69:
55:13:74:d5:ce:c7:07:86:41:22:69:ee:5c:5a:46:13:83:ef:
46:90:a3:03:aa:f9:fd:a9:a8:2d:ca:3c:77:60:e6:ee:79:03:
7c:3a:33:78:a5:13:c9:4d:df:9b:58:af:e1:d3:41:89:cb:d9:
81:ae:35:01:08:ca:9c:7b:7f:a5:d2:5b:c5:ef:31:34:85:a3:
a6:17:a6:ae:dd:14:d5:0e:88:63:16:50:6c:eb:ff:0b:be:45:
6e:17:eb:65:9a:ab:e8:94:d1:a6:e9:ca:90:9b:8c:08:29:4b:
3f:ef:5d:6c:03:c4:b8:d8:d9:b0:04:80:43:30:31:c9:71:bc:
9b:82:67:b8:37:55:57:a8:47:42:58:43:35:9e:f1:e1:08:0f:
fd:17:f6:a2:92:88:83:c4:a7:1f:5b:af:c4:ac:ec:85:a5:af:
77:a8:79:b3:19:b2:92:9d:7e:0f:9a:20:3e:99:47:a3:48:59:
37:ba:07:ef
-----BEGIN CERTIFICATE-----
MIIDfDCCAmSgAwIBAgIBATANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJDTjES
MBAGA1UECAwJR3VhbmdEb25nMQswCQYDVQQHDAJHWjERMA8GA1UECgwIQXBwcm9h
Y2gxFjAUBgNVBAMMDXd3dy5oZWlvay5jb20wHhcNMjQxMjI1MDg1NTMwWhcNMjUx
MjI1MDg1NTMwWjBbMQswCQYDVQQGEwJDTjESMBAGA1UECAwJ5bm/5Lic55yBMRsw
GQYDVQQKDBLlub/kuJznnIHmnI3liqHlmagxGzAZBgNVBAMMEuW5v+S4nOecgeac
jeWKoeWZqDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALK5ii9TdWEy
Zl85mVhjLpIUmrxeTtnOeFlEjx5N6t97Ty2ofYv9dZc+eVlZs6u9EwT42OaLpa9l
pfI2JZvf0ZGTFVEwFZhTs4sTxpR2jDOdO0V4kCeb+AdmF+ZRogyndH7PIuzk87Jk
nSjhwPdhWT56nsU36495+taBzi7dBdmhfTXUmsLCA/iExgvuqDK1d9DRjTjmhTrN
yIojTld4CVP36u2IziTV57oNljwa8flSg0TALuicMrYGEz2Ii7zY6xxWuBoo2snI
OChRr/yHwjoCv1r7vD/f+VP7Cer2laZ6TZao0JvgOzKy066sdlAIvuiuffPrAJov
1Oe2dQvRxvMCAwEAAaNNMEswCQYDVR0TBAIwADAdBgNVHQ4EFgQU4q9a+/O4JSxi
2TsJ6hplDj4SVBgwHwYDVR0jBBgwFoAUkMyfn8Vx4qeP/S9cdZ6Du4CuibUwDQYJ
KoZIhvcNAQELBQADggEBAArnWsD2tRT2xGq404v82cCmfCLiG5PbVYlEesNH5bpc
hyepMlunJrfl2lLIyp49vGR+/4F4aACwIxuLnHNgGL19LV/WQSDNaVUTdNXOxweG
QSJp7lxaRhOD70aQowOq+f2pqC3KPHdg5u55A3w6M3ilE8lN35tYr+HTQYnL2YGu
NQEIypx7f6XSW8XvMTSFo6YXpq7dFNUOiGMWUGzr/wu+RW4X62Waq+iU0abpypCb
jAgpSz/vXWwDxLjY2bAEgEMwMclxvJuCZ7g3VVeoR0JYQzWe8eEID/0X9qKSiIPE
px9br8Ss7IWlr3eoebMZspKdfg+aID6ZR6NIWTe6B+8=
-----END CERTIFICATE-----

导出为 PKCS#12 文件

1
2
3
4
5
6
OpenSSL 1.0.2k-fips

[root@localhost demoCA]# openssl pkcs12 -export -in client_cert.pem -inkey client.key -out client.p12

Enter Export Password:
Verifying - Enter Export Password:

Adobe Acrobat添加OpenSSL 3.0.7版本生成的.p12文件会有加密算法与 KDF(密钥派生函数)问题,添加以下参数解决

参数说明:

-certpbe PBE-SHA1-3DES:指定证书的加密算法为 3DES(适用于旧软件)。
-keypbe PBE-SHA1-3DES:指定私钥的加密算法为 3DES。
-macalg SHA1:将 MAC 的算法强制设置为 SHA-1,而不是 OpenSSL 3.x 的默认 SHA-256 或更高。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
OpenSSL 3.0.7

[root@localhost demoCA]# openssl pkcs12 -export -in client_cert.pem -inkey client.key -out client.p12 -certpbe PBE-SHA1-3DES -keypbe PBE-SHA1-3DES -macalg SHA1

Enter Export Password:
Verifying - Enter Export Password:


[root@localhost demoCA]# ll
total 40
-rw-r--r--. 1 root root 1302 Dec 25 16:29 cacert.pem
-rw-r--r--. 1 root root 4443 Dec 25 16:55 client_cert.pem
-rw-r--r--. 1 root root 989 Dec 25 16:42 client.csr
-rw-------. 1 root root 1704 Dec 25 16:42 client.key
-rw-------. 1 root root 2643 Dec 25 17:09 client.p12
-rw-r--r--. 1 root root 225 Dec 25 16:55 index.txt
-rw-r--r--. 1 root root 21 Dec 25 16:55 index.txt.attr
-rw-r--r--. 1 root root 0 Dec 25 15:59 index.txt.old
drwxr-xr-x. 2 root root 20 Dec 25 16:55 newcerts
drwx------. 2 root root 23 Dec 25 16:21 private
-rw-r--r--. 1 root root 3 Dec 25 16:55 serial
-rw-r--r--. 1 root root 3 Dec 25 15:59 serial.old

收视设备信息

  • 光猫型号: HG6142A
  • 盒子型号: TR100 IPTV-G9
  • 连接方式: LAN2
  • 单播组播:单播 & 组播
  • 认证时效: 7天(省内跨市联通网络可看)
  • 连接模式:桥接
  • VLAN ID: 45

获取IPTV频道

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
import requests
import json
import os
import sys

# 设置工作目录为脚本所在目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))

s = requests.Session()
s.headers['User-Agent'] = 'okhttp/3.10.0'

# 获取所有频道信息
all_channels_response = s.get("http://120.87.12.38:8083/epg/api/custom/getAllChannel.json")
all_channels_data = all_channels_response.json()

if all_channels_data['status'] != '200' or 'channels' not in all_channels_data:
print("错误:无法获取频道列表或频道列表格式错误。")
sys.exit(1)

# 提取频道代码
channel_codes = [channel['params']['hwcode'] for channel in all_channels_data['channels'] if 'params' in channel and 'hwcode' in channel['params']]
channel_codes_str = ','.join(channel_codes)

# 获取访问令牌
token_response = s.get("http://120.87.11.11:33200/EPG/oauth/v2/token?grant_type=EncryToken&client_id=shuma2&authinfo=52A57E9F6583C26C2F2E0C6E3FC7452F164A4C84BDEE623FF387E69D25C4C1A57031D44BBAA6DEF42155B7524C4DED856EE61F320E7CFEDEA491B8AFDF90FF4B4830CEE38BAAEC1E88165B70BC52666557400555BED665177880021B8C65ECBF&UserID=0175205891367&DeviceType=TR100-G9&DeviceVersion=219.470.107&userdomain=1&datadomain=1&accountType=1")
access_token = token_response.json().get('access_token')

if not access_token:
print("错误:未能获取有效的 access token。")
sys.exit(1)

# 使用访问令牌请求数据
headers = {
'Authorization': access_token,
'User-Agent': 'okhttp/3.10.0',
'Connection': 'Keep-Alive',
'Content-Type': 'application/json;charset=utf-8'
}
data = json.dumps({"channelcodes": channel_codes_str})
# 请修改此处IP,确保与鉴权URL的IP一致
response = s.post("http://120.87.11.11:33200/EPG/interEpg/channellist/batch", headers=headers, data=data)

if not response.json():
print("错误:返回的数据为空。")
sys.exit(1)

# 保存响应数据到文件
with open('iptv.json', 'w', encoding='UTF-8') as f:
json.dump(response.json(), f)

# 复制 base.txt 到 iptv.txt 和 iptv.m3u8
with open('base.txt', 'r', encoding='UTF-8') as f_base, open('iptv.txt', 'w', encoding='UTF-8') as f_iptv, open('iptv.m3u8', 'w', encoding='UTF-8') as f_m3u8:
base_content = f_base.read()
f_iptv.write(base_content)
f_m3u8.write("#EXTM3U\n")

# 读取 iptv.json 并处理数据
with open('iptv.json', 'r', encoding='UTF-8') as f:
data = json.load(f)
if not data.get('channellist'):
print("错误:'channellist' 数据为空。")
sys.exit(1)

channels = {}

for item in data['channellist']:
hwcode = item['channelcode']
channel_info = next((ch for ch in all_channels_data['channels'] if ch['params'].get('hwcode') == hwcode), None)
if not channel_info:
continue

channel_name = channel_info['title']
timeshift_url = item['timeshifturl']
group = "其它"
if "CCTV" in channel_name or "CGTN" in channel_name:
group = "央视"
elif any(x in channel_name for x in ["广东", "广州", "大湾区", "南方", "岭南"]):
group = "广东"
elif "卫视" in channel_name:
group = "卫视"

if group:
if group not in channels:
channels[group] = []
channels[group].append((channel_name, timeshift_url))

# 写入处理后的数据到 iptv.txt 和 iptv.m3u8
with open('iptv.txt', 'a', encoding='UTF-8') as f_iptv, open('iptv.m3u8', 'a', encoding='UTF-8') as f_m3u8:
for group, ch_list in channels.items():
f_iptv.write(f"{group},#genre#\n")
for channel_name, url in ch_list:
f_iptv.write(f"{channel_name},{url}\n")
f_m3u8.write(f"#EXTINF:-1,{channel_name}\n{url}\n")

事以至此

想必以下方法你们都搞过啦

  • 修改注册表;
  • 修改组策略;
  • 添加凭证项;
  • 替换动态库;
  • 关闭防火墙。

不妨试试

  • 卸载共享主机打印驱动;
  • 重新安装共享主机驱动;
  • 重新连接共享主机打印;
  • 直至客机提示安装驱动。

设备信息

  • ASUS RT-AC 1200GU (F/W Ver:V3.0.0.4.380_10732 H/W Ver:A)
  • openwrt-23.05.4-ramips-mt7621-asus_rt-ac57u-v1-initramfs-kernel.bin
  • openwrt-23.05.4-ramips-mt7621-asus_rt-ac57u-v1-squashfs-sysupgrade.bin

改国内源

vi /etc/opkg/distfeeds.conf

1
2
3
4
5
6
src/gz openwrt_core https://mirrors.aliyun.com/openwrt/releases/23.05.4/targets/x86/64/packages
src/gz openwrt_base https://mirrors.aliyun.com/openwrt/releases/23.05.4/packages/x86_64/base
src/gz openwrt_luci https://mirrors.aliyun.com/openwrt/releases/23.05.4/packages/x86_64/luci
src/gz openwrt_packages https://mirrors.aliyun.com/openwrt/releases/23.05.4/packages/x86_64/packages
src/gz openwrt_routing https://mirrors.aliyun.com/openwrt/releases/23.05.4/packages/x86_64/routing
src/gz openwrt_telephony https://mirrors.aliyun.com/openwrt/releases/23.05.4/packages/x86_64/telephony

环境安装

1
2
3
4
5
6
7
8
9
root@Openwrt:# opkg install python3-base

root@Openwrt:~# opkg install python3-pip

root@Openwrt:~# /usr/bin/python3 -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --upgrade pip

root@Openwrt:~# opkg install requests

root@Openwrt:~# opkg install bs4

pip清华源

1
2
3
4
5
pip install pip -U

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pip -U

设备信息

  • ASUS RT-AC 1200GU (F/W Ver:V3.0.0.4.380_10732 H/W Ver:A)
  • openwrt-23.05.4-ramips-mt7621-asus_rt-ac57u-v1-initramfs-kernel.bin
  • openwrt-23.05.4-ramips-mt7621-asus_rt-ac57u-v1-squashfs-sysupgrade.bin

安装插件

1
2
3
opkg update

opkg install fdisk swap-utils kmod-usb-storage kmod-fs-ext4 e2fsprogs kmod-usb-ohci kmod-usb-uhci block-mount

磁盘分区

1
2
3
4
5
6
7
8
9
10
11
12
13
fdisk -l (查看辨别U盘外设磁盘名称,如/dev/sda。)

fdisk /dev/sda (U盘分区)

p (查看U盘分区)
d (删除U盘分区)
n (新增U盘分区)
p (选主分区类型)
1 (分区数量选壹)
Enter(按回车键)
Enter(按回车键)
w (保存分区配置)
reboot(重启启动)

格式分区

1
2
3
fdisk -l (查看分区名称,如/dev/sda1。)

mkfs.ext4 /dev/sda1 (格式化分区为ext4)

挂载分区

1
2
3
mkdir /mnt/sda1

mount /dev/sda1 /mnt/sda1

数据迁移

1
2
3
cp -r /overlay/* /mnt/sda1

block info (查看U盘的UUID)

配置挂载

1
登录路由器管理界面 - 系统 - 挂载点 - 添加 - UUID、挂载点(作为外部overlay使用) - 保存应用

完成

0%