Node.js同步XMLHttpRequest设置responseType为arraybuffer及图片解密问询
漫画网站图片爬取问题及解决方案
问题说明
- 爬取某漫画网站图片时,网站采用加密算法获取图片并解析为Blob链接,需在解密步骤后终止流程,返回
Uint8Array,再通过Python代码写入文件。 - 咨询:Node.js v18.3.0环境下,使用
async:false的XMLHttpRequest时,能否设置responseType为arraybuffer?
同步XHR设置responseType的说明
通过jsdom模拟的XMLHttpRequest在同步模式下理论上支持设置responseType: 'arraybuffer',但存在版本兼容性问题,且同步XHR本身不被现代环境推荐。更可靠的方式是改用Node.js原生http/https模块发起同步请求,直接获取二进制数据。
修正后的实现代码
1. getAndDecrypt.js(核心解密逻辑)
const CryptoJS = require("crypto-js"); const https = require("https"); const { URL } = require("url"); // 与网站一致的解密密钥 const secretKey = "my2ecret782ecret"; // 同步获取远程图片的ArrayBuffer function getSyncArrayBuffer(url) { const urlObj = new URL(url); let chunks = []; const buffer = Buffer.from(new Promise((resolve, reject) => { const req = https.request({ hostname: urlObj.hostname, path: urlObj.pathname + urlObj.search, method: 'GET' }, (res) => { res.on('data', chunk => chunks.push(chunk)); res.on('end', () => resolve(Buffer.concat(chunks))); }); req.on('error', reject); req.end(); })); return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength); } // 将CryptoJS WordArray转为Uint8Array function wordArrayToUint8Array(wordArray) { const uint8Array = new Uint8Array(wordArray.sigBytes); let byteIndex = 0; for (const word of wordArray.words) { uint8Array[byteIndex++] = (word >> 24) & 0xff; if (byteIndex === uint8Array.length) break; uint8Array[byteIndex++] = (word >> 16) & 0xff; if (byteIndex === uint8Array.length) break; uint8Array[byteIndex++] = (word >> 8) & 0xff; if (byteIndex === uint8Array.length) break; uint8Array[byteIndex++] = word & 0xff; if (byteIndex === uint8Array.length) break; } return uint8Array; } // AES解密函数 function decryptData(arrayBuffer, key) { const wordArray = CryptoJS.lib.WordArray.create(arrayBuffer); const decrypted = CryptoJS.AES.decrypt({ ciphertext: wordArray }, CryptoJS.enc.Utf8.parse(key), { iv: CryptoJS.enc.Utf8.parse(key), padding: CryptoJS.pad.Pkcs7 }); return wordArrayToUint8Array(decrypted); } // 对外暴露的入口函数 function getDecryptedImage(url) { const arrayBuffer = getSyncArrayBuffer(url); return decryptData(arrayBuffer, secretKey); } module.exports = { getDecryptedImage };
2. Python调用代码
import execjs def read_js_file(filename): with open(filename, 'r', encoding='utf-8') as f: return f.read() def get_decrypted_image(url): js_code = read_js_file('getAndDecrypt.js') ctx = execjs.compile(js_code) uint8array = ctx.call('getDecryptedImage', url) # 转换为Python可写入的bytes类型 return bytes(uint8array.values()) if __name__ == '__main__': target_url = 'https://img.mwimser2.cc/static/upload/book/id/18928/547567/4c8ca89af48c44d45893f959b16447b2.webp?v=20220724' image_bytes = get_decrypted_image(target_url) with open('a.webp', 'wb') as f: f.write(image_bytes)
关键修改点
- 替换
jsdom模拟XHR为Node.js原生https模块,彻底避免同步XHR的responseType兼容性问题。 - 补全原代码中缺失的二进制转换逻辑,确保数据流转正确。
- 优化解密后
Uint8Array到Pythonbytes的转换逻辑,保证写入文件的图片可正常打开。
内容的提问来源于stack exchange,提问作者openwld
相关产品推荐
相关产品推荐

