├── .gitignore ├── README.md ├── cmpedu-downloader.py ├── cmpedu-downloader.user.js └── intro └── example.png /.gitignore: -------------------------------------------------------------------------------- 1 | .history -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cmpedu Downloader 2 | 3 | 机械工业出版社教育服务网资源下载,无需登录,无需教师权限,油猴脚本。 4 | 5 | ## 如何使用 6 | 7 | [点击安装油猴项目](https://greasyfork.org/scripts/483095) 8 | 9 | 进入书籍或资源详情页自动显示下载面板 10 | 11 | 支持以下网址格式: 12 | 13 | - `http://www.cmpedu.com/books/book/12345.htm` 14 | - `http://www.cmpedu.com/ziyuans/ziyuan/12345.htm` 15 | - `http://m.cmpedu.com/books/book/12345.htm` 16 | - `http://m.cmpedu.com/ziyuans/ziyuan/12345.htm` 17 | 18 | 19 | ![demo](intro/example.png) 20 | 21 | ## 免责声明 22 | 23 | **用途限制** 24 | 本项目仅供学习、研究及技术探讨使用,请勿将其用于任何违反法律法规或第三方服务条款的活动。 25 | 26 | **责任声明** 27 | 使用者须自行评估本程序的适用性和合法性。作者不对任何因使用本项目而导致的直接或间接后果负责,包括但不限于数据泄露、服务封禁或其他法律纠纷。 28 | 29 | **删除要求** 30 | 如果您选择下载或使用此代码,请在下载后 **24 小时内删除**。这是为了确保用户在合理时间内用于学习或研究目的,避免可能的滥用风险。 31 | 32 | 本项目与相关网站及其相关机构无任何关联。 33 | 34 | 本项目为开源项目,仅供技术交流使用。禁止将本程序用于商业目的或其他违反法律法规的行为。 35 | 36 | 用户在使用本程序时须严格遵守当地法律法规,任何违反法律的行为均由使用者自行承担。 37 | 38 | ## 侵权联系 39 | 40 | 如果您认为本项目侵犯了您的合法权益(如版权或知识产权),请通过以下方式联系我们,我们将在收到相关通知后尽快处理: 41 | 42 | 1. **电子邮件**: yanyao(#)email.com(请将 `#` 替换为 `@`) 43 | 2. **GitHub Issues**: 提交详细描述的 Issue,说明相关侵权行为。 44 | 45 | 我们将认真对待每一份投诉,并在确认后采取必要措施。 -------------------------------------------------------------------------------- /cmpedu-downloader.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | import re 4 | 5 | def get_all_resources(resource_divs): 6 | all_resources = [] 7 | for resource in resource_divs: 8 | resource_title = resource.find("div", class_="gjzy_listRTit").text.strip() 9 | resource_id = resource.find("a")["href"].split("/")[-1].split(".")[0] 10 | download_url = f"http://www.cmpedu.com/ziyuans/d_ziyuan.df?id={resource_id}" 11 | all_resources.append((resource_id, resource_title, download_url)) 12 | return all_resources 13 | 14 | def main(): 15 | while True: 16 | book_id = input("请输入BookID (输入 'exit' 退出程序): ") 17 | 18 | if book_id.lower() == 'exit': 19 | print("退出程序。") 20 | break 21 | 22 | url = f"http://www.cmpedu.com/ziyuans/index.htm?BOOK_ID={book_id}" 23 | 24 | try: 25 | response = requests.get(url) 26 | response.raise_for_status() 27 | 28 | soup = BeautifulSoup(response.text, 'html.parser') 29 | 30 | resource_divs = soup.find_all("div", class_="row gjzy_list") 31 | 32 | if resource_divs: 33 | print(f"\n资源目录在线查看:{url} \n") 34 | 35 | all_resources = get_all_resources(resource_divs) 36 | 37 | headers = { 38 | "Accept-Encoding": "gzip, deflate", 39 | "Connection": "keep-alive", 40 | "Accept": "text/html, */*; q=0.01", 41 | "User-Agent": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 42 | "Accept-Language": "en-US,en;q=0.9", 43 | "X-Requested-With": "XMLHttpRequest" 44 | } 45 | 46 | for resource_id, resource_title, download_url in all_resources: 47 | response = requests.get(download_url, headers=headers) 48 | if response.status_code == 200: 49 | download_links = re.findall(r'window\.location\.href=\'(https?://[^\'"]+)\'', response.text) 50 | if download_links: 51 | download_link = download_links[0] 52 | print(f"{resource_title} 下载链接: {download_link}") 53 | # try: 54 | # import webbrowser 55 | # webbrowser.open(download_link) 56 | # except Exception as e: 57 | # print(f"{resource_title} 打开下载链接失败: {e}") 58 | else: 59 | print(f"{resource_title} 下载链接未找到!") 60 | else: 61 | print(f"{resource_title} 下载请求失败! 状态码: {response.status_code}") 62 | 63 | else: 64 | print("没有找到资源信息。") 65 | 66 | except requests.exceptions.RequestException as e: 67 | print(f"发生请求错误: {e}") 68 | except Exception as e: 69 | print(f"发生错误: {e}") 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /cmpedu-downloader.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name Cmpedu Resource Downloader 3 | // @namespace http://tampermonkey.net/ 4 | // @version 2.0 5 | // @description 机械工业出版社教育服务网资源下载,无需登录,无需教师权限,油猴脚本。 6 | // @author yanyaoli 7 | // @match *://*.cmpedu.com/ziyuans/ziyuan/* 8 | // @match *://*.cmpedu.com/books/book/* 9 | // @connect *.cmpedu.com 10 | // @grant GM_xmlhttpRequest 11 | // @grant GM_addStyle 12 | // ==/UserScript== 13 | 14 | (function() { 15 | 'use strict'; 16 | 17 | // 样式注入 18 | GM_addStyle(` 19 | .cmp-panel { position: fixed; top: 20px; right: 20px; width: 300px; max-height: 70vh; background: #ced6e0; border-radius: 12px; box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15); z-index: 99999; font-family: 'Segoe UI', system-ui, sans-serif; overflow: hidden; transition: transform 0.2s ease; } 20 | .panel-header { padding: 16px; background: #a4b0be; border-bottom: 1px solid #747d8c; display: flex; justify-content: space-between; align-items: center; } 21 | .panel-title { margin: 0; font-size: 16px; color: #1a1a1a; font-weight: 600; } 22 | .close-btn { background: none; border: none; cursor: pointer; color: #6b7280; font-size: 24px; line-height: 1; padding: 4px; transition: color 0.2s; } 23 | .close-btn:hover { color: #1a1a1a; } 24 | .panel-content { padding: 16px; max-height: calc(70vh - 73px); overflow-y: auto; } 25 | .resource-item { padding: 12px 0; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #f0f0f0; cursor: pointer; } 26 | .resource-item:hover { color: #1e90ff; } 27 | .resource-item:last-child { border-bottom: none; } 28 | .skeleton { 29 | background: #f2f2f2; 30 | border-radius: 4px; 31 | height: 20px; 32 | width: 100%; 33 | margin-bottom: 12px; 34 | animation: pulse 1.5s infinite; 35 | } 36 | .skeleton:last-child { margin-bottom: 0; } 37 | .error-message { color: #dc3545; display: flex; align-items: center; gap: 8px; padding: 12px; background: #fff5f5; border-radius: 8px; margin: 8px 0; } 38 | @keyframes skeleton-loading { 39 | 0% { background-position: 200% 0; } 40 | 100% { background-position: -200% 0; } 41 | } 42 | @media (max-width: 480px) { .cmp-panel { width: 90%; right: 5%; left: auto; top: 10px; } } 43 | `); 44 | 45 | // 基础配置 46 | const isMobile = window.location.host.startsWith('m.'); 47 | const baseUrl = isMobile ? 'http://m.cmpedu.com' : 'http://www.cmpedu.com'; 48 | const panelId = "downloadPanel"; 49 | 50 | function extractBookId() { 51 | if (window.location.href.includes('books/book')) { 52 | return window.location.pathname.split("/").pop().split(".")[0]; 53 | } 54 | if (window.location.href.includes('ziyuans/ziyuan')) { 55 | const el = document.getElementById('BOOK_ID'); 56 | return el ? el.value : null; 57 | } 58 | return null; 59 | } 60 | 61 | function createPanel() { 62 | const panel = document.createElement('div'); 63 | panel.id = panelId; 64 | panel.className = 'cmp-panel'; 65 | panel.innerHTML = ` 66 |
67 |

资源下载

68 | 69 |
70 |
71 |
72 |
73 |
74 |
75 | `; 76 | document.body.appendChild(panel); 77 | panel.querySelector('.close-btn').addEventListener('click', () => panel.remove()); 78 | return panel; 79 | } 80 | 81 | function updatePanelContent(panel, content) { 82 | const panelContent = panel.querySelector('.panel-content'); 83 | panelContent.innerHTML = content; 84 | } 85 | 86 | function createResourceItem(title) { 87 | return ` 88 |
89 | ${title} 90 |
91 | `; 92 | } 93 | 94 | function updateResourceItem(panel, index, content, downloadLink) { 95 | const items = panel.querySelectorAll('.resource-item'); 96 | if(items[index]) { 97 | const item = items[index]; 98 | item.innerHTML = `${content}`; 99 | item.style.cursor = 'pointer'; 100 | item.setAttribute('data-download-link', downloadLink); 101 | item.onclick = () => window.open(downloadLink, '_blank'); 102 | } else { 103 | // If we don't have an existing item, append a new one 104 | const panelContent = panel.querySelector('.panel-content'); 105 | const newItem = document.createElement('div'); 106 | newItem.className = 'resource-item'; 107 | newItem.innerHTML = `${content}`; 108 | newItem.style.cursor = 'pointer'; 109 | newItem.setAttribute('data-download-link', downloadLink); 110 | newItem.onclick = () => window.open(downloadLink, '_blank'); 111 | panelContent.appendChild(newItem); 112 | } 113 | } 114 | 115 | function processResourceResponse(response, title) { 116 | const downloadLinks = response.responseText.match(/window\.location\.href=\'(https?:\/\/[^\'"]+)\'/); 117 | if (downloadLinks) { 118 | return [title, downloadLinks[1]]; 119 | } 120 | return [null, null]; 121 | } 122 | 123 | // 主逻辑 124 | const bookId = extractBookId(); 125 | if (!bookId) { 126 | console.error("无法提取 BOOK_ID"); 127 | return; 128 | } 129 | const resourceUrl = `${baseUrl}/ziyuans/index.htm?BOOK_ID=${bookId}`; 130 | const panel = createPanel(); 131 | 132 | GM_xmlhttpRequest({ 133 | method: "GET", 134 | url: resourceUrl, 135 | onload: function(response) { 136 | const parser = new DOMParser(); 137 | const doc = parser.parseFromString(response.responseText, "text/html"); 138 | const resourceDivs = doc.querySelectorAll("div.row.gjzy_list"); 139 | const resources = Array.from(resourceDivs).map(div => { 140 | return { 141 | title: div.querySelector("div.gjzy_listRTit")?.textContent.trim() || "未知资源", 142 | resourceId: div.querySelector("a")?.href.split("/").pop().split(".")[0] 143 | }; 144 | }); 145 | 146 | if (resources.length === 0) { 147 | updatePanelContent(panel, "未找到资源。"); 148 | return; 149 | } 150 | 151 | // Clear skeleton placeholders before adding real content 152 | updatePanelContent(panel, ''); 153 | 154 | resources.forEach(({ title, resourceId }, index) => { 155 | const downloadUrl = `${baseUrl}/ziyuans/d_ziyuan.df?id=${resourceId}`; 156 | GM_xmlhttpRequest({ 157 | method: "GET", 158 | url: downloadUrl, 159 | headers: { 160 | "Accept-Encoding": "gzip, deflate", 161 | "Connection": "keep-alive", 162 | "Accept": "text/html, */*; q=0.01", 163 | "User-Agent": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5", 164 | "Accept-Language": "en-US,en;q=0.9", 165 | "X-Requested-With": "XMLHttpRequest" 166 | }, 167 | onload: function(response) { 168 | const [resourceTitle, downloadLink] = processResourceResponse(response, title); 169 | if (resourceTitle) { 170 | updateResourceItem(panel, index, resourceTitle, downloadLink); 171 | } else { 172 | updateResourceItem(panel, index, `${title} - 链接解析失败`, null); 173 | } 174 | }, 175 | onerror: function() { 176 | updateResourceItem(panel, index, `${title} - 请求失败`, null); 177 | } 178 | }); 179 | }); 180 | }, 181 | onerror: function() { 182 | updatePanelContent(panel, "获取资源页面失败。"); 183 | } 184 | }); 185 | })(); 186 | -------------------------------------------------------------------------------- /intro/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanyaoli/cmpedu-dl/2411210a9e9c7e5f0f5d12da0c98ceabcba75de7/intro/example.png --------------------------------------------------------------------------------