简介这份资源面向具备一定Python基础的爬虫学习者与Web数据管理初学者围绕「用Python抓取网页内容并存入MongoDB」这一典型场景展开帮助读者理解文档型数据库与传统关系型数据库的差异掌握从请求网页、解析HTML到持久化存储的完整链路。包内共1个docx文档压缩包约608KB内容以图文与代码示例为主涵盖requests发送请求、BeautifulSoup解析页面、pymongo连接数据库及insert_one写入文档等关键环节并穿插MongoDB中文档、集合、数据库等核心概念的讲解。目前已有11252人学习下载适合希望把爬取结果结构化保存、并借助MongoDB灵活模式与索引能力提升检索效率的读者参考也可作为课程设计或小型数据采集项目的实践素材。1. 用 Python 爬取网页并导出为 Word从抓取到排版的完整落地路径很多人第一次接到「把某个网页内容整理成 Word 文档」的需求时第一反应是手动复制粘贴。页面少还能忍一旦要处理几十上百个页面或者需要每天定时更新手工操作就会变成灾难。用 Python 爬取网页并导出为 Word 文档本质上是一条「抓取 → 解析 → 清洗 → 排版 → 落盘」的流水线核心工具组合通常是 requests 负责拿页面、BeautifulSoup 或 lxml 负责解析、python-docx 负责生成 .docx 文件。这套方案适合需要批量归档公告、整理行业资讯、生成周报素材的从业者也适合刚学完 Python 基础语法想找一个完整练手项目的开发者。它不追求分布式爬虫的吞吐量而是把重点放在「内容结构还原」和「文档可读性」上——这两点恰恰是复制粘贴最容易丢失的东西。2. 抓取层选型requests 与解析器的组合怎么定2.1 静态页面与动态渲染的判断方法在写任何代码之前先要判断目标页面是静态返回还是依赖 JavaScript 渲染。最直接的办法是打开浏览器开发者工具的 Network 面板禁用 JavaScript 后刷新页面如果正文内容还在说明是静态页面requests 就能拿到如果正文消失说明内容由前端脚本注入requests 拿到的 HTML 里只有空壳。这个判断决定了后续技术栈静态页面用 requests BeautifulSoup 足够动态页面则需要考虑 Selenium 或 Playwright 这类能驱动真实浏览器的工具。我一般会先用 requests 试一次看返回文本里有没有目标关键词没有再做动态方案避免过早引入重型依赖。2.2 最小可运行抓取脚本与请求头配置下面这段代码是一个最小可运行的抓取骨架包含请求头伪装、超时设置和编码处理。很多新手拿到的 HTML 中文乱码问题往往出在编码判断上这里用 apparent_encoding 做兜底。import requests from bs4 import BeautifulSoup # 请求头里带上 UA避免被服务端直接拒绝 headers { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ), Accept-Language: zh-CN,zh;q0.9, } url https://example.com/article/123 # timeout 必须设否则网络卡住时脚本会一直挂着 resp requests.get(url, headersheaders, timeout10) # 先看响应头里的编码再用 apparent_encoding 兜底 resp.encoding resp.apparent_encoding # 用 lxml 解析比 html.parser 快容错也更好 soup BeautifulSoup(resp.text, lxml) # 先定位标题不同站点标签不同常见是 h1 或带特定 class 的 div title_tag soup.find(h1) title title_tag.get_text(stripTrue) if title_tag else 未命名文档 print(title) print(resp.status_code)这段代码里几个参数值得展开说。timeout10 是连接和读取的总超时生产环境建议拆成 connect timeout 和 read timeout 两个值比如 timeout(5, 15)连接阶段快速失败读取阶段给足时间。resp.apparent_encoding 会调用 chardet 做编码探测比直接写死 utf-8 更稳但探测本身有开销如果目标站点编码固定直接指定更快。解析器选 lxml 而不是 html.parser是因为 lxml 基于 C 实现解析大页面时速度差距明显而且对残缺 HTML 的修复能力更强。2.3 正文提取的三种定位策略拿到 soup 之后怎么准确提取正文是第一个分水岭。常见做法有三种按标签路径定位、按 class/id 定位、按文本密度启发式提取。按标签路径最直接比如 soup.find(div, class_article-content)但一旦站点改版就会失效。按文本密度适合结构不固定的页面思路是遍历所有 div计算其中 p 标签文本长度占比占比最高的那个通常就是正文容器。我一般会先看目标站点的 HTML 结构如果 class 命名语义清晰就用第二种结构混乱才上第三种。提取到的内容不要急着写 Word先存成中间结构比如一个包含 title、paragraphs、images 的字典这样排版层和抓取层解耦后面换输出格式也不用重写抓取逻辑。3. 用 python-docx 生成文档样式、段落与图片的写入3.1 文档骨架的初始化与样式继承python-docx 的核心对象是 Document它默认基于一个空白模板。如果你需要公司或实验室的统一格式可以准备一个 .docx 模板文件用 Document(template.docx) 加载这样标题样式、页边距、字体都会继承模板设置。下面这段代码演示了从零创建文档并写入标题和段落的基本流程。from docx import Document from docx.shared import Pt, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH # 加载模板可以继承预设样式没有模板就用默认 doc Document() # 添加一级标题level1 对应 Heading 1 样式 doc.add_heading(抓取结果汇总, level1) # 添加正文段落 para doc.add_paragraph(这是从目标页面提取的第一段正文内容。) # 对段落里的 run 做精细控制比如字号和颜色 run para.runs[0] run.font.size Pt(12) run.font.color.rgb RGBColor(0x33, 0x33, 0x33) # 设置段落对齐方式 para.alignment WD_ALIGN_PARAGRAPH.LEFT doc.save(output.docx)这里的关键点是理解 paragraph 和 run 的关系。一个段落可以包含多个 run每个 run 是一段连续的同格式文本。add_paragraph 返回的是段落对象直接设置 paragraph.font 会影响整个段落但更推荐对 run 操作因为实际排版中经常需要一段话里部分加粗、部分变色。add_heading 的 level 参数对应 Word 里的标题级别导出后可以直接生成导航目录这对长文档很实用。3.2 图片下载与嵌入的完整链路网页正文里的图片不能直接引用 URLWord 文档需要把图片数据嵌入进去。流程是先用 requests 下载图片二进制流再用 BytesIO 包装成文件对象最后通过 add_picture 插入。下面这段代码处理了图片下载失败和尺寸限制两个常见问题。import requests from io import BytesIO from docx.shared import Inches def add_image_from_url(doc, img_url, max_widthInches(5.5)): try: # 图片请求也要带 UA部分站点会校验来源 resp requests.get(img_url, timeout15) resp.raise_for_status() img_stream BytesIO(resp.content) # 先按最大宽度插入高度自动等比缩放 doc.add_picture(img_stream, widthmax_width) except Exception as e: # 图片失败不能中断整个文档生成记录占位即可 doc.add_paragraph(f[图片加载失败: {img_url}]) print(f图片下载异常: {e})max_width 设成 5.5 英寸是因为 A4 纸默认页边距下正文区域宽度大约就是这个值超过会导致图片被裁切或溢出页面。add_picture 只指定 width 时python-docx 会自动按原始宽高比计算高度不用手动算。异常处理里把失败图片替换成文字占位是为了保证文档结构完整不至于因为一张图挂掉整个任务。3.3 表格数据的结构化写入如果抓取的内容包含表格比如统计数据或参数列表用 add_table 比手动拼段落更合适。下面是一个把二维列表写入 Word 表格的例子。def add_table_to_doc(doc, data): if not data: return rows, cols len(data), len(data[0]) table doc.add_table(rowsrows, colscols) # 使用内置的网格样式导出后带边框 table.style Table Grid for i, row in enumerate(data): for j, cell_text in enumerate(row): table.cell(i, j).text str(cell_text)table.style 设为 Table Grid 会应用带边框的样式这是 python-docx 内置样式名不用额外定义。注意 cell.text 赋值会覆盖单元格原有内容如果需要保留格式要操作 cell.paragraphs。表格数据量大的时候建议先做一次列宽估算避免导出后列宽不均影响阅读。4. 避坑与排查编码、反爬与文档损坏的常见问题4.1 中文乱码现象是导出文档里全是问号或方块原因通常有两个一是 requests 没有正确设置 encoding二是 python-docx 写入时字符串本身已经是错误编码。解决方法是抓取阶段打印 resp.encoding 和 resp.apparent_encoding 对比如果两者不一致且页面 meta 里声明了 charset以 meta 声明为准手动设置。写入 Word 前确保所有字符串都是 Python 的 str 类型不要传入 bytes。4.2 请求被拒现象是返回 403 或验证码页面原因是服务端识别出请求不是来自真实浏览器。解决方法是补全请求头除了 User-Agent还要带上 Referer、Accept、Accept-Encoding 等字段。如果仍然被拒检查请求频率是否过高适当加 time.sleep 随机延迟。注意不要用多线程猛刷同一个站点这既容易触发封禁也不是这套方案的设计目标。4.3 文档打不开现象是生成的 .docx 用 Word 打开提示文件损坏原因多半是图片流写入时没有正确关闭或定位。BytesIO 对象在 add_picture 之后不需要手动关闭但如果同一个流被多次读取指针会停在末尾导致后续读取为空。解决方法是每次插入图片都新建一个 BytesIO不要复用。另外doc.save 之前确保没有未捕获的异常中断了文档对象的状态。4.4 段落顺序错乱现象是导出后文字和图片顺序与网页不一致原因是抓取阶段把文本和图片分开存储写入时按类型批量处理丢失了原始顺序。解决方法是在解析阶段就按 DOM 顺序遍历遇到文本节点记文本遇到 img 标签记图片 URL生成一个有序列表写入时按列表顺序逐项处理。这个改动不大但能避免后期大量返工。4.5 样式不生效现象是设置了字体但导出后没变化原因是 python-docx 的样式继承有优先级直接对 run 设置 font 通常有效但如果模板里定义了更具体的样式规则可能会覆盖。解决方法是先用 doc.styles 查看可用样式名优先通过样式名控制而不是逐个 run 设置。如果必须精细控制确保在 add_run 之后立即设置属性不要等段落写完了再回头改。5. 进阶技巧批量任务编排与文档质量校验单页抓取跑通之后真正的挑战在于批量处理和结果校验。我一般会把整个流程拆成三个独立函数fetch_page 负责拿 HTML 并返回解析后的结构化数据build_docx 负责把结构化数据写成文档validate_docx 负责检查生成结果。这样做的好处是每一层都可以单独测试出问题时能快速定位是抓取挂了还是排版挂了。批量任务编排上不建议一上来就上多线程。先用 for 循环串行跑十个页面观察总耗时和失败率。如果瓶颈确实在网络 IO再考虑用 concurrent.futures 的 ThreadPoolExecutor把并发数控制在 3 到 5 之间。下面是一个带重试和断点记录的批量骨架。import time import json from pathlib import Path def batch_process(url_list, output_diroutput): Path(output_dir).mkdir(exist_okTrue) # 用 json 记录已完成的 URL支持断点续跑 progress_file Path(output_dir) / progress.json done set() if progress_file.exists(): done set(json.loads(progress_file.read_text(encodingutf-8))) for idx, url in enumerate(url_list): if url in done: continue try: data fetch_page(url) # 抓取与解析 doc build_docx(data) # 生成文档对象 doc.save(Path(output_dir) / fdoc_{idx}.docx) done.add(url) # 每完成一个就落盘进度避免中途崩溃丢状态 progress_file.write_text( json.dumps(list(done), ensure_asciiFalse), encodingutf-8 ) except Exception as e: print(f处理失败 {url}: {e}) # 随机延迟降低对目标站点的压力 time.sleep(1.5)这段代码里 progress.json 是关键它让批量任务具备断点续跑能力。实际跑几十个页面时中途遇到网络抖动或某个页面结构异常是常态没有进度记录就得从头再来。延迟设 1.5 秒是经验值既不会太慢也能明显降低被限流的概率。文档质量校验这块我习惯在生成后做三项检查段落数是否为零、图片占位符数量是否超过阈值、文档能否被 python-docx 重新打开。第三项尤其重要因为有些损坏在保存时不会报错只有重新读取才会暴露。下面这个校验函数可以直接复用。from docx import Document def validate_docx(file_path, min_paragraphs3, max_failed_images2): try: doc Document(file_path) except Exception as e: return False, f文档无法打开: {e} paras [p.text for p in doc.paragraphs if p.text.strip()] if len(paras) min_paragraphs: return False, f有效段落过少: {len(paras)} failed sum(1 for p in paras if 图片加载失败 in p) if failed max_failed_images: return False, f失败图片过多: {failed} return True, 校验通过min_paragraphs 和 max_failed_images 这两个阈值要根据实际页面调整。资讯类页面正文段落通常不少于 5 段设 3 是保守下限图片失败容忍度设 2 是因为偶发网络问题不可避免但超过 2 张就说明抓取环节有系统性问题需要回头检查图片 URL 的提取逻辑。最后说一个我踩过的坑早期版本里我把所有页面的内容写进同一个 Word 文档结果文档越来越大打开越来越慢一次失败还导致整个文件损坏。后来改成每个页面独立生成一个 .docx再用一个汇总脚本按需合并。合并时用 docxcompose 这类库比手动复制段落更可靠因为它能保留样式和图片关系。这个改动让单点故障不再影响全局也方便单独重跑失败页面。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?