ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Selenium抓地图数据+Matplotlib可视化实战指南

Selenium抓地图数据+Matplotlib可视化实战指南 简介本资源是一套面向Python初学者与进阶学习者的地图可视化实战项目聚焦Selenium网页数据抓取与Matplotlib地理图表绘制两大核心技能适用于数据分析、地理信息可视化及自动化采集等实际场景。压缩包共27个文件含4个CSV数据源如GDP-2018.csv、all_codes.csv、8个TXT配置与编码映射文件province.txt、code_map.txt等、14张已生成的省级/城市级可视化PNG图表涵盖GDP排名、人口分布、气泡图等以及1个主程序Map_of_China.py完整呈现从数据获取、清洗、编码映射到多维度地图渲染的全流程。资源大小5.36MB结构清晰、模块解耦便于逐层理解与复用。已有98人学习下载配套代码注释详尽包含坐标定位逻辑、省份编码匹配策略及Matplotlib地理着色技巧可直接运行调试并拓展至全国其他地理维度分析。1. 为什么用 Selenium 抓地图数据、再用 Matplotlib 渲染比直接调 API 更可控很多开发者一看到“地图可视化”第一反应是接入高德、百度或腾讯地图 JS SDK——但这类方案在批量导出静态图、嵌入离线报告、做自动化地理分析时立刻卡住JS 渲染依赖浏览器上下文截图分辨率难控坐标系转换黑盒且无法与 pandas、scikit-learn 等科学计算栈无缝衔接。而Selenium Matplotlib这个组合本质是把“地图”拆解为两个可编程环节前端动态加载的地理数据采集Selenium和后端结构化坐标渲染Matplotlib。它不追求实时交互而是解决「从网页中稳定提取带地理标签的原始数据如某市各街道人口密度、某平台门店热力分布再按业务逻辑定制绘图样式」这一类典型需求。适合数据分析师、GIS 工程师、自动化报表开发人员——尤其当目标页面没有开放 API、或返回数据格式混乱如 HTML 表格嵌套坐标、JavaScript 动态注入经纬度、或需模拟用户真实操作路径如切换行政区划、点击筛选按钮触发地图重绘时这套方案反而更鲁棒。它不替代专业 GIS 工具但能快速补位 Python 生态中“网页地理数据 → 可复现图表”的最后一公里。2. Selenium 抓取地图页面的核心策略绕过渲染陷阱精准定位地理数据2.1 为什么不能只靠 requests BeautifulSoup关键在于动态坐标注入多数地图页面如政府公开数据平台、企业门店查询页使用 Leaflet、ECharts 或自研地图引擎地理要素点、面、热力层并非写死在 HTML 中而是通过 JavaScript 在 DOM 加载后动态创建。例如div idmap-container/div script // 地图初始化后通过 AJAX 获取 geojson 数据并 addLayer() fetch(/api/regions?cityshanghai) .then(r r.json()) .then(data map.addLayer(L.geoJSON(data))); /script此时requests.get()返回的 HTML 里只有空容器div idmap-container/div真实坐标数据藏在后续异步响应中。Selenium 的价值正在于此它启动真实浏览器环境等待 JS 执行完成、DOM 更新完毕再提取最终渲染结果。提示不要用time.sleep(5)等固定时长——网络波动或页面逻辑变更会导致抓取失败。必须基于元素状态做显式等待。2.2 定位地理数据的三种可靠路径附可执行代码2.2.1 路径一监听 Network 面板中的 XHR 响应推荐用于结构化数据若页面通过 AJAX 加载 GeoJSON 或 CSV 格式地理数据可在 Selenium 启动时启用 Chrome DevTools Protocol (CDP) 捕获请求from selenium import webdriver from selenium.webdriver.chrome.options import Options import json chrome_options Options() chrome_options.add_argument(--headless) # 无头模式 chrome_options.add_argument(--no-sandbox) chrome_options.add_argument(--disable-dev-shm-usage) driver webdriver.Chrome(optionschrome_options) # 启用 CDP 监听网络请求 driver.execute_cdp_cmd(Network.enable, {}) driver.get(https://example-map-page.com/shanghai) # 等待特定 URL 的响应完成 def wait_for_geojson_request(driver, target_url_part): logs driver.get_log(performance) for log in logs: message json.loads(log[message])[message] if Network.responseReceived in message[method]: if target_url_part in message[params][response][url]: return message[params][response][url] return None # 示例等待包含 /geojson 的请求 geojson_url wait_for_geojson_request(driver, /geojson) if geojson_url: # 用 requests 直接获取该 URL 数据避免 Selenium 解析 JSON import requests geo_data requests.get(geojson_url).json() print(f成功捕获 GeoJSON共 {len(geo_data[features])} 个地理要素)此方法优势在于数据格式标准GeoJSON、字段明确feature.geometry.coordinates、无需解析 HTML 结构。适用于政府数据门户、开源地图平台等规范接口。2.2.2 路径二从渲染后的 DOM 中提取 SVG/Canvas 坐标适用于无 API 的商业地图当页面使用 Canvas 绘制热力图或 SVG 绘制行政区划时坐标已转为像素值需反向映射到地理坐标系。以 SVG 为例# 假设地图容器为 svg idmap-svg每个区域用 path dM... 描述 svg_element driver.find_element(id, map-svg) paths svg_element.find_elements(tag name, path) region_data [] for path in paths: # 获取 path 的 d 属性SVG 路径指令 d_attr path.get_attribute(d) # 获取绑定的>driver.execute_cdp_cmd(Console.enable, {}) logs [] def log_listener(event): if event[level] info and coordinates in event.get(text, ): logs.append(event[text]) driver.add_event_listener(Console.messageAdded, log_listener) driver.get(https://example.com/map) # 等待日志出现 import time time.sleep(3) print(捕获到控制台日志:, logs)此法不稳定仅作开发期辅助验证生产环境禁用。2.3 处理常见反爬干扰WebDriver 检测与等待策略检测规避添加--disable-blink-featuresAutomationControlled参数并覆盖navigator.webdriver属性chrome_options.add_argument(--disable-blink-featuresAutomationControlled) driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, { get: () undefined }) })智能等待用WebDriverWait等待地图容器渲染完成而非页面加载from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 等待地图容器内出现至少一个地理要素如 classmarker 或>import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.pyplot as plt # 创建 PlateCarree 投影WGS84用于数据输入但用 LambertConformal 显示 ax plt.axes(projectionccrs.LambertConformal( central_longitude105, # 中国中心经度 central_latitude35, # 中国中心纬度 standard_parallels(25, 47) # 标准纬线控制变形 )) # 添加基础底图可选 ax.add_feature(cfeature.COASTLINE, linewidth0.5) ax.add_feature(cfeature.BORDERS, linewidth0.5) # 绘制点数据如门店坐标 lons [121.47, 116.40, 114.05] # 上海、北京、深圳经度 lats [31.23, 39.90, 22.54] # 对应纬度 ax.scatter(lons, lats, transformccrs.PlateCarree(), s50, cred, alpha0.8, label门店位置) plt.legend() plt.title(全国重点城市门店分布Lambert 投影) plt.show()参数说明central_longitude/latitude定义投影中心影响变形最小区域standard_parallels两条标准纬线投影在此处无长度变形transformccrs.PlateCarree()声明输入数据坐标系Matplotlib 自动转换。提示若不用 cartopy可用pyproj.Transformer.from_crs(EPSG:4326, EPSG:32650)手动转换坐标但需自行处理边界裁剪和底图绘制。3.2 分级统计可视化用pcolormesh实现热力网格当数据为栅格化热力如每平方公里人口数避免用scatter画数万点改用pcolormesh绘制矩形网格import numpy as np # 假设已从 Selenium 抓取上海 1km×1km 网格热力数据 # grid_data.shape (100, 100)对应经度范围 [120.9, 121.9]纬度 [30.7, 31.7] lon_edges np.linspace(120.9, 121.9, 101) # 101 条经线边 lat_edges np.linspace(30.7, 31.7, 101) # 101 条纬线边 # 创建 meshgrid lon_mesh, lat_mesh np.meshgrid(lon_edges, lat_edges) # 绘制热力图注意pcolormesh 需传入 edges非 centers im ax.pcolormesh(lon_edges, lat_edges, grid_data, transformccrs.PlateCarree(), cmapYlOrRd, alpha0.9, shadingflat) # 添加颜色条 cbar plt.colorbar(im, axax, orientationvertical, shrink0.6) cbar.set_label(人口密度人/km²)关键参数shadingflat指定网格单元颜色由grid_data[i,j]决定对应(lon_edges[i], lat_edges[j])到(lon_edges[i1], lat_edges[j1])区域cmap选择地理可视化常用色阶如YlOrRd黄→橙→红表示递增数值alpha0.9保留底图可见性避免完全遮盖海岸线。3.3 多子图合成六张图合在一个图上Matplotlib 六张图合在一个图上标题中提到的“matplotlib六张图合在一个图上”是典型报表需求。用plt.subplots()创建 2×3 网格每个子图展示不同维度fig, axes plt.subplots(2, 3, figsize(15, 10), subplot_kw{projection: ccrs.LambertConformal( central_longitude105, central_latitude35)}) # 子图 0,0全国门店数量 axes[0,0].scatter(china_lons, china_lats, transformccrs.PlateCarree(), s10) axes[0,0].set_title(全国门店分布) # 子图 0,1华东热力图 im1 axes[0,1].pcolormesh(east_lon_edges, east_lat_edges, east_grid, transformccrs.PlateCarree(), cmapReds) axes[0,1].set_title(华东人口热力) # 子图 1,03D 地区柱状图Matplotlib 3D 地区地图可视化大屏样式 ax3d fig.add_subplot(2, 3, 5, projection3d) # 用 bar3d 绘制各省 GDP简化示意 xpos np.arange(len(provinces)) ypos np.zeros(len(provinces)) zpos np.zeros(len(provinces)) dx dy 0.5 dz gdp_values ax3d.bar3d(xpos, ypos, zpos, dx, dy, dz, colorskyblue, alpha0.7) ax3d.set_title(各省 GDP亿元) # 其他子图依此类推... plt.tight_layout() plt.savefig(dashboard_map.png, dpi300, bbox_inchestight)核心技巧subplot_kw统一传递投影参数避免重复设置plt.tight_layout()自动调整子图间距防止标题重叠bbox_inchestight裁剪空白边距适配大屏展示。4. 从网页到图表的完整流水线参数配置表与排错清单4.1 关键参数速查表可直接复制到项目配置文件参数名作用推荐值说明SELENIUM_HEADLESS是否启用无头模式True生产环境必开节省资源SELENIUM_TIMEOUT显式等待超时秒数30避免因网络慢导致脚本卡死MAP_PROJECTIONMatplotlib 地图投影类型LambertConformal中国区域推荐比PlateCarree失真小GRID_RESOLUTION_KM热力图网格精度11km 网格适合城市级10km 适合省级MATPLOTLIB_DPI输出图像分辨率300打印/汇报需高 DPI网页展示用150CHROMEDRIVER_PATHChromeDriver 路径/usr/local/bin/chromedriverLinux 系统需绝对路径Windows 用rC:\chromedriver.exe4.2 典型报错与修复方案按发生频率排序4.2.1 Selenium 报错NoSuchElementException即使元素存在现象driver.find_element(id, map-container)报错但浏览器手动检查该元素存在。原因元素在 iframe 内或被动态加载到 shadow DOM。修复# 检查是否在 iframe 中 iframes driver.find_elements(tag name, iframe) for iframe in iframes: driver.switch_to.frame(iframe) try: elem driver.find_element(id, map-container) break except: driver.switch_to.default_content() continue # 检查 shadow DOMChrome 95 shadow_root driver.execute_script(return document.querySelector(#map-host).shadowRoot) elem shadow_root.find_element(css selector, .map-canvas)4.2.2 Matplotlib 报错UserWarning: No contour levels were found within the data range现象contourf()或pcolormesh()不显示颜色仅绘出坐标轴。原因grid_data全为 NaN 或常数或lon_edges/lat_edges未严格递增。修复# 强制检查数据有效性 assert not np.isnan(grid_data).all(), 网格数据全为 NaN assert np.min(grid_data) ! np.max(grid_data), 网格数据无变化 assert np.all(np.diff(lon_edges) 0), 经度边不递增4.2.3 坐标偏移地图上点位置与实际不符现象上海坐标 (121.47, 31.23) 显示在杭州附近。原因未指定transform参数Matplotlib 默认按平面坐标处理。修复所有地理绘图函数必须显式传入transformccrs.PlateCarree()。4.3 性能优化技巧减少 Selenium 启动开销每次抓取都新建 WebDriver 实例极慢。复用会话# 全局单例驱动注意线程安全 _driver None def get_driver(): global _driver if _driver is None: _driver webdriver.Chrome(optionschrome_options) _driver.implicitly_wait(10) return _driver # 使用时 driver get_driver() driver.get(https://target.com/map1) # ... 抓取逻辑 # 不关闭 driver下次复用注意长期运行需定期清理缓存避免内存泄漏driver.execute_cdp_cmd(Network.clearBrowserCache, {})。5. 进阶技巧用 Basemap 替代 Cartopy 实现旧系统兼容Cartopy 依赖 Proj 库在 CentOS 7 等旧系统上安装复杂。若环境受限可用basemap虽已停止维护但稳定from mpl_toolkits.basemap import Basemap # 初始化等距圆柱投影简单但有失真 m Basemap(projectioncyl, llcrnrlat18, urcrnrlat54, llcrnrlon73, urcrnrlon136, resolutioni) # 将经纬度转换为投影坐标 x, y m(lons, lats) m.scatter(x, y, s50, cblue, alpha0.7) # 绘制中国国界需下载 shapefile m.readshapefile(shapefiles/china, china, linewidth1.5) plt.title(Basemap 绘制中国地图) plt.show()关键差异Basemap的readshapefile()需提前下载.shp文件如 Natural Earth 数据投影选项少cyl,merc,tmercLambert 支持弱但pip install basemap在 Python 3.8 下仍可编译适合遗留系统。验证 Basemap 是否正常工作# 测试坐标转换 test_lon, test_lat 121.47, 31.23 x_test, y_test m(test_lon, test_lat) print(f({test_lon}, {test_lat}) → ({x_test:.2f}, {y_test:.2f})) # 应输出合理数值若输出(nan, nan)说明投影范围设置错误需调整llcrnrlat/urcrnrlat参数。本文还有配套的精品资源点击获取
返回列表