mirror of
https://github.com/hect0x7/JMComic-Crawler-Python.git
synced 2025-09-26 22:31:30 +08:00
parent
49e6d26aaf
commit
fc3842f2ba
31
README.md
31
README.md
@ -6,8 +6,6 @@
|
||||
|
||||
**友情提示:珍爱JM,为了减轻JM的服务器压力,请不要一次性爬取太多本子,西门🙏🙏🙏**.
|
||||
|
||||
|
||||
|
||||
## 安装教程
|
||||
|
||||
* 通过pip官方源安装(推荐,并且更新也是这个命令)
|
||||
@ -21,8 +19,6 @@
|
||||
pip install -e ./
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 快速上手
|
||||
|
||||
使用下面的两行代码,即可实现功能:把某个本子(album)里的所有章节(photo)下载到本地
|
||||
@ -45,14 +41,13 @@ $ jmcomic 422866
|
||||
# 更多用法请参考文件 usage/usage_cl.py (命令行使用介绍)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## 进阶使用
|
||||
|
||||
进阶使用可以参考本repo下usage文件夹内的示例代码文件,下面是各个文件的作用,你可以挑感兴趣的阅读:
|
||||
|
||||
- API上手介绍: `getting_started.py`
|
||||
- 命令行使用介绍: `usage_cl.py`
|
||||
- 配置客户端的实现(网页端、移动端): `usage_configure_client_impl.py`
|
||||
- 使用API实现简单功能: `usage_simple.py`
|
||||
- 演示jmcomic模块的可自定义功能点: `usage_custom.py`
|
||||
- 使用API的Filter过滤功能: `usage_feature_filter.py`
|
||||
@ -66,39 +61,33 @@ $ jmcomic 422866
|
||||
- 测试你的ip可以访问哪些禁漫域名: `pick_domain.py`
|
||||
- 基于GitHub Actions下载本子: `workflow_download.py`
|
||||
|
||||
|
||||
|
||||
## 项目特点
|
||||
|
||||
- **绕过Cloudflare的反爬虫**
|
||||
- 支持使用**命令行**下载本子,无需写Python代码,简单易用
|
||||
- 支持使用**GitHub Actions**下载本子,网页上直接输入本子id就能下载([教程:使用GitHub Actions下载禁漫本子](./assets/docs/教程:使用GitHub%20Actions下载禁漫本子.md))
|
||||
- 支持**网页端**和**移动端**两种客户端实现,可通过配置切换(**移动端不限ip兼容性好,网页端限制ip地区但效率高**)
|
||||
- 支持**自动重试和域名切换**机制
|
||||
- **多线程下载**(可细化到一图一线程,效率极高)
|
||||
- 跟进了JM最新的图片分割算法(2023-02-08)
|
||||
- **可配置性强**
|
||||
|
||||
- 不配置也能使用,十分方便
|
||||
- 配置可以从**配置文件**生成,支持多种文件格式
|
||||
- 配置点有:`是否使用磁盘缓存` `并发下载图片数` `图片类型转换` `下载路径` `请求元信息(headers,cookies,proxies)`等
|
||||
- 配置点有:`请求域名` `客户端实现` `是否使用磁盘缓存` `同时下载的章节/图片数量` `图片格式转换` `下载路径规则` `请求元信息(headers,cookies,proxies)`等
|
||||
- **可扩展性强**
|
||||
|
||||
- **支持Plugin插件,可以方便地扩展功能,以及使用别人的插件**
|
||||
- 目前内置支持的插件有:`登录插件` `硬件占用监控插件` `只下载新章插件` `压缩文件插件`
|
||||
- 支持自定义本子/章节/图片下载前后的回调函数
|
||||
- 支持自定义debug日志
|
||||
- 支持自定义类:Downloader(负责调度)/Option(负责配置)/Client(负责请求)/实体类 等等
|
||||
- ......
|
||||
- 支持**自动重试和域名切换**机制
|
||||
- **多线程下载**(可细化到一图一线程,效率极高)
|
||||
- 跟进了JM最新的图片分割算法(2023-02-08)
|
||||
|
||||
|
||||
|
||||
- 支持自定义类:`Downloader(负责调度)` `Option(负责配置)` `Client(负责请求)` `实体类`等
|
||||
|
||||
## 使用小说明
|
||||
|
||||
* Python >= 3.7
|
||||
* 个人项目,文档和示例会有不及时之处,可以Issue提问
|
||||
|
||||
|
||||
|
||||
|
||||
## 项目文件夹介绍
|
||||
|
||||
* assets:存放一些非代码的资源文件
|
||||
@ -111,8 +100,6 @@ $ jmcomic 422866
|
||||
* tests:测试目录,存放测试代码,使用unittest
|
||||
* usage:用法目录,存放示例/使用代码
|
||||
|
||||
|
||||
|
||||
## 感谢以下项目
|
||||
|
||||
### 图片分割算法代码+禁漫移动端API
|
||||
|
@ -1,15 +1,16 @@
|
||||
# GitHub Actions 下载脚本配置
|
||||
version: '2.0'
|
||||
|
||||
dir_rule:
|
||||
base_dir: ${JM_DOWNLOAD_DIR}
|
||||
rule: Bd_Aauthor_Atitle_Pindex
|
||||
|
||||
|
||||
client:
|
||||
domain:
|
||||
- jmcomic1.me
|
||||
- jmcomic.me
|
||||
impl: api
|
||||
domain: [
|
||||
"www.jmapinode1.cc",
|
||||
"www.jmapinode2.cc",
|
||||
"www.jmapinode3.cc",
|
||||
"www.jmapibranch2.cc",
|
||||
]
|
||||
|
||||
# 插件配置
|
||||
plugin:
|
||||
@ -18,10 +19,3 @@ plugin:
|
||||
kwargs:
|
||||
interval: 0.5 # 间隔时间
|
||||
enable_warning: false # 不告警
|
||||
|
||||
# after_album:
|
||||
# - plugin: zip # 压缩文件插件
|
||||
# kwargs:
|
||||
# level: album # 按照章节,一个章节一个压缩文件
|
||||
# filename_rule: Atitle # 压缩文件的命名规则
|
||||
# zip_dir: ${JM_ZIP_DIR} # 压缩文件存放的文件夹
|
||||
|
@ -2,7 +2,7 @@
|
||||
# 被依赖方 <--- 使用方
|
||||
# config <--- entity <--- toolkit <--- client <--- option <--- downloader
|
||||
|
||||
__version__ = '2.3.3'
|
||||
__version__ = '2.3.4'
|
||||
|
||||
from .api import *
|
||||
from .jm_plugin import *
|
||||
|
@ -1,11 +1,12 @@
|
||||
from .jm_client_interface import *
|
||||
|
||||
|
||||
# 抽象基类,实现了域名管理,发请求,重试机制,debug,缓存等功能
|
||||
class AbstractJmClient(
|
||||
JmcomicClient,
|
||||
PostmanProxy,
|
||||
):
|
||||
client_key: None
|
||||
client_key = None
|
||||
|
||||
def __init__(self,
|
||||
postman: Postman,
|
||||
@ -145,11 +146,11 @@ class AbstractJmClient(
|
||||
def is_cache_enabled(self) -> bool:
|
||||
return getattr(self, '__enable_cache__', False)
|
||||
|
||||
def get_jmcomic_url(self, postman=None):
|
||||
return JmModuleConfig.get_jmcomic_url(postman or self.get_root_postman())
|
||||
def get_html_domain(self, postman=None):
|
||||
return JmModuleConfig.get_html_domain(postman or self.get_root_postman())
|
||||
|
||||
def get_jmcomic_domain_all(self, postman=None):
|
||||
return JmModuleConfig.get_jmcomic_domain_all(postman or self.get_root_postman())
|
||||
def get_html_domain_all(self, postman=None):
|
||||
return JmModuleConfig.get_html_domain_all(postman or self.get_root_postman())
|
||||
|
||||
def get_domain_list(self):
|
||||
return self.domain_list
|
||||
@ -390,6 +391,7 @@ class JmHtmlClient(AbstractJmClient):
|
||||
)
|
||||
|
||||
|
||||
# 基于禁漫移动端(APP)实现的JmClient
|
||||
class JmApiClient(AbstractJmClient):
|
||||
client_key = 'api'
|
||||
API_SEARCH = '/search'
|
||||
|
@ -373,14 +373,22 @@ class JmcomicClient(
|
||||
JmSearchAlbumClient,
|
||||
Postman,
|
||||
):
|
||||
def get_jmcomic_url(self):
|
||||
return JmModuleConfig.get_jmcomic_url()
|
||||
|
||||
def get_jmcomic_domain_all(self):
|
||||
return JmModuleConfig.get_jmcomic_domain_all()
|
||||
client_key: None
|
||||
|
||||
def get_domain_list(self) -> List[str]:
|
||||
"""
|
||||
获取当前client的域名配置
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def set_domain_list(self, domain_list: List[str]):
|
||||
"""
|
||||
设置当前client的域名配置
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def get_html_domain(self):
|
||||
return JmModuleConfig.get_html_domain()
|
||||
|
||||
def get_html_domain_all(self):
|
||||
return JmModuleConfig.get_html_domain_all()
|
||||
|
@ -52,18 +52,27 @@ class JmModuleConfig:
|
||||
SCRAMBLE_NUM_8 = 421926 # 2023-02-08后改了图片切割算法
|
||||
SCRAMBLE_CACHE = {}
|
||||
|
||||
# API的相关配置
|
||||
# 移动端api密钥
|
||||
# 移动端API的相关配置
|
||||
# API密钥
|
||||
MAGIC_18COMICAPPCONTENT = '18comicAPPContent'
|
||||
# 移动端的图片域名
|
||||
DOMAIN_IMAGE_LIST = [f"cdn-msp.jmapiproxy{i}.cc" for i in range(1, 4)]
|
||||
|
||||
# 下载时的一些默认值配置
|
||||
default_author = 'default-author'
|
||||
# 域名配置 - 移动端
|
||||
# 图片域名
|
||||
DOMAIN_API_IMAGE_LIST = [f"cdn-msp.jmapiproxy{i}.cc" for i in range(1, 4)]
|
||||
# API域名
|
||||
DOMAIN_API_LIST = [
|
||||
"www.jmapinode1.cc",
|
||||
"www.jmapinode2.cc",
|
||||
"www.jmapinode3.cc",
|
||||
"www.jmapibranch2.cc",
|
||||
]
|
||||
|
||||
# 模块级别的可重写配置
|
||||
DOMAIN = None
|
||||
DOMAIN_LIST = None
|
||||
# 域名配置 - 网页端
|
||||
# 无需配置,默认为None,需要的时候会发起请求获得
|
||||
DOMAIN_HTML = None
|
||||
DOMAIN_HTML_LIST = None
|
||||
|
||||
# 模块级别的可重写类配置
|
||||
CLASS_DOWNLOADER = None
|
||||
CLASS_OPTION = None
|
||||
CLASS_ALBUM = None
|
||||
@ -86,6 +95,8 @@ class JmModuleConfig:
|
||||
enable_jm_debug = True
|
||||
# debug时解码url
|
||||
decode_url_when_debug = True
|
||||
# 下载时的一些默认值配置
|
||||
default_author = 'default-author'
|
||||
|
||||
@classmethod
|
||||
def downloader_class(cls):
|
||||
@ -139,18 +150,18 @@ class JmModuleConfig:
|
||||
return clazz
|
||||
|
||||
@classmethod
|
||||
@field_cache("DOMAIN")
|
||||
def domain(cls, postman=None):
|
||||
@field_cache("DOMAIN_HTML")
|
||||
def get_html_domain(cls, postman=None):
|
||||
"""
|
||||
由于禁漫的域名经常变化,调用此方法可以获取一个当前可用的最新的域名 domain,
|
||||
并且设置把 domain 设置为禁漫模块的默认域名。
|
||||
这样一来,配置文件也不用配置域名了,一切都在运行时动态获取。
|
||||
"""
|
||||
from .jm_toolkit import JmcomicText
|
||||
return JmcomicText.parse_to_jm_domain(cls.get_jmcomic_url(postman))
|
||||
return JmcomicText.parse_to_jm_domain(cls.get_html_url(postman))
|
||||
|
||||
@classmethod
|
||||
def get_jmcomic_url(cls, postman=None):
|
||||
def get_html_url(cls, postman=None):
|
||||
"""
|
||||
访问禁漫的永久网域,从而得到一个可用的禁漫网址
|
||||
@return: https://jm-comic2.cc
|
||||
@ -158,14 +169,14 @@ class JmModuleConfig:
|
||||
postman = postman or cls.new_postman(session=True)
|
||||
|
||||
url = postman.with_redirect_catching().get(cls.JM_REDIRECT_URL)
|
||||
cls.jm_debug('获取禁漫URL', f'[{cls.JM_REDIRECT_URL}] → [{url}]')
|
||||
cls.jm_debug('获取禁漫网页URL', f'[{cls.JM_REDIRECT_URL}] → [{url}]')
|
||||
return url
|
||||
|
||||
@classmethod
|
||||
@field_cache("DOMAIN_LIST")
|
||||
def get_jmcomic_domain_all(cls, postman=None):
|
||||
@field_cache("DOMAIN_HTML_LIST")
|
||||
def get_html_domain_all(cls, postman=None):
|
||||
"""
|
||||
访问禁漫发布页,得到所有禁漫的域名
|
||||
访问禁漫发布页,得到所有的禁漫网页域名
|
||||
|
||||
@return: ['18comic.vip', ..., 'jm365.xyz/ZNPJam'], 最后一个是【APP軟件下載】
|
||||
"""
|
||||
@ -179,7 +190,7 @@ class JmModuleConfig:
|
||||
from .jm_toolkit import JmcomicText
|
||||
domain_list = JmcomicText.analyse_jm_pub_html(resp.text)
|
||||
|
||||
cls.jm_debug('获取禁漫全部域名', f'[{resp.url}] → {domain_list}')
|
||||
cls.jm_debug('获取禁漫网页全部域名', f'[{resp.url}] → {domain_list}')
|
||||
return domain_list
|
||||
|
||||
@classmethod
|
||||
@ -240,6 +251,8 @@ class JmModuleConfig:
|
||||
|
||||
# option 默认配置字典
|
||||
JM_OPTION_VER = '2.1'
|
||||
CLIENT_IMPL_DEFAULT = 'html'
|
||||
|
||||
default_option_dict: dict = {
|
||||
'version': JM_OPTION_VER,
|
||||
'debug': None,
|
||||
@ -262,7 +275,7 @@ class JmModuleConfig:
|
||||
'headers': None,
|
||||
}
|
||||
},
|
||||
'impl': 'html',
|
||||
'impl': None,
|
||||
'retry_times': 5
|
||||
},
|
||||
'plugin': {},
|
||||
@ -293,10 +306,9 @@ class JmModuleConfig:
|
||||
if client['cache'] is None:
|
||||
client['cache'] = True
|
||||
|
||||
# headers
|
||||
meta_data = client['postman']['meta_data']
|
||||
if meta_data['headers'] is None:
|
||||
meta_data['headers'] = cls.headers()
|
||||
# client impl
|
||||
if client['impl'] is None:
|
||||
client['impl'] = cls.CLIENT_IMPL_DEFAULT
|
||||
|
||||
# threading photo
|
||||
dt = option_dict['download']['threading']
|
||||
@ -312,7 +324,12 @@ class JmModuleConfig:
|
||||
|
||||
@classmethod
|
||||
def register_client(cls, client_class):
|
||||
cls.CLASS_CLIENT_IMPL[client_class.client_key] = client_class
|
||||
client_key = getattr(client_class, 'client_key', None)
|
||||
if client_key is None:
|
||||
from .jm_toolkit import ExceptionTool
|
||||
ExceptionTool.raises(f'未配置client_key, class: {client_class}')
|
||||
|
||||
cls.CLASS_CLIENT_IMPL[client_key] = client_class
|
||||
|
||||
|
||||
jm_debug = JmModuleConfig.jm_debug
|
||||
|
@ -300,6 +300,10 @@ class JmOption:
|
||||
|
||||
def new_jm_client(self, domain_list=None, impl=None, **kwargs) -> JmcomicClient:
|
||||
postman_conf: dict = self.client.postman.src_dict
|
||||
impl = impl or self.client.impl
|
||||
if domain_list is None:
|
||||
domain_list = self.client.domain
|
||||
domain_list: List[str]
|
||||
|
||||
# support kwargs overwrite meta_data
|
||||
if len(kwargs) != 0:
|
||||
@ -311,15 +315,16 @@ class JmOption:
|
||||
postman = Postmans.create(data=postman_conf)
|
||||
|
||||
# domain_list
|
||||
if domain_list is None:
|
||||
domain_list = self.client.domain
|
||||
|
||||
domain_list: List[str]
|
||||
if len(domain_list) == 0:
|
||||
domain_list = [JmModuleConfig.domain()]
|
||||
domain_list = self.decide_client_domain(impl)
|
||||
|
||||
# headers
|
||||
meta_data = postman_conf['meta_data']
|
||||
if meta_data['headers'] is None:
|
||||
meta_data['headers'] = JmModuleConfig.headers(domain_list[0])
|
||||
|
||||
# client
|
||||
client = JmModuleConfig.client_impl_class(impl or self.client.impl)(
|
||||
client = JmModuleConfig.client_impl_class(impl)(
|
||||
postman,
|
||||
self.client.retry_times,
|
||||
fallback_domain_list=domain_list,
|
||||
@ -331,6 +336,28 @@ class JmOption:
|
||||
|
||||
return client
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def decide_client_domain(self, client_key: str) -> List[str]:
|
||||
def is_client_type(ct: Type[JmcomicClient]):
|
||||
if client_key == ct:
|
||||
return True
|
||||
|
||||
clazz = JmModuleConfig.client_impl_class(client_key)
|
||||
if issubclass(clazz, ct):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
if is_client_type(JmApiClient):
|
||||
# 移动端
|
||||
return JmModuleConfig.DOMAIN_API_LIST
|
||||
|
||||
if is_client_type(JmHtmlClient):
|
||||
# 网页端
|
||||
return [JmModuleConfig.get_html_domain()]
|
||||
|
||||
ExceptionTool.raises(f'没有配置域名,且是无法识别的client类型: {client_key}')
|
||||
|
||||
@classmethod
|
||||
def merge_default_dict(cls, user_dict, default_dict=None):
|
||||
"""
|
||||
|
@ -465,7 +465,7 @@ class JmApiAdaptTool:
|
||||
fields['sort'] = sort
|
||||
fields['scramble_id'] = '0'
|
||||
import random
|
||||
fields['data_original_domain'] = random.choice(JmModuleConfig.DOMAIN_IMAGE_LIST)
|
||||
fields['data_original_domain'] = random.choice(JmModuleConfig.DOMAIN_API_IMAGE_LIST)
|
||||
|
||||
|
||||
class JmImageTool:
|
||||
|
@ -9,33 +9,66 @@ from jmcomic import *
|
||||
# set encoding
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, 'utf-8')
|
||||
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, 'utf-8')
|
||||
# 获取项目根目录
|
||||
project_dir = os.path.abspath(os.path.dirname(__file__) + '/../..')
|
||||
os.chdir(project_dir)
|
||||
|
||||
|
||||
def ts():
|
||||
return time_stamp(False)
|
||||
|
||||
|
||||
skip_time_cost_debug = file_exists('./.idea')
|
||||
|
||||
cost_time_dict = {}
|
||||
|
||||
|
||||
class JmTestConfigurable(unittest.TestCase):
|
||||
option: JmOption = None
|
||||
client: JmcomicClient = None
|
||||
project_dir: str = None
|
||||
project_dir: str = project_dir
|
||||
|
||||
def setUp(self) -> None:
|
||||
print_sep('>')
|
||||
if skip_time_cost_debug:
|
||||
return
|
||||
method_name = self._testMethodName
|
||||
cost_time_dict[method_name] = ts()
|
||||
print_eye_catching(f' [{format_ts()} | {method_name}] '.center(70, '🚀'))
|
||||
|
||||
def tearDown(self) -> None:
|
||||
print_sep('<')
|
||||
if skip_time_cost_debug:
|
||||
return
|
||||
method_name = self._testMethodName
|
||||
begin = cost_time_dict[method_name]
|
||||
end = ts()
|
||||
print_eye_catching(f' [cost {end - begin:.02f}s | {self._testMethodName}] '.center(70, '✅'))
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# 获取项目根目录
|
||||
cls.project_dir = os.path.abspath(os.path.dirname(__file__) + '/../..')
|
||||
os.chdir(cls.project_dir)
|
||||
|
||||
# 设置 JmOption,JmcomicClient
|
||||
option = create_option_by_env('JM_OPTION_PATH_TEST')
|
||||
try:
|
||||
option = create_option_by_env('JM_OPTION_PATH_TEST')
|
||||
except JmcomicException:
|
||||
option = create_option('./assets/config/option_test.yml')
|
||||
|
||||
cls.option = option
|
||||
cls.client = option.build_jm_client()
|
||||
|
||||
# 跨平台设置
|
||||
cls.adapt_os()
|
||||
|
||||
if skip_time_cost_debug:
|
||||
return
|
||||
cost_time_dict[cls.__name__] = ts()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls) -> None:
|
||||
if skip_time_cost_debug:
|
||||
return
|
||||
begin = cost_time_dict[cls.__name__]
|
||||
end = ts()
|
||||
print_eye_catching(f' [total cost {end - begin:.02f}s | {cls.__name__}] '.center(60, '-'))
|
||||
|
||||
@classmethod
|
||||
def use_option(cls, op_filename: str) -> JmOption:
|
||||
return create_option(f'./assets/config/{op_filename}')
|
||||
|
@ -45,8 +45,8 @@ class Test_Api(JmTestConfigurable):
|
||||
|
||||
def test_get_jmcomic_url(self):
|
||||
func_list = {
|
||||
self.client.get_jmcomic_url,
|
||||
self.client.get_jmcomic_domain_all,
|
||||
self.client.get_html_domain,
|
||||
self.client.get_html_domain_all,
|
||||
# JmModuleConfig.get_jmcomic_url,
|
||||
# JmModuleConfig.get_jmcomic_domain_all,
|
||||
}
|
||||
|
@ -60,6 +60,8 @@ class Test_Client(JmTestConfigurable):
|
||||
JmModuleConfig.raise_exception_executor = default_raise_exception_executor
|
||||
ExceptionTool.replace_old_exception_executor(raises)
|
||||
self.assertRaises(B, JmcomicText.parse_to_album_id, 'asdhasjhkd')
|
||||
# 还原
|
||||
JmModuleConfig.raise_exception_executor = default_raise_exception_executor
|
||||
|
||||
def test_detail_property_list(self):
|
||||
album = self.client.get_album_detail(410090)
|
||||
|
@ -36,3 +36,63 @@ class Test_Custom(JmTestConfigurable):
|
||||
os.path.abspath(save_dir),
|
||||
os.path.abspath(base_dir + dic[1] + '/' + dic[2]),
|
||||
)
|
||||
|
||||
def test_extends_api_client(self):
|
||||
class MyClient(JmApiClient):
|
||||
pass
|
||||
|
||||
JmModuleConfig.register_client(MyClient)
|
||||
|
||||
self.assertListEqual(
|
||||
JmModuleConfig.DOMAIN_API_LIST,
|
||||
self.option.new_jm_client(domain_list=[], impl=MyClient.client_key).get_domain_list()
|
||||
)
|
||||
|
||||
def test_extends_html_client(self):
|
||||
class MyClient(JmHtmlClient):
|
||||
pass
|
||||
|
||||
JmModuleConfig.register_client(MyClient)
|
||||
|
||||
html_domain = self.client.get_html_domain()
|
||||
self.assertListEqual(
|
||||
[html_domain],
|
||||
self.option.new_jm_client(domain_list=[], impl=MyClient.client_key).get_domain_list()
|
||||
)
|
||||
|
||||
def test_client_key_missing(self):
|
||||
class MyClient(JmcomicClient):
|
||||
pass
|
||||
|
||||
# '不重写 client_key'
|
||||
self.assertRaises(
|
||||
JmModuleConfig.CLASS_EXCEPTION,
|
||||
JmModuleConfig.register_client,
|
||||
MyClient,
|
||||
)
|
||||
|
||||
def test_custom_client_empty_domain(self):
|
||||
class MyClient(JmcomicClient):
|
||||
client_key = 'myclient'
|
||||
pass
|
||||
|
||||
JmModuleConfig.register_client(MyClient)
|
||||
# '自定义client,不配置域名'
|
||||
self.assertRaises(
|
||||
JmModuleConfig.CLASS_EXCEPTION,
|
||||
self.option.new_jm_client,
|
||||
domain_list=[],
|
||||
impl=MyClient.client_key,
|
||||
)
|
||||
|
||||
def test_client_empty_domain(self):
|
||||
class MyClient(JmApiClient):
|
||||
client_key = 'myclient'
|
||||
pass
|
||||
|
||||
JmModuleConfig.register_client(MyClient)
|
||||
self.assertListEqual(
|
||||
JmModuleConfig.DOMAIN_API_LIST,
|
||||
self.option.new_jm_client(domain_list=[], impl=MyClient.client_key).get_domain_list(),
|
||||
msg='继承client,不配置域名',
|
||||
)
|
||||
|
@ -12,6 +12,21 @@ jmcomic.download_album('422866') # 传入要下载的album的id,即可下载
|
||||
# 如果没有配置,则会使用 JmOption.default(),下载的路径是[当前工作文件夹/本子章节名称/图片].
|
||||
|
||||
|
||||
"""
|
||||
--------------------
|
||||
批量下载介绍
|
||||
--------------------
|
||||
"""
|
||||
from jmcomic import download_album
|
||||
|
||||
# 如果你想要批量下载,可以使用 list/set/tuple/生成器 作为第一个参数。
|
||||
# 第二个参数依然是可选的JmOption对象
|
||||
download_album(['422866', '1', '2', '3']) # list
|
||||
download_album({'422866', '1', '2', '3'}) # set
|
||||
download_album(('422866', '1', '2', '3')) # tuple
|
||||
download_album(aid for aid in ('422866', '1', '2', '3')) # 生成器
|
||||
|
||||
|
||||
"""
|
||||
--------------------
|
||||
配置文件上手
|
||||
@ -27,9 +42,9 @@ option.to_file('保存路径.yml') # yml格式
|
||||
option.to_file('保存路径.json') # json格式
|
||||
|
||||
# 如果你修改了默认配置,现在想用你修改后的配置来下载,使用如下代码
|
||||
option = JmOption.from_file('保存路径.yml')
|
||||
# 或者
|
||||
option = create_option('保存路径.yml')
|
||||
# 或者
|
||||
option = JmOption.from_file('保存路径.yml')
|
||||
# 使用你的option配置来下载
|
||||
jmcomic.download_album('23333', option)
|
||||
|
||||
@ -71,39 +86,3 @@ download_album('xxx', option)
|
||||
JmModuleConfig.default_option_dict['client']['postman']['meta_data']['proxies'] = ProxyBuilder.clash_proxy()
|
||||
# 调用下载api**不需要**传入option
|
||||
download_album('xxx')
|
||||
|
||||
|
||||
"""
|
||||
--------------------
|
||||
批量下载介绍
|
||||
--------------------
|
||||
"""
|
||||
from jmcomic import download_album
|
||||
|
||||
# 如果你想要批量下载,可以使用 list/set/tuple/生成器 作为第一个参数。
|
||||
# 第二个参数依然是可选的JmOption对象
|
||||
download_album(['422866', '1', '2', '3']) # list
|
||||
download_album({'422866', '1', '2', '3'}) # set
|
||||
download_album(('422866', '1', '2', '3')) # tuple
|
||||
download_album(aid for aid in ('422866', '1', '2', '3')) # 生成器
|
||||
|
||||
|
||||
"""
|
||||
--------------------
|
||||
获取域名介绍
|
||||
--------------------
|
||||
"""
|
||||
from jmcomic import JmModuleConfig
|
||||
|
||||
# 方式1: 访问禁漫发布页
|
||||
domain_ls = JmModuleConfig.get_jmcomic_url_all()
|
||||
print(url_ls)
|
||||
|
||||
# 方式2: 访问禁漫的永久网域
|
||||
url = JmModuleConfig.get_jmcomic_url()
|
||||
print(url)
|
||||
|
||||
# Q:download_album(xxx),没有传入option,那么访问JM使用的默认域名是什么?
|
||||
# A:默认域名是: JmModuleConfig.get_jmcomic_url(),如果想定制此默认域名:
|
||||
# 做法1: 定制option,见上
|
||||
# 做法2: JmModuleConfig.DOMAIN = '18comic.vip'
|
||||
|
41
usage/usage_configure_client_impl.py
Normal file
41
usage/usage_configure_client_impl.py
Normal file
@ -0,0 +1,41 @@
|
||||
"""
|
||||
|
||||
该文件介绍不同客户端、以及配置客户端实现的方式(移动端、网页端)
|
||||
|
||||
有关不同客户端的介绍:
|
||||
|
||||
网页端:
|
||||
代号为 html
|
||||
最早实现的客户端,由于网页信息较为丰富,相对而言效率比较高
|
||||
但是不同域名会限制ip,比如美国ip不能使用 18comic.vip 域名
|
||||
不同域名的网站稳定性也差别很大
|
||||
国内直连域名经常换
|
||||
|
||||
移动端:
|
||||
代号为 api
|
||||
在v2.3.3版本中引入的新客户端实现
|
||||
比网页端效率比较低,因为同样的信息量,移动端需要请求多个接口
|
||||
但是不限制ip地区,兼容性很好,国内直连比较稳定
|
||||
|
||||
|
||||
|
||||
1. 通过配置文件
|
||||
```yml
|
||||
client:
|
||||
# 下面两行只能选一行放到你的配置文件
|
||||
impl: api # 移动端
|
||||
impl: html # 网页端
|
||||
|
||||
```
|
||||
|
||||
2. 通过修改模块配置类 JmModuleConfig
|
||||
|
||||
# JmModuleConfig.CLIENT_IMPL_DEFAULT 该字段表示默认的客户端实现
|
||||
# 当配置文件中不存在client.impl配置时,就使用 JmModuleConfig.CLIENT_IMPL_DEFAULT
|
||||
# 你可以不在配置文件中配置,而是使用下面的代码(在你创建option前)
|
||||
|
||||
JmModuleConfig.CLIENT_IMPL_DEFAULT = 'api' # 移动端
|
||||
JmModuleConfig.CLIENT_IMPL_DEFAULT = 'html' # 网页端
|
||||
|
||||
|
||||
"""
|
@ -56,21 +56,20 @@ def custom_client_class():
|
||||
# 默认情况下,JmOption使用client类是根据配置项 `client.impl` 决定的
|
||||
# JmOption会根据`client.impl`到 JmModuleConfig.CLASS_CLIENT_IMPL 中查找
|
||||
|
||||
# 你可以自定义一个`client.impl`,例如 'my-client',
|
||||
# 或者使用jmcomic内置 'html' 和 'api',
|
||||
# 然后把你的`client.impl`和类一起配置到JmModuleConfig中
|
||||
# 自定义client的步骤如下
|
||||
|
||||
# 1. 自定义Client类
|
||||
class MyClient(JmHtmlClient):
|
||||
client_key = 'myclient'
|
||||
pass
|
||||
|
||||
# 2. 让你的配置类生效
|
||||
JmModuleConfig.CLASS_CLIENT_IMPL['my-client'] = MyClient
|
||||
# 2. 让MyClient生效
|
||||
JmModuleConfig.register_client(MyClient)
|
||||
|
||||
# 3. 在配置文件中使用你定义的client.impl,后续使用这个option即可
|
||||
"""
|
||||
client:
|
||||
impl: 'my-client'
|
||||
impl: myclient
|
||||
"""
|
||||
|
||||
|
||||
|
@ -22,7 +22,7 @@ client = option.build_jm_client()
|
||||
def download_jm_album():
|
||||
ls = str_to_list('''
|
||||
438696
|
||||
|
||||
https://18comic.vip/album/497896/
|
||||
|
||||
''')
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user