Python爬虫实战:手把手教你采集公开地图图例说明页!
㊗️本期内容已收录至专栏《Python爬虫实战》持续完善知识体系与项目实战建议先订阅收藏后续查阅更方便㊙️本期爬虫难度指数⭐ (入门级)福利一次订阅后专栏内的所有文章可永久免费看持续更新中保底1000(篇)硬核实战内容。全文目录 开篇语0️⃣ 前言Preface1️⃣ 摘要Abstract2️⃣ 背景与需求Why3️⃣ 合规与注意事项必写⚠️4️⃣ 技术选型与整体流程What/How5️⃣ 环境准备与依赖安装可复现6️⃣ 核心实现请求层Fetcher7️⃣ 核心实现解析层Parser8️⃣ 数据存储与导出Storage9️⃣ 运行方式与结果展示必写 常见问题与排错强烈建议写1️⃣1️⃣ 进阶优化可选但加分1️⃣2️⃣ 总结与延伸阅读 文末✅ 专栏持续更新中建议收藏 订阅✅ 互动征集✅ 免责声明 开篇语哈喽各位小伙伴们你们好呀我是【喵手】。运营社区 C站 / 掘金 / 腾讯云 / 阿里云 / 华为云 / 51CTO欢迎大家常来逛逛一起学习一起进步我长期专注Python 爬虫工程化实战主理专栏 《Python爬虫实战》从采集策略到反爬对抗从数据清洗到分布式调度持续输出可复用的方法论与可落地案例。内容主打一个“能跑、能用、能扩展”让数据价值真正做到——抓得到、洗得净、用得上。专栏食用指南建议收藏✅ 入门基础环境搭建 / 请求与解析 / 数据落库✅ 进阶提升登录鉴权 / 动态渲染 / 反爬对抗✅ 工程实战异步并发 / 分布式调度 / 监控与容错✅ 项目落地数据治理 / 可视化分析 / 场景化应用专栏推广时间如果你想系统学爬虫而不是碎片化东拼西凑欢迎订阅专栏《Python爬虫实战》一次订阅后专栏内的所有文章可永久免费阅读持续更新中。订阅后更新会优先推送按目录学习更高效0️⃣ 前言Preface一句话说明今天我们将使用 Python 的requests和BeautifulSoup针对结构化的公开地图图例说明页进行抓取深入解析 HTML 表格最终产出一份包含图例分类、图片链接及含义的.csv基础字典表。读完本篇你将轻松获得掌握如何优雅地遍历和解析复杂的网页表格table,tr,td数据。学会“按层级提取上下文”的技巧例如将表格上方的h3标题作为分类字段写入表格数据中。拥有一份能自动生成 GIS 图例结构化文件及英文数据分布图的强悍脚本1️⃣ 摘要Abstract本文聚焦于 GIS 基础数据采集完整拆解了单一或分页式公开图例说明页的抓取流程。通过构建基于requests的静态请求层与基于bs4的表格解析层精准抽取嵌套在单元格内的文本与图像链接最终使用 Pandas 进行数据清洗与持久化输出gis_map_legends.csv。读完能获得什么深入理解 Web 表格元素与数据字典映射的底层逻辑。掌握遇到不规则表格如合并单元格、空行时的容错策略。结合matplotlib生成专业视角的图例分类统计柱状图。2️⃣ 背景与需求Why为什么要爬在地理信息科学GIS与制图学中图例是地图的“语言”。各大官方测绘机构或开源地图如 OpenStreetMap都有详尽的符号定义。通过自动化聚合这些图例说明我们可以快速建立本地的“制图符号知识库”用于后续的自动化地图渲染、空间数据分析或机器视觉地图识别训练。目标字段清单Category分类如“水系”、“交通”、“居民地”Legend_Name图例名如“常年河”、“高速公路”Symbol_Image符号图例图标的网络绝对路径Meaning含义该图例代表的具体地理现象或说明Notes备注使用条件或附加属性如“宽度50m的河流使用此符号”3️⃣ 合规与注意事项必写⚠️在浩瀚的地理数据海洋中航行我们必须遵守航海规则哦遵守 robots.txt测绘与地理信息网站通常较为严谨抓取前务必检查根目录下的爬虫协议。不采集敏感地理数据我们本次仅采集公开的、普适性的地图符号定义与美术图例绝不涉及敏感坐标、涉密地理信息或未公开的国防测绘数据保持技术探索的纯粹与合法。频率控制即使是单页面包含多个表格也不要高频重试保护目标站点的带宽资源。4️⃣ 技术选型与整体流程What/How技术路线本篇属于静态网页的表格结构提取。整体流程➡️采集目标页发送请求获取包含各类图例说明的 HTML 源码。➡️定位分类与表格寻找包裹图例的区块如h2分类名及其下方的table。➡️遍历单元格核心按行tr读取分别提取文本单元格td和包含图片的单元格img src...。➡️清洗拼接完整的图片 URL去除换行符。➡️存储扁平化导出。为什么选requestsbs4抓取表格数据其实pandas.read_html()是最快的。但是read_html只能提取纯文本会把包含图例图标的img src标签直接丢失因此我们必须使用 BeautifulSoup 来精细化解剖 DOM 树确保既能拿到文字又能拿到图片链接。5️⃣ 环境准备与依赖安装可复现给你的开发环境装配上专业的勘探工具箱吧️Python 版本推荐 Python 3.8依赖安装在你的终端运行pipinstallrequests beautifulsoup4 pandas matplotlib urllib3项目结构推荐gis_legend_scraper/ ├── legend_spider.py # 抓取逻辑主程序 ├── gis_map_legends.csv # 导出的符号字典数据 └── category_distribution.png # 生成的英文分类统计图6️⃣ 核心实现请求层Fetcher面对可能比较“老旧”或传统的测绘机构网站请求头一定要设置得全面且要注意网页可能存在的特殊编码如 GBK。importrequestsimporttimeimportrandomimportlogging logging.basicConfig(levellogging.INFO,format%(asctime)s - %(levelname)s - %(message)s)deffetch_legend_page(url,retries3):headers{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36,Accept:text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8}forattemptinrange(retries):try:logging.info(f Mapping expedition started:{url}(Attempt{attempt1}))responserequests.get(url,headersheaders,timeout15)response.raise_for_status()# 智能推测编码防止中文网页特别是国内测绘站点乱码response.encodingresponse.apparent_encodingreturnresponse.textexceptrequests.exceptions.RequestExceptionase:logging.warning(fConnection hiccup:{e})time.sleep(random.uniform(2,4))logging.error(Failed to reach the coordinates.)returnNone7️⃣ 核心实现解析层Parser高能来袭 很多网页的结构是先来一个h3水系图例/h3接着紧跟一个table classlegend-table。我们要在解析时动态地把h3的文本作为Category塞进后续解析出的每一行数据中。(注以下解析逻辑基于典型的“标题表格”结构模拟实际应用中需根据目标站点的真实 F12 结构调整。)frombs4importBeautifulSoupfromurllib.parseimporturljoindefparse_legend_tables(html,base_url):解析图例说明页提取跨层级的分类和表格内容ifnothtml:return[]soupBeautifulSoup(html,html.parser)legends_data[]# 假设网页由多个区块组成每个区块有一个分类标题 h3 和一个表格 table# 我们遍历所有的内容区块sectionssoup.find_all(div,class_legend-section)# 兜底如果网站没有 class_legend-section就直接找所有的 tabletablessoup.find_all(table)ifnotsectionselse[sec.find(table)forsecinsectionsifsec.find(table)]current_categoryGeneral GIS Symbols# 默认分类fortableintables:# 尝试寻找该表格前的一个标题作为“分类”prev_h3table.find_previous_sibling(h3)ifprev_h3:current_categoryprev_h3.text.strip()# 开始解析表格行rowstable.find_all(tr)forrowinrows:cellsrow.find_all([td,th])# 跳过表头行或空行ifnotcellsorcells[0].nameth:continuetry:# 假设表格结构为图例名 | 符号图片 | 含义 | 备注# 1. 提取文本字段namecells[0].text.strip()meaningcells[2].text.strip()iflen(cells)2elseN/Anotescells[3].text.strip()iflen(cells)3else# 2. 提取符号图片链接最关键的一步symbol_img_tagcells[1].find(img)ifsymbol_img_tagandsymbol_img_tag.get(src):raw_srcsymbol_img_tag.get(src)symbol_urlurljoin(base_url,raw_src)else:symbol_urlNo Image / Font Symbol# 过滤掉完全空的行ifnotnameandsymbol_urlNo Image / Font Symbol:continuelegends_data.append({Category:current_category,Legend_Name:name,Symbol_Image:symbol_url,Meaning:meaning,Notes:notes})exceptExceptionase:logging.debug(fSkipping a malformed row:{e})continuereturnlegends_data8️⃣ 数据存储与导出Storage千辛万苦挖到的测绘图例必须严谨地封存入库我们依然选用数据分析的好帮手 Pandas并确保中文字符在 CSV 中不会变成外星文。importpandasaspddefexport_to_csv(data_list,filenamegis_map_legends.csv):ifnotdata_list:logging.warning(Cartography empty! No data to save.)returndfpd.DataFrame(data_list)# 数据清洗将名称中可能存在的异常换行符替换为空格df[Legend_Name]df[Legend_Name].str.replace(r\n|\r, ,regexTrue)df[Meaning]df[Meaning].str.replace(r\n|\r, ,regexTrue)# 按照需求大纲排列字段cols[Category,Legend_Name,Symbol_Image,Meaning,Notes]dfdf[cols]df.to_csv(filename,indexFalse,encodingutf-8-sig)logging.info(f✨ Success!{len(df)}map symbols mapped and saved to{filename})9️⃣ 运行方式与结果展示必写将指南针与地图合二为一我们就可以出发啦执行python legend_spider.py。同时我们将利用 Matplotlib 输出一张展现图例分类丰富度的全英文图表。importmatplotlib.pyplotaspltdefgenerate_english_bar_chart(csv_filegis_map_legends.csv,img_filecategory_distribution.png):try:dfpd.read_csv(csv_file)ifdf.emptyorCategorynotindf.columns:return# 统计各个分类下的符号数量category_countsdf[Category].value_counts().head(8)# 展示前8大类plt.figure(figsize(10,6))# Default English Textcategory_counts.plot(kindbarh,color#2ca02c,edgecolorblack)# 使用代表地理与自然的绿色plt.title(Distribution of Map Symbols by Category,fontsize14,fontweightbold)plt.xlabel(Number of Symbols,fontsize12)plt.ylabel(Legend Category (Mapped),fontsize12)plt.gca().invert_yaxis()# 数量最多的排在最上面plt.grid(axisx,linestyle--,alpha0.7)plt.tight_layout()plt.savefig(img_file)logging.info(f English Chart saved as{img_file})exceptExceptionase:logging.error(fVisualization error:{e})defmain():target_domainhttps://example-gis-portal.com# 占位符target_urlf{target_domain}/legends-guide# 1. 发起请求htmlfetch_legend_page(target_url)# 2. 解析表格内容dataparse_legend_tables(html,target_domain)# 3. 存储与图表渲染export_to_csv(data)generate_english_bar_chart()if__name____main__:# main() # 演示代码需替换真实站点的 URL 及 HTML 结构特征pass展示 3 行示例结果模拟数据CategoryLegend_NameSymbol_ImageMeaningNotesHydrographyPerennial Riverhttps://…/img/river.pngFlowing water year-roundWidth 20m in scale 1:50kTransportationNational Highwayhttps://…/img/hwy.pngMajor interconnected roadsRed double lineTopographyContour Linehttps://…/img/contour.svgEqual elevation lineInterval depends on scale 常见问题与排错强烈建议写在挖坟哦不挖掘古老或奇葩网页表格时你一定会遇到这些拦路虎报错IndexError: list index out of range诊断某些tr行可能存在合并单元格colspan导致这一行并没有你预期的 4 个td。解决永远先检查长度像代码里写的if len(cells) 2:。或者跳过这种非数据行。提取的文本全是\xa0\n\t这种乱码或空白诊断网页里充斥着 HTML 不断行空格nbsp;和回车。解决在cell.text后务必加上.strip()去除首尾空白对于中间的空白可以使用replace(\xa0, )进行清洗。图例不是图片是一个 CSS 色块比如一个红色的正方形div stylebackground: red;解决这是现代 WebGIS 常用的技巧你需要修改 Parser 逻辑不找img而是提取div的style属性或class颜色属性color cell.find(div).get(style)。1️⃣1️⃣ 进阶优化可选但加分想从一名普通的测绘员晋升为 GIS 架构师吗试试这些高端操作符号物理下载器仅有 URL 不够过瘾引入 Python 的urllib.request.urlretrieve顺着Symbol_Image链接把所有.png和.svg下载到本地icons/文件夹下并以Legend_Name.svg命名。你的本地符号库瞬间建成输出 GeoJSON 或 Mapbox Style JSON既然是 GIS 数据何不一步到位写一个转换函数将 CSV 数据直接映射转化为 Mapbox GL JS 可以直接识别的style.json格式直接在前端地图上渲染出来️处理多页分页如果图例页按字母 A-Z 分页显示可以在外部套一个循环构造如?letterA的 URL结合列表扩展legends_data.extend(new_data)实现全站收割。1️⃣2️⃣ 总结与延伸阅读呼干得漂亮 我们今天顺利完成了一次“网页制图学”的探险。你不仅学会了如何对付复杂的 DOM 表格元素还掌握了如何在同一张表里结合文本与图片链接的混合提取。这份数据集不仅是你的代码结晶更是理解数字地球的一把钥匙️下一步可以探索什么有了这些地图符号数据你可以尝试去了解一下 Python 领域的空间数据处理神器——Geopandas或者去玩一玩Folium / Kepler.gl。你可以试着把你抓取到的图例自定义成标记图标撒点到一张真实的交互式地图上。 文末好啦以上就是本期的全部内容啦如果你在实践过程中遇到任何疑问欢迎在评论区留言交流我看到都会尽量回复咱们下期见小伙伴们在批阅的过程中如果觉得文章不错欢迎点赞、收藏、关注哦三连就是对我写作道路上最好的鼓励与支持❤️✅ 专栏持续更新中建议收藏 订阅墙裂推荐订阅专栏 《Python爬虫实战》本专栏秉承着以“入门 → 进阶 → 工程化 → 项目落地”的路线持续更新争取让每一期内容都做到✅ 讲得清楚原理✅ 跑得起来代码✅ 用得上场景✅ 扛得住工程化想系统提升的小伙伴强烈建议先订阅专栏 《Python爬虫实战》再按目录大纲顺序学习效率十倍上升✅ 互动征集想让我把【某站点/某反爬/某验证码/某分布式方案】等写成某期实战评论区留言告诉我你的需求我会优先安排实现(更新)哒~⭐️ 若喜欢我就请关注我叭更新不迷路⭐️ 若对你有用就请点赞支持一下叭给我一点点动力⭐️ 若有疑问就请评论留言告诉我叭我会补坑 更新迭代✅ 免责声明本文爬虫思路、相关技术和代码仅用于学习参考对阅读本文后的进行爬虫行为的用户本作者不承担任何法律责任。使用或者参考本项目即表示您已阅读并同意以下条款合法使用 不得将本项目用于任何违法、违规或侵犯他人权益的行为包括但不限于网络攻击、诈骗、绕过身份验证、未经授权的数据抓取等。风险自负 任何因使用本项目而产生的法律责任、技术风险或经济损失由使用者自行承担项目作者不承担任何形式的责任。禁止滥用 不得将本项目用于违法牟利、黑产活动或其他不当商业用途。使用或者参考本项目即视为同意上述条款,即 “谁使用谁负责” 。如不同意请立即停止使用并删除本项目。