commit
b873cc2430
9 changed files with 514 additions and 0 deletions
@ -0,0 +1,175 @@ |
|||||
|
# Based on https://raw.githubusercontent.com/github/gitignore/main/Node.gitignore |
||||
|
|
||||
|
# Logs |
||||
|
|
||||
|
logs |
||||
|
_.log |
||||
|
npm-debug.log_ |
||||
|
yarn-debug.log* |
||||
|
yarn-error.log* |
||||
|
lerna-debug.log* |
||||
|
.pnpm-debug.log* |
||||
|
|
||||
|
# Caches |
||||
|
|
||||
|
.cache |
||||
|
|
||||
|
# Diagnostic reports (https://nodejs.org/api/report.html) |
||||
|
|
||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json |
||||
|
|
||||
|
# Runtime data |
||||
|
|
||||
|
pids |
||||
|
_.pid |
||||
|
_.seed |
||||
|
*.pid.lock |
||||
|
|
||||
|
# Directory for instrumented libs generated by jscoverage/JSCover |
||||
|
|
||||
|
lib-cov |
||||
|
|
||||
|
# Coverage directory used by tools like istanbul |
||||
|
|
||||
|
coverage |
||||
|
*.lcov |
||||
|
|
||||
|
# nyc test coverage |
||||
|
|
||||
|
.nyc_output |
||||
|
|
||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) |
||||
|
|
||||
|
.grunt |
||||
|
|
||||
|
# Bower dependency directory (https://bower.io/) |
||||
|
|
||||
|
bower_components |
||||
|
|
||||
|
# node-waf configuration |
||||
|
|
||||
|
.lock-wscript |
||||
|
|
||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html) |
||||
|
|
||||
|
build/Release |
||||
|
|
||||
|
# Dependency directories |
||||
|
|
||||
|
node_modules/ |
||||
|
jspm_packages/ |
||||
|
|
||||
|
# Snowpack dependency directory (https://snowpack.dev/) |
||||
|
|
||||
|
web_modules/ |
||||
|
|
||||
|
# TypeScript cache |
||||
|
|
||||
|
*.tsbuildinfo |
||||
|
|
||||
|
# Optional npm cache directory |
||||
|
|
||||
|
.npm |
||||
|
|
||||
|
# Optional eslint cache |
||||
|
|
||||
|
.eslintcache |
||||
|
|
||||
|
# Optional stylelint cache |
||||
|
|
||||
|
.stylelintcache |
||||
|
|
||||
|
# Microbundle cache |
||||
|
|
||||
|
.rpt2_cache/ |
||||
|
.rts2_cache_cjs/ |
||||
|
.rts2_cache_es/ |
||||
|
.rts2_cache_umd/ |
||||
|
|
||||
|
# Optional REPL history |
||||
|
|
||||
|
.node_repl_history |
||||
|
|
||||
|
# Output of 'npm pack' |
||||
|
|
||||
|
*.tgz |
||||
|
|
||||
|
# Yarn Integrity file |
||||
|
|
||||
|
.yarn-integrity |
||||
|
|
||||
|
# dotenv environment variable files |
||||
|
|
||||
|
.env |
||||
|
.env.development.local |
||||
|
.env.test.local |
||||
|
.env.production.local |
||||
|
.env.local |
||||
|
|
||||
|
# parcel-bundler cache (https://parceljs.org/) |
||||
|
|
||||
|
.parcel-cache |
||||
|
|
||||
|
# Next.js build output |
||||
|
|
||||
|
.next |
||||
|
out |
||||
|
|
||||
|
# Nuxt.js build / generate output |
||||
|
|
||||
|
.nuxt |
||||
|
dist |
||||
|
|
||||
|
# Gatsby files |
||||
|
|
||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js |
||||
|
|
||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support |
||||
|
|
||||
|
# public |
||||
|
|
||||
|
# vuepress build output |
||||
|
|
||||
|
.vuepress/dist |
||||
|
|
||||
|
# vuepress v2.x temp and cache directory |
||||
|
|
||||
|
.temp |
||||
|
|
||||
|
# Docusaurus cache and generated files |
||||
|
|
||||
|
.docusaurus |
||||
|
|
||||
|
# Serverless directories |
||||
|
|
||||
|
.serverless/ |
||||
|
|
||||
|
# FuseBox cache |
||||
|
|
||||
|
.fusebox/ |
||||
|
|
||||
|
# DynamoDB Local files |
||||
|
|
||||
|
.dynamodb/ |
||||
|
|
||||
|
# TernJS port file |
||||
|
|
||||
|
.tern-port |
||||
|
|
||||
|
# Stores VSCode versions used for testing VSCode extensions |
||||
|
|
||||
|
.vscode-test |
||||
|
|
||||
|
# yarn v2 |
||||
|
|
||||
|
.yarn/cache |
||||
|
.yarn/unplugged |
||||
|
.yarn/build-state.yml |
||||
|
.yarn/install-state.gz |
||||
|
.pnp.* |
||||
|
|
||||
|
# IntelliJ based IDEs |
||||
|
.idea |
||||
|
|
||||
|
# Finder (MacOS) folder config |
||||
|
.DS_Store |
||||
@ -0,0 +1,15 @@ |
|||||
|
# pic |
||||
|
|
||||
|
To install dependencies: |
||||
|
|
||||
|
```bash |
||||
|
bun install |
||||
|
``` |
||||
|
|
||||
|
To run: |
||||
|
|
||||
|
```bash |
||||
|
bun run index.ts |
||||
|
``` |
||||
|
|
||||
|
This project was created using `bun init` in bun v1.1.18. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. |
||||
@ -0,0 +1,53 @@ |
|||||
|
import { IncomingMessage } from "http" |
||||
|
import { fetch } from "bun" |
||||
|
import https from "https" |
||||
|
import fs from "fs" |
||||
|
|
||||
|
const maxNum = 3 |
||||
|
function request(opts: any, cb: any, num = 0) { |
||||
|
https.get(opts, function (res: IncomingMessage) { |
||||
|
if (res.statusCode === 302) { |
||||
|
if (num > maxNum) { |
||||
|
console.error("重定向次数超过三次,已强制终止"); |
||||
|
return |
||||
|
} |
||||
|
let url = new URL(res.headers['location']!) |
||||
|
if (url) { |
||||
|
console.log(`重定向:`, url.href); |
||||
|
num++ |
||||
|
// 重定向
|
||||
|
request({ |
||||
|
...opts, |
||||
|
hostname: url.hostname, |
||||
|
path: url.pathname + url.search, |
||||
|
}, cb, num) |
||||
|
} |
||||
|
} else if (res.statusCode === 200) { |
||||
|
cb && cb(res) |
||||
|
} else { |
||||
|
console.log(`当前${opts.hostname}请求${opts.path}未成功`, res.statusCode); |
||||
|
// res.on('data', (d) => {
|
||||
|
// process.stdout.write(d);
|
||||
|
// });
|
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 会重定向,需要想重定向请求
|
||||
|
request({ |
||||
|
hostname: "api.zzzmh.cn", |
||||
|
path: "/v2/bz/v3/getUrl/1b87e2c5880511ebb6edd017c2d2eca211", |
||||
|
headers: { |
||||
|
'Accept': '*/*', |
||||
|
'Accept-Encoding': 'gzip, deflate', |
||||
|
// "User-Agent": getRandomUserAgent(),
|
||||
|
// 'Connection': 'keep-alive',
|
||||
|
'Referer': 'https://bz.zzzmh.cn/' |
||||
|
} |
||||
|
}, function (res: IncomingMessage) { |
||||
|
const write = fs.createWriteStream("./a.jpg") |
||||
|
res.pipe(write) |
||||
|
write.on("error", function (err) { |
||||
|
console.log(err) |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,111 @@ |
|||||
|
import { fetch } from "bun" |
||||
|
import fs from "node:fs" |
||||
|
import * as cheerio from "cheerio" |
||||
|
|
||||
|
// https://bz.zzzmh.cn/index
|
||||
|
|
||||
|
// const stream = await (await fetch("https://api.zzzmh.cn/v2/bz/v3/getUrl/1b87e2c5880511ebb6edd017c2d2eca211")).arrayBuffer()
|
||||
|
// const stream = await (await fetch("https://api.zzzmh.cn/v2/bz/v3/getUrl/393204c88fff4715a4b4aa88aabdb2ed21")).arrayBuffer()
|
||||
|
// fs.writeFileSync("./a.png", Buffer.from(stream!))
|
||||
|
|
||||
|
let totalPage = 1 |
||||
|
async function downImageByPage(current: number) { |
||||
|
const res = await (await fetch("https://api.zzzmh.cn/v2/bz/v3/getData", { |
||||
|
method: "POST", |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json;charset=utf-8', |
||||
|
referer: "https://bz.zzzmh.cn/" |
||||
|
}, |
||||
|
body: `{ "size": 24, "current": ${current}, "sort": 0, "category": 0, "resolution": 0, "color": 0, "categoryId": 0, "ratio": 0 }` |
||||
|
})).json() |
||||
|
totalPage = res.data.totalPage |
||||
|
for (let i = 0; i < res.data.list.length; i++) { |
||||
|
const item = res.data.list[i]; |
||||
|
console.log(`正在下载第${i + 1}张图片`); |
||||
|
await (() => { |
||||
|
return new Promise((resolve) => { |
||||
|
setTimeout(() => { |
||||
|
resolve(1) |
||||
|
}, Math.random() * 2000 + 500); |
||||
|
}) |
||||
|
})(); |
||||
|
const stream = await (await fetch("https://api.zzzmh.cn/v2/bz/v3/getUrl/" + item.i + (item.t == 2 ? "21" : "11"))).arrayBuffer() |
||||
|
const p = "./dist/" + "page-" + current + "/" + item.i + ".png" |
||||
|
if (!await checkExist("./dist/" + "page-" + current)) { |
||||
|
fs.mkdirSync("./dist/" + "page-" + current, { recursive: true }) |
||||
|
} |
||||
|
const isExist = await checkExist(p) |
||||
|
if (!isExist) { |
||||
|
fs.writeFileSync(p, Buffer.from(stream!), { flag: "w" }) |
||||
|
} else { |
||||
|
console.log("文件已存在,跳过下载", item.i + ".png"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
downImageByPage(2) |
||||
|
|
||||
|
function checkExist(path: string) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
fs.access(path, fs.constants.F_OK, (err) => { |
||||
|
if (err) { |
||||
|
resolve(false) |
||||
|
} else { |
||||
|
resolve(true) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// async function readAllChunks(readableStream: any) {
|
||||
|
// const reader = readableStream.getReader();
|
||||
|
// const chunks = [];
|
||||
|
|
||||
|
// let done, value;
|
||||
|
// while (!done) {
|
||||
|
// ({ value, done } = await reader.read());
|
||||
|
// if (done) {
|
||||
|
// return chunks;
|
||||
|
// }
|
||||
|
// chunks.push(value);
|
||||
|
// }
|
||||
|
// }
|
||||
|
// const rr = await readAllChunks(stream)
|
||||
|
// fs.writeFileSync("./a.png", Buffer.from(rr!))
|
||||
|
|
||||
|
// const maxNum = 3
|
||||
|
// function request(opts: any, cb: any, num = 0) {
|
||||
|
// https.get(opts, function (res: IncomingMessage) {
|
||||
|
// if (res.statusCode === 302) {
|
||||
|
// if (num > maxNum) {
|
||||
|
// console.error("重定向次数超过三次,已强制终止");
|
||||
|
// return
|
||||
|
// }
|
||||
|
// let url = new URL(res.headers['location']!)
|
||||
|
// if (url) {
|
||||
|
// console.log(`重定向:`, url.href);
|
||||
|
// num++
|
||||
|
// // 重定向
|
||||
|
// request({
|
||||
|
// ...opts,
|
||||
|
// hostname: url.hostname,
|
||||
|
// path: url.pathname + url.search,
|
||||
|
// }, cb, num)
|
||||
|
// }
|
||||
|
// } else if (res.statusCode === 200) {
|
||||
|
// cb && cb(res)
|
||||
|
// } else {
|
||||
|
// console.log(`当前${opts.hostname}请求${opts.path}未成功`, res.statusCode);
|
||||
|
// // res.on('data', (d) => {
|
||||
|
// // process.stdout.write(d);
|
||||
|
// // });
|
||||
|
// }
|
||||
|
// })
|
||||
|
// }
|
||||
|
|
||||
|
// fetch("https://bz.zzzmh.cn/index")
|
||||
|
// .then((res) => res.text())
|
||||
|
// .then((html) => {
|
||||
|
// const $ = cheerio.load(html)
|
||||
|
// console.log($("title").text());
|
||||
|
// });
|
||||
Binary file not shown.
@ -0,0 +1,15 @@ |
|||||
|
{ |
||||
|
"name": "pic", |
||||
|
"module": "index.ts", |
||||
|
"type": "module", |
||||
|
"devDependencies": { |
||||
|
"@types/bun": "latest" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"typescript": "^5.0.0" |
||||
|
}, |
||||
|
"dependencies": { |
||||
|
"@types/node": "^22.7.9", |
||||
|
"cheerio": "^1.0.0" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
{ |
||||
|
"compilerOptions": { |
||||
|
// Enable latest features |
||||
|
"lib": ["ESNext", "DOM"], |
||||
|
"target": "ESNext", |
||||
|
"module": "ESNext", |
||||
|
"moduleDetection": "force", |
||||
|
"jsx": "react-jsx", |
||||
|
"allowJs": true, |
||||
|
|
||||
|
// Bundler mode |
||||
|
"moduleResolution": "bundler", |
||||
|
"allowImportingTsExtensions": true, |
||||
|
"verbatimModuleSyntax": true, |
||||
|
"noEmit": true, |
||||
|
|
||||
|
// Best practices |
||||
|
"strict": true, |
||||
|
"skipLibCheck": true, |
||||
|
"noFallthroughCasesInSwitch": true, |
||||
|
|
||||
|
// Some stricter flags (disabled by default) |
||||
|
"noUnusedLocals": false, |
||||
|
"noUnusedParameters": false, |
||||
|
"noPropertyAccessFromIndexSignature": false |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,64 @@ |
|||||
|
import { fetch } from "bun"; |
||||
|
import * as cheerio from "cheerio" |
||||
|
import fs from "node:fs"; |
||||
|
import path from "node:path"; |
||||
|
|
||||
|
const output = path.resolve(__dirname, "../dist/wallhaven") |
||||
|
|
||||
|
async function downImageByPage(page: number) { |
||||
|
const html = await (await fetch("https://wallhaven.cc/search?categories=110&purity=100&topRange=1M&sorting=toplist&order=desc&ai_art_filter=1&page=" + page, { |
||||
|
method: "GET" |
||||
|
})).text() |
||||
|
|
||||
|
const $ = cheerio.load(html); |
||||
|
|
||||
|
const previewList: string[] = [] |
||||
|
|
||||
|
$("#thumbs > section > ul > li > figure > a").each((index, el) => { |
||||
|
previewList[index] = $(el).attr("href") as string |
||||
|
}); |
||||
|
|
||||
|
for (let i = 0; i < previewList.length; i++) { |
||||
|
const previewURL = previewList[i]; |
||||
|
console.log(`正在下载第${i + 1}张图片`); |
||||
|
await (() => { |
||||
|
return new Promise((resolve) => { |
||||
|
setTimeout(() => { |
||||
|
resolve(1) |
||||
|
}, Math.random() * 2000 + 500); |
||||
|
}) |
||||
|
})(); |
||||
|
const html = await (await fetch(previewURL, { |
||||
|
method: "GET" |
||||
|
})).text() |
||||
|
const $ = cheerio.load(html); |
||||
|
const imageURL = $("#wallpaper").attr("src") as string |
||||
|
const p = path.resolve(output, "page-" + page + "/" + imageURL.split('/').slice(-1).join('')) |
||||
|
console.log("图片地址:", imageURL); |
||||
|
console.log("图片保存地址:", p); |
||||
|
if (!await checkExist(path.resolve(output, "page-" + page))) { |
||||
|
fs.mkdirSync(path.resolve(output, "page-" + page), { recursive: true }) |
||||
|
} |
||||
|
const isExist = await checkExist(p) |
||||
|
if (!isExist) { |
||||
|
const stream = await (await fetch(imageURL)).arrayBuffer() |
||||
|
fs.writeFileSync(p, Buffer.from(stream!), { flag: "w" }) |
||||
|
} else { |
||||
|
console.log("文件已存在,跳过下载", imageURL); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function checkExist(path: string) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
fs.access(path, fs.constants.F_OK, (err) => { |
||||
|
if (err) { |
||||
|
resolve(false) |
||||
|
} else { |
||||
|
resolve(true) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
downImageByPage(1) |
||||
@ -0,0 +1,54 @@ |
|||||
|
import { fetch } from "bun" |
||||
|
import fs from "node:fs" |
||||
|
import path from "node:path" |
||||
|
|
||||
|
const output = path.resolve(__dirname, "../dist/zzzmh") |
||||
|
|
||||
|
let totalPage = 1 |
||||
|
async function downImageByPage(current: number) { |
||||
|
const res = await (await fetch("https://api.zzzmh.cn/v2/bz/v3/getData", { |
||||
|
method: "POST", |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json;charset=utf-8', |
||||
|
referer: "https://bz.zzzmh.cn/" |
||||
|
}, |
||||
|
body: `{ "size": 24, "current": ${current}, "sort": 0, "category": 0, "resolution": 0, "color": 0, "categoryId": 0, "ratio": 0 }` |
||||
|
})).json() |
||||
|
totalPage = res.data.totalPage |
||||
|
for (let i = 0; i < res.data.list.length; i++) { |
||||
|
const item = res.data.list[i]; |
||||
|
console.log(`正在下载第${i + 1}张图片`); |
||||
|
await (() => { |
||||
|
return new Promise((resolve) => { |
||||
|
setTimeout(() => { |
||||
|
resolve(1) |
||||
|
}, Math.random() * 2000 + 500); |
||||
|
}) |
||||
|
})(); |
||||
|
const stream = await (await fetch("https://api.zzzmh.cn/v2/bz/v3/getUrl/" + item.i + (item.t == 2 ? "21" : "11"))).arrayBuffer() |
||||
|
const p = path.resolve(output, "page-" + current + "/" + item.i + ".png") |
||||
|
if (!await checkExist(path.resolve(output, "page-" + current))) { |
||||
|
fs.mkdirSync(path.resolve(output, "page-" + current), { recursive: true }) |
||||
|
} |
||||
|
const isExist = await checkExist(p) |
||||
|
if (!isExist) { |
||||
|
fs.writeFileSync(p, Buffer.from(stream!), { flag: "w" }) |
||||
|
} else { |
||||
|
console.log("文件已存在,跳过下载", item.i + ".png"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function checkExist(path: string) { |
||||
|
return new Promise((resolve, reject) => { |
||||
|
fs.access(path, fs.constants.F_OK, (err) => { |
||||
|
if (err) { |
||||
|
resolve(false) |
||||
|
} else { |
||||
|
resolve(true) |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
downImageByPage(2) |
||||
Loading…
Reference in new issue