-
-
Save yige233/274fbd29ba26a6f9d75d698c1790e0d5 to your computer and use it in GitHub Desktop.
| /** | |
| * 批量下载自己已购买的电子书和个人文档 | |
| * 要求:至少有一台Kindle设备。 | |
| * 打开 https://www.amazon.cn/hz/mycd/myx/ ,然后按F12键进入Console(控制台),把代码全部复制并粘贴到控制台中,回车。 | |
| * 然后输入 download("ebook") ,下载所有的电子书 | |
| * 想下载个人文档,则是输入 download("pdoc") | |
| * 下载时如果某个文件下载失败,可以使用刚刚运行的函数(也就是 download() 或者 download("pdoc") )重新开始下载。在网页没被关闭的情况下,程序会忽略已经下载了的文件。 | |
| * 脚本运行期间请不要关闭网页,请允许网页自动下载多个文件 | |
| * 如果网页被关闭了,但恰巧你保存了上次下载任务返回的成功下载的文件列表, | |
| * 可以选择复制该列表中的所有文字,并将其作为 download 的第二个参数传入(如 download("ebook",["something","something else"]) ),这样程序同样会忽略已经下载了的文件。 | |
| * | |
| * 原来通过请求获取下载url的方法只适用于电子书,虽然有意识到下载链接似乎有一定规律,但也没多想, | |
| * 后来看到 https://github.com/yihong0618/Kindle_download_helper 这个项目,发现他是用拼接url而非请求获取url, | |
| * 这样可以获取个人文档的下载链接,于是研究了下拼接用的参数,现在这个脚本也能下载个人文档了 | |
| * 在 @Xpink1999 的帮助下,程序现在可以正常处理超过1000本书了。(成功下载了1763本书) | |
| * | |
| * 2024/6/9: 修改了下载模块,避免产生“fetch时设置了‘credentials: "include"’的情况下,响应cros头是‘*’,则请求失败”的情况。 | |
| * 这样的下载方式实际上和网页的下载方式相同,但也因此,程序无法检测书本是否下载成功。 | |
| * | |
| * @param {String} type 默认为 "ebook" ,下载电子书,若为 "pdoc", 则是下载个人文档 | |
| * @param {Array} completedList 数组,如果一本电子书的asin在其中就不会下载 | |
| * @param {Number} timeout 每个下载操作间隔的时间,单位为秒,默认为 20s | |
| * | |
| */ | |
| async function download(type = "ebook", completedList = [], timeout = 20) { | |
| function HTMLdecode(str) { | |
| const textarea = document.createElement("textarea"); | |
| textarea.innerHTML = str; | |
| return textarea.value; | |
| } | |
| async function request(activity, input) { | |
| const result = await fetch("https://www.amazon.cn/hz/mycd/digital-console/ajax", { | |
| headers: { | |
| "content-type": "application/x-www-form-urlencoded", | |
| }, | |
| method: "POST", | |
| body: [["activity", activity].join("="), ["activityInput", JSON.stringify(input)].join("="), ["csrfToken", encodeURIComponent(window.csrfToken)].join("=")].join("&"), | |
| credentials: "include", | |
| }); | |
| const resType = result.headers.get("content-type"); | |
| if (result.status == 200 && resType.includes("application/json")) { | |
| return await result.json(); | |
| } | |
| console.warn({ 状态: "请求失败", 状态码: result.status, post: { activity, input }, response: await result.text() }); | |
| return {}; | |
| } | |
| async function dlFile(url, fileName = "未命名文件") { | |
| const a = document.createElement("a"); | |
| a.href = url; | |
| a.download = fileName; | |
| a.click(); | |
| await new Promise((resolve) => setTimeout(resolve, timeout * 1000)); | |
| console.log("下载完成", fileName); | |
| } | |
| const batchSize = 1000, | |
| newCompletedList = [...completedList], | |
| docType = type == "pdoc" ? "PDOC" : "EBOK", | |
| ownershipDataCommon = { | |
| showSharedContent: true, | |
| fetchCriteria: { sortOrder: "DESCENDING", sortIndex: "DATE", startIndex: 0, batchSize: batchSize, totalContentCount: -1 }, | |
| surfaceType: "LargeDesktop", | |
| }, | |
| ownershipDataEbook = { | |
| contentType: "Ebook", | |
| contentCategoryReference: "booksAll", | |
| itemStatusList: ["Active"], | |
| excludeExpiredItemsFor: ["KOLL", "Purchase", "Pottermore", "FreeTrial", "DeviceRegistration", "KindleUnlimited", "Sample", "Prime", "ComicsUnlimited", "Comixology"], | |
| originTypes: [ | |
| "Purchase", | |
| "PublicLibraryLending", | |
| "PersonalLending", | |
| "Sample", | |
| "ComicsUnlimited", | |
| "KOLL", | |
| "RFFLending", | |
| "Pottermore", | |
| "Prime", | |
| "Rental", | |
| "DeviceRegistration", | |
| "FreeTrial", | |
| "KindleUnlimited", | |
| "Comixology", | |
| ], | |
| }, | |
| ownershipDataPDoc = { | |
| contentType: "KindlePDoc", | |
| contentCategoryReference: "pdocs", | |
| itemStatusList: ["Active"], | |
| }, | |
| { | |
| success = false, | |
| GetDevicesOverview: { | |
| deviceList: [{ deviceSerialNumber = null, deviceTypeID = null, customerID = null }], | |
| }, | |
| } = await request("GetDevicesOverview", { surfaceType: "LargeDesktop" }); | |
| let bookCount = 0; | |
| if (!deviceSerialNumber || !success) { | |
| return console.warn("获取Kindle设备信息失败"); | |
| } | |
| while (true) { | |
| const data = type == "pdoc" ? ownershipDataPDoc : ownershipDataEbook; | |
| const { | |
| success = false, | |
| GetContentOwnershipData: { items = [], numberOfItems = 0 }, | |
| } = await request("GetContentOwnershipData", Object.assign({}, ownershipDataCommon, data)); | |
| if (!success) { | |
| return console.warn("获取书本信息失败"); | |
| } | |
| for (const { asin, authors, author, title } of items) { | |
| if (newCompletedList.includes(asin)) continue; | |
| const url = `https://cde-ta-g7g.amazon.com/FionaCDEServiceEngine/FSDownloadContent?type=${docType}&key=${asin}&fsn=${deviceSerialNumber}&device_type=${deviceTypeID}&customerId=${customerID}&authPool=AmazonCN`, | |
| fileName = `${HTMLdecode(authors || author)} - ${HTMLdecode(title)}.azw3`; | |
| try { | |
| console.log("开始下载书籍:", fileName, "ASIN:", asin); | |
| await dlFile(url, fileName); | |
| newCompletedList.push(asin); | |
| } catch (err) { | |
| console.warn(err); | |
| } | |
| } | |
| ownershipDataCommon.fetchCriteria.startIndex += batchSize; | |
| bookCount += items.length; | |
| if (bookCount >= numberOfItems) break; | |
| } | |
| console.log("Kindle 设备序列号:", deviceSerialNumber, "可以用于为下载的电子书移除DRM。个人文档无需去除DRM"); | |
| console.log("下方的内容,是本次下载任务中,已经完成下载的电子书的数据。将其作为 download 函数的第二个参数传入,则该次下载任务会忽略这些已下载的电子书。"); | |
| console.log(newCompletedList); | |
| console.log("任务结束……"); | |
| } |
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声
新代码有了更多的ajax,第一个的状态码是302,其余均是200
又试了一遍就代码,也同样很多ajax,第一个的状态码也是302,其余200
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声新代码有了更多的ajax,第一个的状态码是302,其余均是200 又试了一遍就代码,也同样很多ajax,第一个的状态码也是302,其余200
我又去看了一遍官方网页是如何获取设备列表的,发现它用了新的API,但很神奇的是我代码中旧的API也能工作,我现在怀疑旧API在你那不能用
你可以用下面的代码验证一下我的想法,复制到控制台执行就行
(async () => {
if (!window.csrfToken) {
return console.warn("没有找到提交请求所必须的csrf token");
}
const a = await fetch("https://www.amazon.cn/hz/mycd/ajax", {
headers: {
"content-type": "application/x-www-form-urlencoded",
},
body: `data={\"param\":{\"GetDevices\":{}}}&csrfToken=${encodeURIComponent(window.csrfToken)}`,
method: "POST",
credentials: "include",
});
const b = await fetch("https://www.amazon.cn/hz/mycd/digital-console/ajax", {
headers: {
"content-type": "application/x-www-form-urlencoded",
},
body: `activity=GetDevicesOverview&activityInput=%7B%22surfaceType%22%3A%22LargeDesktop%22%7D&csrfToken=${encodeURIComponent(window.csrfToken)}`,
method: "POST",
credentials: "include",
});
try {
await a.json();
console.log("旧API可以工作");
} catch {
console.warn("旧API不能工作");
}
try {
await b.json();
console.log("新API可以工作");
} catch {
console.warn("新API不能工作");
}
})();
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声新代码有了更多的ajax,第一个的状态码是302,其余均是200 又试了一遍就代码,也同样很多ajax,第一个的状态码也是302,其余200
我又去看了一遍官方网页是如何获取设备列表的,发现它用了新的API,但很神奇的是我代码中旧的API也能工作,我现在怀疑旧API在你那不能用 你可以用下面的代码验证一下我的想法,复制到控制台执行就行
(async () => { if (!window.csrfToken) { return console.warn("没有找到提交请求所必须的csrf token"); } const a = await fetch("https://www.amazon.cn/hz/mycd/ajax", { headers: { "content-type": "application/x-www-form-urlencoded", }, body: `data={\"param\":{\"GetDevices\":{}}}&csrfToken=${encodeURIComponent(window.csrfToken)}`, method: "POST", credentials: "include", }); const b = await fetch("https://www.amazon.cn/hz/mycd/digital-console/ajax", { headers: { "content-type": "application/x-www-form-urlencoded", }, body: `activity=GetDevicesOverview&activityInput=%7B%22surfaceType%22%3A%22LargeDesktop%22%7D&csrfToken=${encodeURIComponent(window.csrfToken)}`, method: "POST", credentials: "include", }); try { await a.json(); console.log("旧API可以工作"); } catch { console.warn("旧API不能工作"); } try { await b.json(); console.log("新API可以工作"); } catch { console.warn("新API不能工作"); } })();
提示新旧API都可以工作
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声新代码有了更多的ajax,第一个的状态码是302,其余均是200 又试了一遍就代码,也同样很多ajax,第一个的状态码也是302,其余200
我又去看了一遍官方网页是如何获取设备列表的,发现它用了新的API,但很神奇的是我代码中旧的API也能工作,我现在怀疑旧API在你那不能用 你可以用下面的代码验证一下我的想法,复制到控制台执行就行
提示新旧API都可以工作
我更新了下这个gist,再试试看呢?如果还是行,新的代码应该会给出更多有用的信息
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声新代码有了更多的ajax,第一个的状态码是302,其余均是200 又试了一遍就代码,也同样很多ajax,第一个的状态码也是302,其余200
我又去看了一遍官方网页是如何获取设备列表的,发现它用了新的API,但很神奇的是我代码中旧的API也能工作,我现在怀疑旧API在你那不能用 你可以用下面的代码验证一下我的想法,复制到控制台执行就行
提示新旧API都可以工作
我更新了下这个gist,再试试看呢?如果还是行,新的代码应该会给出更多有用的信息
可以工作了。但是中间会出现一些错误代码,不知道是什么意思
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声新代码有了更多的ajax,第一个的状态码是302,其余均是200 又试了一遍就代码,也同样很多ajax,第一个的状态码也是302,其余200
我又去看了一遍官方网页是如何获取设备列表的,发现它用了新的API,但很神奇的是我代码中旧的API也能工作,我现在怀疑旧API在你那不能用 你可以用下面的代码验证一下我的想法,复制到控制台执行就行
提示新旧API都可以工作
我更新了下这个gist,再试试看呢?如果还是行,新的代码应该会给出更多有用的信息
可以工作了。但是中间会出现一些错误代码,不知道是什么意思
可以正常工作了就好。
你的截图中的这些错误都不是由这个gist的代码引发的。可以看到每条报错的最右侧,那里说明了这个报错是哪里来的。如果是在控制台中执行的代码,是以“VM”开头的。
页面是一样的,今天又出现了新的错误,因为不是专业计算机,可能不是很明白,也是通过网络搜到这个解决方法。下载自购书籍没有问题,但是个人文档一直不行
很奇怪,看起来由于某种原因,第二个ajax请求被重定向到了https://www.amazon.cn/500 ,你可以看看你截图里第二个ajax的状态码是不是3开头的
试试看这个gist:https://gist.github.com/smartchaos/fcf95f4ebe21059d5eb5241bef87f5c1 ,如果可以用的话还请告知我一声新代码有了更多的ajax,第一个的状态码是302,其余均是200 又试了一遍就代码,也同样很多ajax,第一个的状态码也是302,其余200
我又去看了一遍官方网页是如何获取设备列表的,发现它用了新的API,但很神奇的是我代码中旧的API也能工作,我现在怀疑旧API在你那不能用 你可以用下面的代码验证一下我的想法,复制到控制台执行就行
提示新旧API都可以工作
我更新了下这个gist,再试试看呢?如果还是行,新的代码应该会给出更多有用的信息
可以工作了。但是中间会出现一些错误代码,不知道是什么意思
可以正常工作了就好。 你的截图中的这些错误都不是由这个gist的代码引发的。可以看到每条报错的最右侧,那里说明了这个报错是哪里来的。如果是在控制台中执行的代码,是以“VM”开头的。
好的。多谢大佬
还要咨询一下大佬,如果超过1000本书怎么继续,代码我需要修改哪些地方?Kindle_download_helper这个项目我有尝试,但是获取列表很不稳定,有时候只能抓取到几百本甚至几十本书,这个项目非常稳定,很好用。
还要咨询一下大佬,如果超过1000本书怎么继续,代码我需要修改哪些地方?Kindle_download_helper这个项目我有尝试,但是获取列表很不稳定,有时候只能抓取到几百本甚至几十本书,这个项目非常稳定,很好用。
试试我新修改的这份代码
原来的代码是有处理获取1000本之后的书的情况的,但似乎完全没用😂因为我本身没有超过1000本书,所以没条件去测试,新的处理逻辑也不能保证一定生效
还要咨询一下大佬,如果超过1000本书怎么继续,代码我需要修改哪些地方?Kindle_download_helper这个项目我有尝试,但是获取列表很不稳定,有时候只能抓取到几百本甚至几十本书,这个项目非常稳定,很好用。
试试我新修改的这份代码 原来的代码是有处理获取1000本之后的书的情况的,但似乎完全没用😂因为我本身没有超过1000本书,所以没条件去测试,新的处理逻辑也不能保证一定生效
感谢,非常完美,第一次就差四本下载失败,继续后也都成功了,一共1763项均成功,再次感谢
感谢,下载成功
请问,为什么打开网页 https://www.amazon.cn/hz/mycd/myx/ 我运行这个脚本之后,网页直接跳转到 amazon.cn/error
请问,为什么打开网页 https://www.amazon.cn/hz/mycd/myx/ 我运行这个脚本之后,网页直接跳转到 amazon.cn/error
试试浏览器的无痕模式呢?
同样网页直接跳转到 amazon.cn/error




我执行代码的页面是 https://www.amazon.cn/hz/mycd/digital-console/contentlist/booksAll/dateDsc/ ,又试了下代码是正常运行的,以及我检查了网页加载的js文件,但其中没找到contextmenuhlpr.js这个文件,所以你是在哪个页面执行代码的呢?