mirror of
https://github.com/JoeanAmier/XHS-Downloader.git
synced 2025-12-26 04:48:05 +08:00
更新项目代码
This commit is contained in:
parent
b9dd05652d
commit
88cc09c371
14
README.md
14
README.md
@ -125,6 +125,20 @@ print(xhs.extract(video_demo, download=download))
|
||||
<img src="static/获取Cookie示意图.png" alt="">
|
||||
<h1>♥️ 支持项目</h1>
|
||||
<p>如果 <b>XHS-Downloader</b> 对您有帮助,请考虑为它点个 <b>Star</b> ⭐,感谢您的支持!</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th align="center">微信(WeChat)</th>
|
||||
<th align="center">支付宝(Alipay)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td align="center"><img src="./static/微信赞助二维码.png" alt="微信赞助二维码" height="200" width="200"></td>
|
||||
<td align="center"><img src="./static/支付宝赞助二维码.png" alt="支付宝赞助二维码" height="200" width="200"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>如果您愿意,可以考虑提供资助为 <b>XHS-Downloader</b> 提供额外的支持!</p>
|
||||
<h1>✉️ 联系作者</h1>
|
||||
<ul>
|
||||
<li>QQ: 2437596031</li>
|
||||
|
||||
11
main.py
11
main.py
@ -1,5 +1,4 @@
|
||||
from source import XHS
|
||||
from source import XHSDownloader
|
||||
|
||||
|
||||
def example():
|
||||
@ -8,6 +7,9 @@ def example():
|
||||
error_demo = "https://github.com/JoeanAmier/XHS_Downloader"
|
||||
image_demo = "https://www.xiaohongshu.com/explore/63b275a30000000019020185"
|
||||
video_demo = "https://www.xiaohongshu.com/explore/64edb460000000001f03cadc"
|
||||
multiple_demo = (
|
||||
"https://www.xiaohongshu.com/explore/63b275a30000000019020185 "
|
||||
"https://www.xiaohongshu.com/explore/64edb460000000001f03cadc")
|
||||
# 实例对象
|
||||
path = "" # 作品下载储存根路径,默认值:当前路径
|
||||
folder = "Download" # 作品下载文件夹名称(自动创建),默认值:Download
|
||||
@ -23,13 +25,14 @@ def example():
|
||||
timeout=timeout,
|
||||
chunk=chunk, ) # 使用自定义参数
|
||||
# xhs = XHS() # 使用默认参数
|
||||
download = True # 是否下载作品文件
|
||||
download = False # 是否下载作品文件
|
||||
# 返回作品详细信息,包括下载地址
|
||||
print(xhs.extract(error_demo)) # 获取数据失败时返回空字典
|
||||
print(xhs.extract(image_demo, download=download))
|
||||
print(xhs.extract(video_demo, download=download))
|
||||
print(xhs.extract(multiple_demo, download=download))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# example()
|
||||
XHSDownloader().run()
|
||||
example()
|
||||
# XHSDownloader().run()
|
||||
|
||||
@ -18,7 +18,7 @@ class Download:
|
||||
folder: str,
|
||||
headers: dict,
|
||||
proxies=None,
|
||||
chunk=256 * 1024, ):
|
||||
chunk=1024 * 1024, ):
|
||||
self.temp = root.joinpath("./temp")
|
||||
self.root = self.__init_root(root, path, folder)
|
||||
self.headers = self.__delete_cookie(headers)
|
||||
@ -30,14 +30,12 @@ class Download:
|
||||
self.chunk = chunk
|
||||
|
||||
def __init_root(self, root: Path, path: str, folder: str) -> Path:
|
||||
if path and (r := Path(path)).exists():
|
||||
if path and (r := Path(path)).is_dir():
|
||||
root = r.joinpath(folder or "Download")
|
||||
else:
|
||||
root = root.joinpath(folder or "Download")
|
||||
if not root.is_dir():
|
||||
root.mkdir()
|
||||
if not self.temp.is_dir():
|
||||
self.temp.mkdir()
|
||||
root.mkdir(exist_ok=True)
|
||||
self.temp.mkdir(exist_ok=True)
|
||||
return root
|
||||
|
||||
def run(self, urls: list, name: str, type_: int, log):
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
from requests import ReadTimeout
|
||||
from requests import exceptions
|
||||
from requests import get
|
||||
|
||||
@ -37,7 +36,7 @@ class Html:
|
||||
exceptions.SSLError,
|
||||
exceptions.ChunkedEncodingError,
|
||||
exceptions.ConnectionError,
|
||||
ReadTimeout,
|
||||
exceptions.ReadTimeout,
|
||||
):
|
||||
print("获取网页源码失败,请尝试设置 Cookie 后重试!")
|
||||
return ""
|
||||
|
||||
@ -2,7 +2,7 @@ from json import dump
|
||||
from json import load
|
||||
from pathlib import Path
|
||||
|
||||
__all__ = ['Settings', 'Batch']
|
||||
__all__ = ['Settings']
|
||||
|
||||
|
||||
class Settings:
|
||||
@ -13,7 +13,7 @@ class Settings:
|
||||
"cookie": "",
|
||||
"proxies": None,
|
||||
"timeout": 10,
|
||||
"chunk": 256 * 1024,
|
||||
"chunk": 1024 * 1024,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
@ -25,19 +25,9 @@ class Settings:
|
||||
|
||||
def create(self) -> dict:
|
||||
with self.file.open("w", encoding="utf-8") as f:
|
||||
dump(self.default, f, indent=2)
|
||||
dump(self.default, f, indent=4)
|
||||
return self.default
|
||||
|
||||
def update(self, data: dict):
|
||||
with self.file.open("w", encoding="utf-8") as f:
|
||||
dump(data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
class Batch:
|
||||
file = Path("../xhs.txt")
|
||||
|
||||
def read_txt(self) -> list:
|
||||
if self.file.is_file():
|
||||
with self.file.open("r") as f:
|
||||
return [i.rstrip('\n') for i in f.readlines()]
|
||||
return []
|
||||
dump(data, f, indent=4, ensure_ascii=False)
|
||||
|
||||
@ -18,7 +18,6 @@ from .Download import Download
|
||||
from .Explore import Explore
|
||||
from .Html import Html
|
||||
from .Image import Image
|
||||
from .Settings import Batch
|
||||
from .Settings import Settings
|
||||
from .Video import Video
|
||||
|
||||
@ -45,7 +44,7 @@ class XHS:
|
||||
cookie=None,
|
||||
proxies=None,
|
||||
timeout=10,
|
||||
chunk=256 * 1024,
|
||||
chunk=1024 * 1024,
|
||||
):
|
||||
self.__update_cookie(cookie)
|
||||
self.html = Html(self.headers, proxies, timeout)
|
||||
@ -72,16 +71,14 @@ class XHS:
|
||||
self.download.run(url, self.__naming_rules(container), 0, log)
|
||||
container["下载地址"] = url
|
||||
|
||||
def extract(self, url: str, download=False, log=None) -> dict:
|
||||
def extract(self, url: str, download=False, log=None) -> dict | list[dict]:
|
||||
if not self.__check(url):
|
||||
print(f"无效的作品链接: {url}")
|
||||
return {}
|
||||
html = self.html.get_html(url)
|
||||
if not html:
|
||||
return {}
|
||||
data = self.explore.run(html)
|
||||
if not data:
|
||||
print(f"获取作品数据失败: {url}")
|
||||
return {}
|
||||
if data["作品类型"] == "视频":
|
||||
self.__get_video(data, html, download, log)
|
||||
@ -105,21 +102,20 @@ class XHS:
|
||||
class XHSDownloader(App):
|
||||
VERSION = 1.6
|
||||
Beta = True
|
||||
CSS_PATH = Path(__file__).resolve().parent.parent.joinpath(
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CSS_PATH = ROOT.joinpath(
|
||||
"static/XHS-Downloader.tcss")
|
||||
BINDINGS = [
|
||||
Binding(key="q", action="quit", description="退出程序"),
|
||||
("d", "toggle_dark", "切换主题"),
|
||||
]
|
||||
APP = XHS(**Settings().run())
|
||||
Batch = Batch()
|
||||
|
||||
def compose(self) -> ComposeResult:
|
||||
yield Header()
|
||||
yield ScrollableContainer(Label("请输入小红书图文/视频作品链接:"),
|
||||
yield ScrollableContainer(Label("请输入小红书图文/视频作品链接(多个链接使用空格分隔):"),
|
||||
Input(placeholder="URL"),
|
||||
HorizontalScroll(Button("下载无水印图片/视频", id="solo"),
|
||||
Button("读取 xhs.txt 文件并批量下载作品", id="batch"),
|
||||
HorizontalScroll(Button("下载无水印图片/视频", id="deal"),
|
||||
Button("读取剪贴板", id="paste"),
|
||||
Button("清空输入框", id="reset"), ))
|
||||
yield Log(auto_scroll=True)
|
||||
@ -129,26 +125,22 @@ class XHSDownloader(App):
|
||||
self.title = f"小红书作品采集工具 V{self.VERSION}{" Beta" if self.Beta else ""}"
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
if event.button.id == "solo":
|
||||
self.solo()
|
||||
elif event.button.id == "batch":
|
||||
self.batch()
|
||||
if event.button.id == "deal":
|
||||
self.deal()
|
||||
elif event.button.id == "reset":
|
||||
self.query_one(Input).value = ""
|
||||
elif event.button.id == "paste":
|
||||
self.query_one(Input).value = paste()
|
||||
|
||||
def solo(self):
|
||||
def deal(self):
|
||||
url = self.query_one(Input).value
|
||||
log = self.query_one(Log)
|
||||
log.write_line(f"当前作品链接: {url}")
|
||||
self.APP.extract(url, True, log)
|
||||
|
||||
def batch(self):
|
||||
urls = self.Batch.read_txt()
|
||||
log = self.query_one(Log)
|
||||
if not urls:
|
||||
log.write_line("未检测到 xhs.txt 文件 或者 该文件为空!")
|
||||
for url in urls:
|
||||
log.write_line(f"当前作品链接: {url}")
|
||||
if not url:
|
||||
log.write_line("未输入任何作品链接!")
|
||||
else:
|
||||
self.APP.extract(url, True, log)
|
||||
self.query_one(Input).value = ""
|
||||
|
||||
|
||||
class FakeGUI:
|
||||
pass
|
||||
|
||||
@ -10,10 +10,7 @@ Button {
|
||||
margin: 1 1;
|
||||
text-style: bold;
|
||||
}
|
||||
Button#solo {
|
||||
tint: green 35%;
|
||||
}
|
||||
Button#batch {
|
||||
Button#deal {
|
||||
tint: green 35%;
|
||||
}
|
||||
Button#paste {
|
||||
|
||||
BIN
static/微信赞助二维码.png
Normal file
BIN
static/微信赞助二维码.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 102 KiB |
BIN
static/支付宝赞助二维码.png
Normal file
BIN
static/支付宝赞助二维码.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 140 KiB |
Loading…
x
Reference in New Issue
Block a user