简介面向需要大量文献调研与综述写作的科研人员和学生IEEE-downloader 是一套基于 Python 的 IEEE 论文批量下载脚本工具旨在缓解逐篇检索、手动保存 PDF 的效率问题。压缩包共 11 个文件以 6 个 Python 脚本为主体配合图标、DOI 清单文本、说明文档和预览图等辅助材料整体仅 159KB目录轻量便于阅读与二次修改。已有 566 人学习。其内容围绕批量任务组织展开包含主程序、配置与工具模块、按页面或链接下载的处理逻辑、界面资源及输入清单模板可帮助读者理解请求发送、页面解析、下载调度以及登录验证、调用限制和延时策略等常见问题的处理思路。适合具备一定 Python 基础、希望学习文献抓取自动化与批量管理流程的读者参考使用时需注意版权与合法访问权限。1. 文献批量下载卡住的从来不是“能不能下”而是“下完还剩几篇是好的”文献批量下载这件事真正让人后悔的地方通常不是拿不到文件而是跑完之后发现三百个 PDF 里有四十个是空白页、二十个是同一篇。IEEE-downloader 这类脚本要解决的正是后半段把一份 DOI 或文章链接清单变成一批经过校验、命名规范、能直接被 Zotero 和本地检索工具吞下的文件。它的价值不在“绕过什么”而在把重复点击、改名、查漏这些机械动作压缩成一条可重跑的命令。它适合的人群很明确正在写系统性综述、手上攥着两三百条检索结果、需要把文献沉淀成本地库的研究生和工程团队也适合那种每隔半年要复盘一次某个方向、希望每次都用同一套流程归档的人。如果你只是临时下三五篇手动点反而更快装环境的时间够你下完了。需要先把边界说清楚脚本只复用你自己的访问权限能下到什么取决于你所在机构订阅覆盖到哪些刊它本身不处理任何鉴权绕过。所以下面的内容全部围绕“在你有权限的范围内把批量这件事做对”展开。2. 拆开脚本看结构一次请求要穿过哪几层2.1 三层职责请求层、队列层、落盘层大部分同类脚本翻车都是因为把这三件事写在一个 for 循环里。跑十篇没问题跑到第一百篇出错时你会发现根本没法定位是没权限、被限速、还是文件写到一半被覆盖了我一般会强制拆成三层每层只对自己那一段负责。请求层只干一件事给一个 URL返回字节流或者一个明确的错误码。它管 session、请求头、超时、退避重试不管文件叫什么名字。队列层管“下一条是谁”从清单里取任务、标记状态、失败重排、控制总量。落盘层管“字节怎么变成文件”先写.part临时文件、校验通过再原子改名、按规则分桶。三层之间通过一个状态表通信而不是通过内存里的变量。这么拆的好处是排错路径清晰。全部 403看请求层某几条反复失败看队列层的重试计数文件存在但打不开看落盘层的校验环节。血泪经验是合并在一起的脚本你只能靠 print 大法猜。2.2 环境准备与目录约定常见做法是 Python 3.9 以上核心依赖就三个requests发请求、pypdf做合法性校验、pdfplumber后面抽全文。少数情况下 PDF 直链是页面里 JS 动态拼出来的那就再加selenium但能不用就不用——浏览器实例跑几百次的内存泄漏比限速还难缠。目录我一般固定成这样跑之前先建好mkdir -p project/{data,pdfs,logs,scripts} # data/ 放原始清单和 manifest.db # pdfs/ 按年份分桶落盘 # logs/ 每次运行一份带时间戳的日志 # scripts/ 放脚本本体几个参数建议在动手前就定下来中途改会很麻烦参数建议值说明请求间隔25 秒 随机抖动低于 2 秒很容易触发限流单请求超时60 秒PDF 体积大30 秒经常不够单条最大重试3 次超过 3 次基本是权限或链接问题重试无意义并发数1这类站点并发上去收益极低风险极高单次运行上限50100 篇分批跑便于中途检查提示并发数写 1 不是保守是实测结论。同一账号并发 5 个请求前 30 篇可能没问题第 40 篇开始整批 403最后总耗时反而更长。2.3 一段能跑通的最小骨架先别管清单和状态机用单条 URL 把请求层调通这是整个流程的地基。import time, random, pathlib import requests HEADERS { # UA 用你本机浏览器的真实值脚本和浏览器保持一致能少很多麻烦 User-Agent: your-browser-ua, Accept: application/pdf,text/html;q0.9,*/*;q0.8, Referer: https://ieeexplore.ieee.org/, # 不少站点会校验来源页 } def backoff(attempt, base5, cap120): 指数退避 抖动避免重试请求整点齐发 delay min(cap, base * (2 ** attempt)) return delay random.uniform(0, delay * 0.3) def fetch_pdf(session, url, out_path, retries3): for attempt in range(retries): try: resp session.get(url, headersHEADERS, timeout60, streamTrue) if resp.status_code 200: tmp out_path.with_suffix(.part) # 先写临时文件 with open(tmp, wb) as f: for chunk in resp.iter_content(64 * 1024): if chunk: f.write(chunk) tmp.replace(out_path) # 校验前的原子改名 return ok, len(open(out_path, rb).read()) if resp.status_code in (403, 429): time.sleep(backoff(attempt)) continue return fhttp_{resp.status_code}, 0 except requests.RequestException as e: time.sleep(backoff(attempt)) last str(e) return failed, 0这段代码里三个地方值得单独说。streamTrue配合分块写入是为了避免几百 MB 一次性进内存写.part再replace是为了防止下载到 70% 断网留下一个能打开但内容截断的坏文件——这种文件最难发现因为它的扩展名和大小看着都正常。Referer头不是玄学很多下载链接是一次性签发的来源页对不上就直接给你一个 HTML。还有一个隐含细节session是外面传进来的不是函数里新建的。这意味着 cookie 和连接复用都在调用方控制后面接清单循环时不用反复握手。3. 清单先洗干净DOI 规范化、去重与 manifest 生成3.1 从检索导出到结构化清单起点通常是一次检索结果的导出。常见做法是从数据库导出 CSV 或 RIS一次最多几千条超过就分页导。导出来第一件事不是马上跑下载而是抽出 DOI 这一列做规范化——DOI 字符串长得像10.1109/XXXX.2021.1234567但导出的格式五花八门带https://doi.org/前缀的、带doi:的、末尾跟了英文句号的、大小写混着的。import re, csv def norm_doi(raw: str) - str: s (raw or ).strip().lower() # 去掉各种前缀变体 s re.sub(r^(https?://(dx\.)?doi\.org/|doi:\s*), , s) # 去掉导出时粘上的尾随标点 s s.strip().strip(.,;)]) return s with open(data/raw_export.csv, encodingutf-8-sig) as f: rows list(csv.DictReader(f)) clean, bad [], [] for r in rows: doi norm_doi(r.get(DOI) or r.get(doi) or ) if re.match(r^10\.\d{4,9}/\S$, doi): # DOI 的基本形状校验 clean.append({doi: doi, title: (r.get(Document Title) or ).strip()}) else: bad.append(r) print(f可用 {len(clean)} 条丢弃 {len(bad)} 条)那句正则^10\.\d{4,9}/\S$是 DOI 的标准形状10.后面跟 4 到 9 位注册商代码再跟斜杠和任意非空白字符。用它做第一道闸门能挡掉导出文件里混进来的页码、年份、空行。丢弃的那些不要直接删单独存一个data/bad_rows.csv很多时候是导出时列错位回头还能救。3.2 规范化与去重四个必须处理的边界去重比想象中麻烦。只按 DOI 精确匹配能挡掉八成的重复剩下两成会让你在写综述时引用同一篇文章两次。下面四个边界我每次都过一遍。第一同一篇文章的 early access 版本和正式卷期版本DOI 可能不同标题完全一样。这类必须按标题归并保留有卷期号的那条。第二会议论文和期刊扩展版标题高度相似但确实是两篇不能盲目合并判定阈值要卡紧。第三标题里的全角半角、连字符、上下标会导致字符串不等得先统一。第四导入多个来源数据库导出 手动补的 别人给的 BibTeX时同一批文献会重复好几遍。import re, difflib def norm_title(t: str) - str: t t.lower() t re.sub(r[\u2010-\u2015\-–—], -, t) # 各种连字符统一 t re.sub(r[^\w\u4e00-\u9fff], , t) # 去掉标点保留中英文 return re.sub(r\s, , t).strip() def dedupe(items, title_threshold0.93): seen_doi, kept, buckets set(), [], {} for it in items: if it[doi] in seen_doi: continue t norm_title(it[title]) hit None for k, v in buckets.items(): if difflib.SequenceMatcher(None, t, k).ratio() title_threshold: hit k break if hit: continue # 标题近似判定为重复 buckets[t] True seen_doi.add(it[doi]) kept.append(it) return kept0.93这个阈值是试出来的调到 0.90 会把“某算法的改进”和“某算法的再改进”误杀调到 0.96 又漏掉一堆因为副标题缺失导致的近似标题。如果你这批文献主题特别集中建议调到 0.95 以上宁可漏合也别错合——错合是静默丢数据比重复更难发现。3.3 用 manifest 把“下载”变成可追踪任务清单洗完之后不要直接喂给下载循环。中间加一层 manifest用 SQLite 存好处是进程被杀、断电、手滑关终端之后重跑能接着上次的状态走。字段类型用途doiTEXT PRIMARY KEY唯一键天然去重titleTEXT落盘命名和后续检索用urlTEXT实际下载地址可能为 NULL 待补statusTEXTpending / running / ok / failed / skippedattemptsINTEGER重试计数超过阈值不再排bytesINTEGER落盘字节数用于校验updated_atTEXT排错时看时间线import sqlite3, pathlib conn sqlite3.connect(data/manifest.db) conn.execute( CREATE TABLE IF NOT EXISTS tasks( doi TEXT PRIMARY KEY, title TEXT, url TEXT, status TEXT DEFAULT pending, attempts INTEGER DEFAULT 0, bytes INTEGER DEFAULT 0, updated_at TEXT DEFAULT (datetime(now)) )) conn.executemany( INSERT OR IGNORE INTO tasks(doi, title) VALUES(?, ?), [(it[doi], it[title]) for it in kept] ) conn.commit() print(待处理:, conn.execute( SELECT count(*) FROM tasks WHERE statuspending).fetchone()[0])INSERT OR IGNORE是这里的关键同一批清单重复导入不会报错也不会重置已有状态。这意味着你可以放心地“补一批新文献进去再跑一次”已经下好的不会重下。4. 执行层的三个参数会话、限速、断点4.1 会话怎么来只复用你自己的授权态脚本本身不产生权限它只是把你浏览器里已经有的访问状态搬到 Python 里。常见做法是两步先在浏览器里正常登录确认能手动下下来一篇再把浏览器的 cookie 导出成 Netscape 格式或 JSON加载进requests.Session。import json, requests def build_session(cookie_filedata/cookies.json): s requests.Session() with open(cookie_file) as f: for c in json.load(f): s.cookies.set(c[name], c[value], domainc.get(domain, )) return s session build_session() probe session.get(https://ieeexplore.ieee.org/document/doc_id, headersHEADERS, timeout30) print(probe.status_code, len(probe.text))这段是冒烟测试不是下载。返回 200 且页面里能看到正文入口说明授权态是活的返回登录页或 403先回去把 cookie 重新导一遍别急着跑全量。cookie 有寿命一般几小时到一天跑到一半集体 403 大多是这个原因。4.2 限速与退避把并发降到 1 反而是最快的很多人第一反应是开多线程把三百篇并行下去。实际跑下来并发 1 加合理退避的总耗时经常更短因为并发触发的限流会让整批请求全部失败重来重试本身就是最大的时间开销。具体参数正常请求之间固定间隔 25 秒加 01.5 秒随机抖动收到 429 时退避基数 10 秒起指数增长封顶 120 秒连续三次 429 就主动停 5 分钟再继续。这套参数不优雅但能让你在跑完两百篇之前不被踢出来。def polite_sleep(i, base3.0): 按任务序号动态调整间隔越跑越慢一点给对端留余地 gap base (i // 50) * 0.5 time.sleep(gap random.uniform(0, 1.5))i // 50那一段是刻意设计的每跑 50 篇把基础间隔加 0.5 秒。前期快一点后期慢一点实测比固定间隔的整体成功率高一截。4.3 断点续传与状态机状态流转只有五条边pending → running → okrunning → failedfailed → running重试。关键是“取任务”这个动作必须是原子的否则你手动开两个终端就会重复下载。def claim_one(conn): row conn.execute( UPDATE tasks SET statusrunning, attemptsattempts1, updated_atdatetime(now) WHERE doi ( SELECT doi FROM tasks WHERE status IN (pending,failed) AND attempts 3 ORDER BY rowid LIMIT 1 ) RETURNING doi, title, url ).fetchone() conn.commit() return row def finish(conn, doi, ok, nbytes0): conn.execute( UPDATE tasks SET status?, bytes?, updated_atdatetime(now) WHERE doi?, (ok if ok else failed, nbytes, doi)) conn.commit()注意RETURNING需要 SQLite 3.35 以上。老环境可以先SELECT再UPDATE但要包在同一个事务里否则并发下会抢到同一条。进程被杀之后的重跑逻辑也就一行把遗留的running全部打回failed再照常跑。这条“后悔药”一定要写在脚本启动处否则每次中断都得手动改数据库。conn.execute(UPDATE tasks SET statusfailed WHERE statusrunning) conn.commit()4.4 命名与分桶让文件系统替你记住上下文文件名是后续检索的第一道索引别用1.pdf、2.pdf这种。我一般用第一作者姓_年份_标题前40字_DOI后6位.pdf按年份分桶import re, pathlib def safe_name(s, limit40): s re.sub(r[\\/:*?|\r\n\t], , s) # 各平台非法字符 s re.sub(r\s, , s).strip() return s[:limit].rstrip( .) # Windows 不允许结尾空格和点 def target_path(row, rootpdfs): doi, title row[0], row[1] or untitled year re.search(r(19|20)\d{2}, doi) year year.group(0) if year else unknown tail doi.split(/)[-1][-6:] name f{safe_name(title)}_{tail}.pdf d pathlib.Path(root) / year d.mkdir(parentsTrue, exist_okTrue) return d / namesafe_name里那两个替换不是洁癖。文件名里带冒号或问号在 Linux 上能存拷到移动硬盘或者发给同组的人就废了结尾的空格和点在 Windows 上会被静默截断导致 manifest 里记的路径和实际文件对不上——这个坑我踩过一次两百个文件的索引全废。5. 避坑与排查下下来打不开、跑到一半失效、状态码三连5.1 现象文件大小正常打开是空白页或只有一页预览原因基本可以锁定在“返回的不是 PDF”。很多下载入口在权限不足时会返回一个 200 的 HTML 页面里面是登录提示或者试读预览requests看来是成功落盘之后就成了一个扩展名骗人的文件。解决办法是落盘时做双重校验头部魔数加页数from pypdf import PdfReader def validate(path): with open(path, rb) as f: if not f.read(5).startswith(b%PDF-): return False, bad magic try: n len(PdfReader(str(path)).pages) except Exception as e: return False, fparse error: {e} return (n 0), f{n} pages校验不通过的文件不能直接删改名成.html存到pdfs/_rejected/里回头看一眼内容就知道是权限问题还是会话过期比凭空猜快得多。5.2 现象前 40 篇一切正常第 41 篇开始全部 403原因几乎都是会话失效或触发频控两者表现一样但处理方式不同。区分办法很简单手动在浏览器里打开下一篇能下就是频控不能下就是会话过期。处理上分两步走。频控就停 510 分钟再把failed打回pending重跑会话过期就重新导 cookie然后从 manifest 里status ! ok的位置接着来。这也是为什么 manifest 必须实时落库——内存里维护状态的话一次 403 就得从头再来。5.3 现象清单 300 条最后只落了 280 个文件找不到少的在哪原因通常是两个。一是文件名冲突后replace直接覆盖了同名是因为标题截断到 40 字后撞车二是早期版本没做INSERT OR IGNORE重复导入把状态重置了。解决靠对账不靠肉眼。跑完之后拿 manifest 里的ok记录和磁盘上的文件做一次双向比对缺的、多的、大小对不上的全部列出来ok_set {r[0] for r in conn.execute(SELECT doi FROM tasks WHERE statusok)} disk {p.stem.split(_)[-1]: p for p in pathlib.Path(pdfs).rglob(*.pdf)} missing [d for d in ok_set if d.split(/)[-1][-6:] not in disk] print(清单有、盘上没有:, len(missing))这个对账脚本建议固定下来每次跑完都执行一遍。它比任何日志都直观。5.4 现象断点续传后之前失败的条目再也不重试了原因是状态机写错把“尝试过”当成“处理过”。attempts到了 3 之后如果还在failed状态WHERE attempts 3就永远筛不到它看起来是“跑完了”实际是“放弃了”。解决是在收尾阶段单独打印一次失败清单并允许手动重置failed conn.execute( SELECT doi, attempts FROM tasks WHERE statusfailed).fetchall() for d, a in failed: print(f放弃: {d} attempts{a}) # 需要重来时UPDATE tasks SET statuspending, attempts0 WHERE doi?这条血泪经验的教训是任何“超过阈值就跳过”的逻辑都必须有一个显式的、能看见的出口。静默跳过是最坏的失败模式。5.5 现象抽取全文时报编码错或者中文标题的文献完全搜不到原因是文件名和文本编码混在一起处理了。文件名走的是操作系统编码PDF 里的文本走的是字体编码两条链路必须分开。标题里的中文、希腊字母、上下标在落盘时会被替换成空格导致后续按标题搜索匹配不上。解决是 manifest 里永远保留原始标题字段检索走数据库而不是走文件名。文件名只负责“人能认出来”索引的事交给 manifest 和后面的全文抽取。6. 把 PDF 变成可写综述的素材抽取、打分与人工复核的最后一公里文件下齐只是半成品。真正省时间的是把 PDF 里的标题、摘要、关键词抽成一张表再用关键词打分排个优先级你就知道先读哪三十篇。抽取字段来源用途标题首页文本块 / manifest校验 PDF 与清单是否错配摘要首页 “Abstract” 到 “Index Terms” 之间关键词打分主输入关键词“Index Terms” 段落聚类和主题筛选页数pypdf筛掉 12 页的会议摘要抽取字数pdfplumber低于 500 字的判定为扫描件转 OCRimport pdfplumber, re, json def extract(path): with pdfplumber.open(path) as pdf: text \n.join((p.extract_text() or ) for p in pdf.pages[:3]) m re.search(rAbstract[—\-:]\s*(.?)(?:Index Terms|Keywords), text, re.S | re.I) abstract re.sub(r\s, , m.group(1)).strip() if m else return {file: str(path), chars: len(text), abstract: abstract[:1200]} def score(abstract, keywords): 按关键词命中数打分权重可以按你的主题手动调 a abstract.lower() return sum(w for kw, w in keywords.items() if kw.lower() in a) KW {review: 1, survey: 2, benchmark: 2, dataset: 2, limitation: 1} rows [extract(p) for p in pathlib.Path(pdfs).rglob(*.pdf)] for r in sorted(rows, keylambda x: score(x[abstract], KW), reverseTrue)[:30]: print(score(r[abstract], KW), r[file])extract只读前三页是有意为之摘要和关键词几乎都在首页读全本会让三百篇的抽取时间从几分钟涨到半小时。score的权重表要按你自己的主题改review和survey给 2 分是因为综述类文献对写综述的边际价值最高limitation给 1 分是因为这类段落往往是找研究缺口的入口。最后一步必须留给人。我一般会拿打分前 60 篇过一遍标题手工标三档必读、备查、弃用。这一步机器替不了——打分只能按词频排判断不了“这篇的方法是不是真的适用于我的场景”。从那以后我每次跑批量任务都强制先拿 5 篇做冒烟测试验证会话、验证落盘、验证校验、验证命名全过了再放全量。三百篇跑一半发现命名规则有问题重来的成本比这五分钟高太多。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
Ubuntu 18.04 下用 dpkg 安装 gcc 7 并配置多版本共存指南 简介:面向 Ubuntu 18.04 系统用户与运维开发者的 GCC 7 离线安装包,针对无外网或受限网络环境下编译工具链缺失、在线 apt 安装失败等问题,提供可直接落地的本地部署方案。压缩包共 25 个文件,以 24 个 deb 安装包和 1 个 sh 脚本… · 2026/11/6 2:11:36
C# WinForms 教务系统实战:三层架构、ADO.NET 与选课事务 简介:这份压缩包面向计算机专业课程设计、毕业设计与 C# 桌面开发入门者,提供一套基于 Windows 窗体和 SQL Server 的教务管理示例,覆盖学生信息管理、成绩管理与选课三类业务。系统区分学生与管理员两类角色,管理员可维护管理员及… · 2026/11/6 2:10:36
Web后台弱口令批量检测实战:判定、限速与复核 简介:面向网络安全从业者与渗透测试学习者的Web后台弱口令检测工具WebCrack,以开源免费形式提供,聚焦弱口令与万能密码的批量探测,适用于授权范围内的网站安全审计、漏洞排查及检测原理学习。压缩包共18个文件、约114KB࿰… · 2026/11/6 2:10:36
Cinder卷备份可靠性增强:状态机驱动的断点续传方案 简介:这是一份面向OpenStack运维工程师与云平台开发者的Python工具脚本,专为简化Cinder卷备份/还原流程而设计,有效应对Cinder Backup Service在多租户支持、备份可见性控制及自动轮换等方面的原生限制。资源包共4个文件(11KB&… · 2026/11/6 2:13:37
计算机专业生如何利用课程优势玩转CTF竞赛 1. 计算机专业生打CTF的核心优势解析作为计算机专业的学生,你可能还没意识到自己已经手握一把打开CTF大门的金钥匙。当年我在大二第一次参加校赛时,就发现专业课里那些看似枯燥的理论知识,在CTF赛场上突然变得鲜活起来。比如《计算机网络》课… · 2026/11/6 2:13:37
Windows NTP 时间同步:w32tm 配置、批量下发与偏差告警 简介:这是一款面向 Windows 系统运维与开发人员的轻量级时间同步工具,重点解决内网或公网环境下主机时钟校准、NTP 服务切换与同步状态上报问题。程序提供可视化图形界面,可指定局域网或公网 NTP 服务,设置同步间隔与最大时间偏差… · 2026/11/6 2:13:37
智能氛围灯如何实现非接触式睡眠监测 1. 项目概述:当科技遇见睡眠健康床头氛围灯早已不是简单的照明工具,现代人更希望它能成为睡眠健康的守护者。这款集成了呼吸心率监测功能的智能氛围灯,通过非接触式生物传感技术,在营造舒适光环境的同时,持续监测使用者… · 2026/11/6 2:13:37
32位程序突破4GB内存限制:大地址标志、AWE与文件映射实战 简介:面向仍在维护32位C/C#程序的开发者,聚焦默认约2GB用户态地址空间限制与Large Address Awareness(LAA)相关处理思路,帮助需要处理大数据、图像或游戏等内存密集任务的读者理解如何让32位程序按需申请更大地址空间&… · 2026/11/6 2:13:37
Seay源代码审计系统实战:从扫描原理到自定义规则开发 简介:Seay源代码审计系统是一款基于C#开发、主要运行于Windows平台的PHP代码安全审计工具,面向网络安全初学者、CTF/AWD参赛选手及渗透测试人员,用于快速发现PHP项目中的SQL注入、代码执行、命令执行、文件包含、文件上传、XSS跨站、信息泄露… · 2026/11/6 2:12:36
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07
三菱PLC数据采集到数据库的时序设计:Python+MC协议实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
ASIC主模式RTL设计:状态机与三态驱动的流片级实现 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
STM32嵌入式C++开发:GDB调试与Renode仿真实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 1:02:11
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:07