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