xiaodu1233 commited on
Commit
c8f08b5
·
1 Parent(s): 2687876
.dockerignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .venv/
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ .venv/
2
+ .venv
3
+
4
+ __pychahe__/
5
+ *.pyc
.vscode/launch.json ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ // Use IntelliSense to learn about possible attributes.
3
+ // Hover to view descriptions of existing attributes.
4
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5
+ "version": "0.2.0",
6
+ "configurations": [
7
+
8
+ {
9
+ "name": "Python Debugger: Current File",
10
+ "type": "debugpy",
11
+ "request": "launch",
12
+ "program": "${file}",
13
+ // 关键改动:${fileDirname} 会让调试器自动进入 run.py 所在的目录(即 jable 目录)
14
+ "cwd": "${fileDirname}",
15
+ "env":{
16
+ "PYTHONPATH":"${workspaceFolder}"
17
+ },
18
+ "console": "integratedTerminal"
19
+ }
20
+ ]
21
+ }
Dockerfile CHANGED
@@ -4,11 +4,22 @@ FROM ubuntu:24.04
4
  # 避免交互式提示(tzdata 等)
5
  ENV DEBIAN_FRONTEND=noninteractive
6
 
 
 
 
 
 
 
 
7
  # 1. 安装 sudo(很多基础镜像没有 sudo)
8
  RUN apt-get update && \
9
  apt-get install -y sudo unzip lsof python3 python3-pip python3-venv curl git wget nodejs npm xz-utils zip libglu1-mesa ffmpeg && \
10
  # 清理缓存,减小镜像体积
11
- apt-get clean && rm -rf /var/lib/apt/lists/*
 
 
 
 
12
 
13
  # 2. 创建新用户(这里叫 devuser,你可以改名)
14
  # -m 创建 home 目录
 
4
  # 避免交互式提示(tzdata 等)
5
  ENV DEBIAN_FRONTEND=noninteractive
6
 
7
+ # 24.04 路径略有不同
8
+ RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list.d/ubuntu.sources && \
9
+ sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list.d/ubuntu.sources
10
+
11
+ # 定义时区变量
12
+ ENV TZ=Asia/Shanghai
13
+
14
  # 1. 安装 sudo(很多基础镜像没有 sudo)
15
  RUN apt-get update && \
16
  apt-get install -y sudo unzip lsof python3 python3-pip python3-venv curl git wget nodejs npm xz-utils zip libglu1-mesa ffmpeg && \
17
  # 清理缓存,减小镜像体积
18
+ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
19
+ echo $TZ > /etc/timezone && \
20
+ apt-get install -y tzdata && \
21
+ apt-get clean && \
22
+ rm -rf /var/lib/apt/lists/*
23
 
24
  # 2. 创建新用户(这里叫 devuser,你可以改名)
25
  # -m 创建 home 目录
jable/baidu.py ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import DrissionPage
2
+ from time import sleep
3
+
4
+ from pathlib import Path
5
+
6
+ import os
7
+
8
+ def start(ts_path: Path, m3u8_path: Path, download_path: Path):
9
+
10
+ pngs = list(ts_path.glob('*.png'))
11
+
12
+ # total_count = 20
13
+ total_count = len(pngs)
14
+ batch_size = 5
15
+ options = DrissionPage.ChromiumOptions()
16
+ options.headless = False
17
+ options.no_imgs = True
18
+ options.incognito = True
19
+
20
+ page = DrissionPage.Chromium(options)
21
+ tab = page.latest_tab
22
+ tab.set.window.max()
23
+ tab.get("https://www.baidu.com")
24
+
25
+ # print(tab.html)
26
+ # ele_ment = tab.ele('xpath://*[@id="right-tool"]/div[3]/div[2]/div/div')
27
+ upload_api = 'aichat/api/file/upload'
28
+ base_cdn = 'https://aisearch.cdn.bcebos.com/'
29
+
30
+ image_cdns = []
31
+
32
+ # 2. 开始大循环
33
+ for i in range(0, total_count, batch_size):
34
+ print(f"--- 正在执行第 {i + 1} 到 {i + batch_size} 次上传 ---")
35
+
36
+ # 【关键改动】每一轮刷新后,必须重新获取元素对象,否则会报错
37
+ # 建议先等待元素出现,确保页面加载完成
38
+ ele_ment = tab.ele('xpath://*[@id="right-tool"]/div[3]/div[2]/div/div', timeout=10)
39
+
40
+ if not ele_ment:
41
+ print("未找到上传元素,跳过本轮")
42
+ tab.refresh()
43
+ sleep(1)
44
+ continue
45
+
46
+ # 3. 准备本轮监听
47
+ tab.listen.start(upload_api)
48
+
49
+ # 4. 准备本轮文件列表并上传
50
+ # urls = [file_path for _ in range(batch_size)]
51
+ urls: list[str] = []
52
+ batch_pages = range(i, min(i + batch_size, total_count))
53
+ for j in batch_pages:
54
+ url = pngs[j]
55
+ urls.append(str(url))
56
+
57
+ print(f"正在上传 {len(urls)} 个文件...")
58
+ print(urls)
59
+ ele_ment.click.to_upload(urls)
60
+
61
+ # 5. 等待数据包捕获
62
+ # count 设置为 batch_size,fit_count=True 表示必须集齐10个响应才继续(除非超时)
63
+ datas = tab.listen.wait(count=batch_size, timeout=15, fit_count=False)
64
+
65
+ if datas:
66
+
67
+ print(f"成功捕获到 {len(datas)} 个响应包")
68
+ # 这里可以遍历 datas 处理响应结果,例如获取文件 ID
69
+ for packet in datas:
70
+ print(packet.request)
71
+ pd = packet.request.postData
72
+ name = pd.get('name', '')
73
+ url = base_cdn + pd.get('path')
74
+ image_cdns.append((name, url))
75
+ else:
76
+ print("警告:本轮未捕获到响应数据,可能上传失败或超时")
77
+
78
+ # 6. 刷新页面,清理状态
79
+ print("本轮结束,正在刷新页面...")
80
+ tab.refresh()
81
+
82
+ # 7. 刷新后稍微等待,确保页面稳定(或使用 wait.load_start())
83
+ sleep(1)
84
+
85
+ print(f"上传任务已完成。下载成功 {len(image_cdns)}")
86
+
87
+ # print(image_cdns)
88
+ sorted_image_cdns = sorted(image_cdns, key=sort_by_name_number)
89
+ print(sorted_image_cdns)
90
+
91
+ lines = []
92
+ timeLines = []
93
+ m3name = str(ts_path.parent).split(os.sep)[-1] + '.m3u8'
94
+ with open(m3u8_path, 'r', encoding='utf-8') as f:
95
+ lines = f.readlines()
96
+ to_m3u8Path = download_path.joinpath(m3name)
97
+
98
+ for str1 in lines:
99
+ if str1.startswith('#EXTINF'):
100
+ timeLines.append(str1)
101
+
102
+ with open(to_m3u8Path, 'w', encoding='utf-8') as f:
103
+ f.write('#EXTM3U\n')
104
+ f.write('#EXT-X-VERSION:3\n')
105
+ f.write('#EXT-X-MEDIA-SEQUENCE:0\n')
106
+ f.write('#EXT-X-ALLOW-CACHE:YES\n')
107
+ f.write('#EXT-X-TARGETDURATION:14\n')
108
+
109
+ for idx, (name, tmp_url) in enumerate(sorted_image_cdns):
110
+ f.write(timeLines[idx])
111
+ f.write(tmp_url+'\n')
112
+
113
+ f.write('#EXT-X-ENDLIST')
114
+
115
+ def sort_by_name_number(item):
116
+
117
+ # 步骤1:拆分文件名和扩展名(000.png → ["000", "png"])
118
+ name_without_ext = item[0].split('.')[0]
119
+ # 步骤2:将数字字符串转为整数("000" → 0,"010" → 10)
120
+ return int(name_without_ext)
121
+
122
+ if __name__ == "__main__":
123
+ pass
jable/hide.png ADDED
jable/jable/__init__.py ADDED
File without changes
jable/jable/items.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Define here the models for your scraped items
2
+ #
3
+ # See documentation in:
4
+ # https://docs.scrapy.org/en/latest/topics/items.html
5
+
6
+ import scrapy
7
+
8
+
9
+ class JableItem(scrapy.Item):
10
+ # define the fields for your item here like:
11
+ # name = scrapy.Field()
12
+ pass
jable/jable/middlewares.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Define here the models for your spider middleware
2
+ #
3
+ # See documentation in:
4
+ # https://docs.scrapy.org/en/latest/topics/spider-middleware.html
5
+
6
+ from scrapy import signals
7
+
8
+ # useful for handling different item types with a single interface
9
+ from itemadapter import ItemAdapter
10
+
11
+
12
+ class JableSpiderMiddleware:
13
+ # Not all methods need to be defined. If a method is not defined,
14
+ # scrapy acts as if the spider middleware does not modify the
15
+ # passed objects.
16
+
17
+ @classmethod
18
+ def from_crawler(cls, crawler):
19
+ # This method is used by Scrapy to create your spiders.
20
+ s = cls()
21
+ crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
22
+ return s
23
+
24
+ def process_spider_input(self, response, spider):
25
+ # Called for each response that goes through the spider
26
+ # middleware and into the spider.
27
+
28
+ # Should return None or raise an exception.
29
+ return None
30
+
31
+ def process_spider_output(self, response, result, spider):
32
+ # Called with the results returned from the Spider, after
33
+ # it has processed the response.
34
+
35
+ # Must return an iterable of Request, or item objects.
36
+ for i in result:
37
+ yield i
38
+
39
+ def process_spider_exception(self, response, exception, spider):
40
+ # Called when a spider or process_spider_input() method
41
+ # (from other spider middleware) raises an exception.
42
+
43
+ # Should return either None or an iterable of Request or item objects.
44
+ pass
45
+
46
+ async def process_start(self, start):
47
+ # Called with an async iterator over the spider start() method or the
48
+ # matching method of an earlier spider middleware.
49
+ async for item_or_request in start:
50
+ yield item_or_request
51
+
52
+ def spider_opened(self, spider):
53
+ spider.logger.info("Spider opened: %s" % spider.name)
54
+
55
+
56
+ class JableDownloaderMiddleware:
57
+ # Not all methods need to be defined. If a method is not defined,
58
+ # scrapy acts as if the downloader middleware does not modify the
59
+ # passed objects.
60
+
61
+ @classmethod
62
+ def from_crawler(cls, crawler):
63
+ # This method is used by Scrapy to create your spiders.
64
+ s = cls()
65
+ crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
66
+ return s
67
+
68
+ def process_request(self, request, spider):
69
+ # Called for each request that goes through the downloader
70
+ # middleware.
71
+
72
+ # Must either:
73
+ # - return None: continue processing this request
74
+ # - or return a Response object
75
+ # - or return a Request object
76
+ # - or raise IgnoreRequest: process_exception() methods of
77
+ # installed downloader middleware will be called
78
+ return None
79
+
80
+ def process_response(self, request, response, spider):
81
+ # Called with the response returned from the downloader.
82
+
83
+ # Must either;
84
+ # - return a Response object
85
+ # - return a Request object
86
+ # - or raise IgnoreRequest
87
+ return response
88
+
89
+ def process_exception(self, request, exception, spider):
90
+ # Called when a download handler or a process_request()
91
+ # (from other downloader middleware) raises an exception.
92
+
93
+ # Must either:
94
+ # - return None: continue processing this exception
95
+ # - return a Response object: stops process_exception() chain
96
+ # - return a Request object: stops process_exception() chain
97
+ pass
98
+
99
+ def spider_opened(self, spider):
100
+ spider.logger.info("Spider opened: %s" % spider.name)
jable/jable/pipelines.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Define your item pipelines here
2
+ #
3
+ # Don't forget to add your pipeline to the ITEM_PIPELINES setting
4
+ # See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
5
+
6
+
7
+ # useful for handling different item types with a single interface
8
+ from itemadapter import ItemAdapter
9
+
10
+
11
+ class JablePipeline:
12
+ def process_item(self, item, spider):
13
+ return item
jable/jable/settings.py ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Scrapy settings for jable project
2
+ #
3
+ # For simplicity, this file contains only settings considered important or
4
+ # commonly used. You can find more settings consulting the documentation:
5
+ #
6
+ # https://docs.scrapy.org/en/latest/topics/settings.html
7
+ # https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
8
+ # https://docs.scrapy.org/en/latest/topics/spider-middleware.html
9
+
10
+ BOT_NAME = "jable"
11
+
12
+ SPIDER_MODULES = ["jable.spiders"]
13
+ NEWSPIDER_MODULE = "jable.spiders"
14
+
15
+ ADDONS = {}
16
+
17
+
18
+ # Crawl responsibly by identifying yourself (and your website) on the user-agent
19
+ USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
20
+
21
+ # Obey robots.txt rules
22
+ ROBOTSTXT_OBEY = False
23
+
24
+ LOG_LEVEL = "ERROR"
25
+
26
+ # Concurrency and throttling settings
27
+ CONCURRENT_REQUESTS = 16
28
+ CONCURRENT_REQUESTS_PER_DOMAIN = 16
29
+ DOWNLOAD_DELAY = 0
30
+
31
+ # Disable cookies (enabled by default)
32
+ #COOKIES_ENABLED = False
33
+
34
+ # Disable Telnet Console (enabled by default)
35
+ #TELNETCONSOLE_ENABLED = False
36
+
37
+ # Override the default request headers:
38
+ #DEFAULT_REQUEST_HEADERS = {
39
+ # "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
40
+ # "Accept-Language": "en",
41
+ #}
42
+
43
+ # Enable or disable spider middlewares
44
+ # See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
45
+ #SPIDER_MIDDLEWARES = {
46
+ # "jable.middlewares.JableSpiderMiddleware": 543,
47
+ #}
48
+
49
+ # Enable or disable downloader middlewares
50
+ # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
51
+ #DOWNLOADER_MIDDLEWARES = {
52
+ # "jable.middlewares.JableDownloaderMiddleware": 543,
53
+ #}
54
+
55
+ # Enable or disable extensions
56
+ # See https://docs.scrapy.org/en/latest/topics/extensions.html
57
+ #EXTENSIONS = {
58
+ # "scrapy.extensions.telnet.TelnetConsole": None,
59
+ #}
60
+
61
+ # Configure item pipelines
62
+ # See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
63
+ #ITEM_PIPELINES = {
64
+ # "jable.pipelines.JablePipeline": 300,
65
+ #}
66
+
67
+ # Enable and configure the AutoThrottle extension (disabled by default)
68
+ # See https://docs.scrapy.org/en/latest/topics/autothrottle.html
69
+ #AUTOTHROTTLE_ENABLED = True
70
+ # The initial download delay
71
+ #AUTOTHROTTLE_START_DELAY = 5
72
+ # The maximum download delay to be set in case of high latencies
73
+ #AUTOTHROTTLE_MAX_DELAY = 60
74
+ # The average number of requests Scrapy should be sending in parallel to
75
+ # each remote server
76
+ #AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
77
+ # Enable showing throttling stats for every response received:
78
+ #AUTOTHROTTLE_DEBUG = False
79
+
80
+ # Enable and configure HTTP caching (disabled by default)
81
+ # See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
82
+ #HTTPCACHE_ENABLED = True
83
+ #HTTPCACHE_EXPIRATION_SECS = 0
84
+ #HTTPCACHE_DIR = "httpcache"
85
+ #HTTPCACHE_IGNORE_HTTP_CODES = []
86
+ #HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"
87
+
88
+ # Set settings whose default value is deprecated to a future-proof value
89
+ FEED_EXPORT_ENCODING = "utf-8"
jable/jable/spiders/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # This package will contain the spiders of your Scrapy project
2
+ #
3
+ # Please refer to the documentation for information on how to create and manage
4
+ # your spiders.
jable/jable/spiders/example.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import scrapy
2
+
3
+
4
+ from upload import upload
5
+
6
+ import time
7
+ URLS = []
8
+ class ExampleSpider(scrapy.Spider):
9
+ name = "example"
10
+ # allowed_domains = ["example.com"]
11
+ start_urls = ["https://www.baidu.com"]
12
+
13
+ def parse(self, response):
14
+ URLS.append(('awd-31221', 'https://play.kvmplay.org/m3/680e95e1772260488/50NjlhMWUzYzhkYTAwOSQzNTg4JDEkMTc3MjIxNzI4OA69a1e3c8da006.m3u8'))
15
+ # URLS.append(('awd-39999', 'https://play.kvmplay.org/m3/45afe831772263170/39NjlhMWVlNDJjNTc2ZCQzNTg4JDIkMTc3MjIxOTk3MA69a1ee42c576b.m3u8'))
16
+ pass
17
+
18
+ def closed(self, reason):
19
+ print('closed ', reason)
20
+
21
+ start_time = time.perf_counter()
22
+
23
+ upload(URLS)
24
+ end_time = time.perf_counter() - start_time
25
+ print(f'结束了 --- {end_time}')
26
+
jable/run.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from scrapy.crawler import CrawlerProcess
2
+ from scrapy.utils.project import get_project_settings
3
+ from jable.spiders.example import ExampleSpider
4
+
5
+
6
+
7
+
8
+ if __name__ == "__main__":
9
+
10
+ process = CrawlerProcess(get_project_settings())
11
+ process.crawl(ExampleSpider)
12
+ process.start()
jable/scrapy.cfg ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Automatically created by: scrapy startproject
2
+ #
3
+ # For more information about the [deploy] section see:
4
+ # https://scrapyd.readthedocs.io/en/latest/deploy.html
5
+
6
+ [settings]
7
+ default = jable.settings
8
+
9
+ [deploy]
10
+ #url = http://localhost:6800/
11
+ project = jable
jable/upload.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from pathlib import Path
3
+ import os
4
+ from baidu import start as baidu_start
5
+ import subprocess
6
+ def changeTSToPng(ts_path: Path):
7
+
8
+ png_path = Path(__file__).parent.joinpath('hide.png')
9
+ print(png_path)
10
+ ts_datas = list(ts_path.glob('*.ts'))
11
+ for ts in ts_datas:
12
+ # print(ts)
13
+ ts_name = str(ts).split(os.sep)[-1].split('.')[0] + '.png'
14
+ with open(ts_path.joinpath(ts_name), 'wb') as f:
15
+ with open(png_path, 'rb') as png_f:
16
+ f.write(png_f.read())
17
+ with open(ts, 'rb') as ts1:
18
+ f.write(ts1.read())
19
+ ts.unlink()
20
+
21
+ def upload(URLS):
22
+ tmp_path = Path.home().joinpath('tmps')
23
+ download_path = Path.home().joinpath('download_videos')
24
+
25
+ print(tmp_path)
26
+ # return
27
+ for name, url in URLS:
28
+ ts_path = tmp_path.joinpath(name).joinpath('0____')
29
+ m3u8_path = ts_path.parent.joinpath('raw.m3u8')
30
+ ARGS = [
31
+ "N_m3u8DL-RE",
32
+ url,
33
+ "--thread-count",
34
+ "11",
35
+ "--skip-merge",
36
+ "True",
37
+ "--tmp-dir",
38
+ tmp_path,
39
+ "--save-dir",
40
+ download_path,
41
+ "--save-name",
42
+ name,
43
+ "-H",
44
+ "Origin: https://www.4kvm.net",
45
+ "-H",
46
+ "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36"
47
+ ]
48
+ subprocess.run(ARGS)
49
+
50
+ # 替换ts 文件变成 png
51
+ changeTSToPng(ts_path)
52
+ baidu_start(ts_path, m3u8_path, download_path)
53
+ print("*" * 50)