Scrapy爬取的GIS数据坐标总是偏移?教你用Proj4进行投影转换(附:坐标系速查表)

编程与开发
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

Scrapy爬取的GIS数据坐标总是偏移?教你用Proj4进行投影转换(附:坐标系速查表)这类问题,本质上通常不是 Scrapy 抓取错了,而是网页接口返回的坐标系与你在 GIS 软件、WebGIS 地图底图或数据库中使用的坐标系不一致。

很多同学用 Scrapy 抓取 POI、地块边界、轨迹点、行政区边界时,发现经纬度看起来“差不多”,但加载到 QGIS、ArcGIS Pro、Leaflet 或 PostGIS 后,总是偏几百米、几公里,甚至直接跑到另一个城市。本文用一个实用流程讲清楚:如何判断坐标系、如何用 Proj4 做投影转换,以及如何建立一张常用坐标系速查表,避免 Scrapy 爬取 GIS 数据坐标偏移。

引言:Scrapy爬取GIS数据为什么会出现坐标偏移

Scrapy 只负责从网页、接口或文件中提取数据。它不会自动知道某个字段是 WGS84 经纬度、GCJ-02 火星坐标、BD-09 百度坐标,还是 Web Mercator 米制坐标。

常见现象包括:

  • 经纬度字段看起来正常,例如 116.397, 39.908,但叠加到底图后偏移。
  • 坐标值很大,例如 12958000, 4852000,在 QGIS 中按经纬度加载后位置完全错误。
  • 同一批数据在高德地图上位置正确,在 OpenStreetMap 或 ArcGIS Pro 中偏移。
  • 接口文档没有写坐标系,只给了字段名,例如 lnglatxy

解决 Scrapy 爬取 GIS 数据坐标偏移,核心不是反复改字段名,而是先确认源坐标系,再转换到目标坐标系。

Scrapy爬取GIS数据坐标偏移 Proj4投影转换流程图
Scrapy 抓取坐标后,应先识别源坐标系,再使用 Proj4 或相关库转换到目标坐标系。

背景:网页接口中的GIS坐标不一定是WGS84

很多入门教程会默认“经纬度就是 WGS84”。这个说法在 GPS、GeoJSON、OpenStreetMap 场景中比较常见,但在中文互联网地图和业务系统中并不总是成立。

Scrapy 爬取 GIS 数据时,可能遇到以下坐标来源:

  • 政府公开数据接口:可能是 CGCS2000、地方投影坐标系、WGS84 或自定义坐标。
  • 商业地图接口:可能是 GCJ-02、高德坐标、腾讯坐标、BD-09 百度坐标。
  • WebGIS 瓦片服务:常见为 EPSG:3857,即 Web Mercator。
  • 旧系统导出的坐标:可能是北京54、西安80、高斯克吕格投影坐标。
  • GeoJSON 或 Shapefile:需要查看元数据、PRJ 文件或接口说明。

如果你把 GCJ-02 当成 WGS84 加载,就会出现国内常见的地图偏移。如果你把 EPSG:3857 的米制坐标当成经纬度,位置会完全错误。如果你把地方投影坐标缺少中央经线信息直接转换,也会得到看似合理但实际错误的结果。

原理:Proj4投影转换解决的是什么问题

Proj4 是一种描述坐标参考系统和投影参数的文本格式,也常被用来泛指基于 PROJ 库的坐标转换能力。它可以描述椭球、投影方式、中央经线、单位、基准面等信息。

一个典型的 WGS84 经纬度 Proj4 字符串如下:

+proj=longlat +datum=WGS84 +no_defs

一个常见的 Web Mercator Proj4 字符串如下:

+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs

Proj4 投影转换主要解决的是:

  • 经纬度坐标与投影平面坐标之间的转换。
  • 不同椭球和基准面之间的坐标表达转换。
  • 同一空间位置在不同坐标参考系统中的数值变化。

但要注意:Proj4 不能直接解决所有互联网地图“加偏”问题。例如 GCJ-02 和 BD-09 属于带有加密偏移规则的坐标体系,不能只靠普通 EPSG 投影参数完成严格转换,通常需要专门的纠偏算法或合规的数据服务。

步骤:用Proj4处理Scrapy爬取GIS数据坐标偏移

步骤1:先观察坐标值范围

拿到 Scrapy 抓取结果后,不要直接导入 GIS。先抽样查看坐标字段。

坐标值特征 可能坐标系 判断建议
经度约 -180 到 180,纬度约 -90 到 90 WGS84、GCJ-02、BD-09 或其他地理坐标系 需要进一步叠加底图验证是否偏移
x、y 为数百万到上千万 Web Mercator 或投影坐标系 不要按经纬度加载,应检查 EPSG 或投影参数
x 为 3 开头或 4 开头的六七位数,y 为数百万 高斯克吕格、地方投影 需要确认带号、中央经线和基准面
经纬度在国内,但叠加 OSM 偏移几百米 GCJ-02 或 BD-09 需判断来源是否为高德、腾讯或百度

步骤2:从接口和网页脚本中找坐标系线索

Scrapy 抓取页面时,坐标系线索经常藏在接口参数、JavaScript 变量或地图初始化代码里。

可以重点搜索这些关键词:

  • epsg
  • crs
  • projection
  • wkid
  • spatialReference
  • EPSG:4326
  • EPSG:3857
  • BD09
  • GCJ02

如果接口返回类似下面的内容,说明数据坐标系已经写在响应里:

{
  "geometry": {
    "x": 12958175.0,
    "y": 4852834.0,
    "spatialReference": {
      "wkid": 3857
    }
  }
}

这里的 wkid: 3857 表示 Web Mercator,不能直接当成 WGS84 经纬度使用。

步骤3:安装Python中的Proj4相关库

在 Python 项目中,通常不直接手写底层 Proj4 转换,而是使用 pyproj。它是 PROJ 的 Python 接口,适合与 Scrapy、GeoPandas、Shapely、PostGIS 工作流衔接。

pip install pyproj

如果后续需要写出 GeoJSON、Shapefile 或 GeoPackage,也可以安装:

pip install geopandas shapely

步骤4:将EPSG:3857转换为WGS84经纬度

假设 Scrapy 抓取到的接口坐标是 EPSG:3857,目标是转换为 WGS84,也就是 EPSG:4326,可使用以下代码:

from pyproj import Transformer

transformer = Transformer.from_crs("EPSG:3857", "EPSG:4326", always_xy=True)

x = 12958175.0
y = 4852834.0

lon, lat = transformer.transform(x, y)

print(lon, lat)

这里必须注意 always_xy=True。它表示输入输出顺序固定为 x、y,也就是经度、纬度或东坐标、北坐标。很多坐标转换错误并不是公式错,而是经纬度顺序被调换了。

步骤5:在Scrapy管道中统一转换坐标

实际项目中,建议不要在 Spider 的解析函数里到处写转换逻辑,而是放到 Scrapy Pipeline 中统一处理。

from pyproj import Transformer

class CoordinateTransformPipeline:
    def __init__(self):
        self.transformer = Transformer.from_crs(
            "EPSG:3857",
            "EPSG:4326",
            always_xy=True
        )

    def process_item(self, item, spider):
        x = float(item["x"])
        y = float(item["y"])

        lon, lat = self.transformer.transform(x, y)

        item["lon_wgs84"] = lon
        item["lat_wgs84"] = lat
        item["source_crs"] = "EPSG:3857"
        item["target_crs"] = "EPSG:4326"

        return item

然后在 Scrapy 的配置中启用 Pipeline:

ITEM_PIPELINES = {
    "your_project.pipelines.CoordinateTransformPipeline": 300,
}

这样做的好处是:坐标转换逻辑集中、结果字段清晰、后续检查方便,也避免不同 Spider 使用不同转换规则。

步骤6:把转换结果导入QGIS验证

坐标转换完成后,不要只看代码输出。应导出 CSV 或 GeoJSON,在 QGIS 中叠加底图验证。

推荐验证流程:

  1. 导出字段:namelon_wgs84lat_wgs84source_crs
  2. 在 QGIS 中选择“添加分隔文本图层”。
  3. x 字段选择 lon_wgs84,y 字段选择 lat_wgs84
  4. 坐标参考系统选择 EPSG:4326 – WGS 84
  5. 叠加 OpenStreetMap 或天地图等底图检查位置。

如果点位仍然偏移,应回头检查源坐标系是否判断错误,或者数据是否本身来自 GCJ-02、BD-09。

常见坑:Scrapy爬取GIS数据坐标偏移排查重点

把GCJ-02当成WGS84

国内许多商业地图坐标不是标准 WGS84。高德、腾讯常见 GCJ-02,百度常见 BD-09。如果把这些坐标直接放到 QGIS 的 WGS84 图层中,通常会出现肉眼明显的偏移。

判断方法很简单:如果数据来自高德、腾讯、百度地图接口,而接口没有明确说明返回 WGS84,就不要默认它是 WGS84。

把EPSG:3857当成经纬度

Web Mercator 是米制坐标,常用于网页瓦片地图。它的 x、y 数值一般很大。如果你看到坐标值超过经纬度范围,却仍按 EPSG:4326 加载,结果一定会错。

经纬度顺序写反

有些接口返回 lng, lat,有些返回 lat, lng。GeoJSON 标准坐标顺序通常是 [longitude, latitude]。如果顺序反了,中国区域的点可能跑到异常位置。

只转换坐标,不记录源坐标系

很多项目后期无法复查,就是因为只保存了转换后的经纬度,没有保存源坐标、源坐标系、转换时间和转换方法。建议至少保留:

  • x_source
  • y_source
  • source_crs
  • lon_wgs84
  • lat_wgs84
  • transform_method

地方坐标系缺少参数

部分政府或工程数据使用地方独立坐标系,仅凭 EPSG:4326 或 EPSG:3857 无法正确转换。此时需要向数据提供方确认中央经线、投影带、椭球、七参数或转换网格等信息。

方法比较:Proj4、GeoPandas、GDAL和PostGIS怎么选

方法 适用场景 优点 注意事项
pyproj / Proj4 Scrapy 抓取后逐条转换点坐标 轻量、代码清晰、适合 Pipeline 必须明确源 CRS 和目标 CRS
GeoPandas 批量处理 GeoJSON、Shapefile、GeoPackage 适合矢量数据表处理 需要正确设置原始 CRS 后再 to_crs
GDAL / ogr2ogr 命令行批量转换空间文件 稳定,适合自动化数据生产 参数较多,新手容易混淆源 CRS 与目标 CRS
PostGIS ST_Transform 数据已入库,需要数据库内转换 适合空间查询和服务发布 几何字段必须有正确 SRID

如果你的核心流程是 Scrapy 抓取接口数据,推荐优先使用 pyproj 在 Pipeline 中转换。如果数据已经整理成空间文件,再用 GeoPandas 或 GDAL。如果数据已经进入 PostGIS,则使用 ST_SetSRIDST_Transform

GeoPandas批量转换示例

import geopandas as gpd
from shapely.geometry import Point

rows = [
    {"name": "point_a", "x": 12958175.0, "y": 4852834.0},
    {"name": "point_b", "x": 12959000.0, "y": 4853000.0}
]

gdf = gpd.GeoDataFrame(
    rows,
    geometry=[Point(row["x"], row["y"]) for row in rows],
    crs="EPSG:3857"
)

gdf_wgs84 = gdf.to_crs("EPSG:4326")

gdf_wgs84.to_file("scrapy_points_wgs84.geojson", driver="GeoJSON")

PostGIS转换示例

如果原始坐标是 EPSG:3857,表中已有 xy 字段,可以先构造几何,再转换为 EPSG:4326。

ALTER TABLE poi ADD COLUMN geom_3857 geometry(Point, 3857);
ALTER TABLE poi ADD COLUMN geom_4326 geometry(Point, 4326);

UPDATE poi
SET geom_3857 = ST_SetSRID(ST_MakePoint(x, y), 3857);

UPDATE poi
SET geom_4326 = ST_Transform(geom_3857, 4326);

注意:ST_SetSRID 只是声明坐标系,不会改变坐标值;ST_Transform 才会真正进行坐标转换。

检查清单:坐标系速查表与项目落地建议

下面这张表可以作为 Scrapy 爬取 GIS 数据后的坐标系速查表。它不能替代正式数据说明,但能帮助你快速缩小排查范围。

名称 常见标识 坐标特征 常见来源 处理建议
WGS84 经纬度 EPSG:4326 经度 -180 到 180,纬度 -90 到 90 GPS、OSM、标准 GeoJSON 可直接用于多数 GIS 分析,但要确认是否真为 WGS84
Web Mercator EPSG:3857 x、y 为米制大数值 WebGIS 瓦片、在线地图底图 用 Proj4、pyproj 或 ST_Transform 转 EPSG:4326
GCJ-02 火星坐标 看似经纬度,但在 WGS84 底图上偏移 高德、腾讯等国内地图 需使用合规纠偏方法,不能只靠普通投影转换
BD-09 百度坐标 相对 GCJ-02 还有二次偏移 百度地图 通常需先转 GCJ-02,再视需求转 WGS84
CGCS2000 EPSG:4490 或投影分带 可为经纬度,也可为投影坐标 国内测绘、自然资源数据 确认是地理坐标还是高斯投影坐标
西安80 / 北京54 地方历史坐标 常见于老工程、规划、地籍数据 历史测绘资料 需要精确转换参数,不能盲转

项目中建议按下面的清单执行:

  • 确认接口来源:政府平台、商业地图、WebGIS 服务还是文件下载。
  • 检查字段名:lnglatxy 不等于坐标系说明。
  • 检查坐标范围:先判断是经纬度还是投影坐标。
  • 查找接口文档:优先相信官方 CRS、EPSG、WKID、PRJ 信息。
  • 统一转换代码:推荐放入 Scrapy Pipeline,而不是分散在解析函数中。
  • 保留源坐标:不要只保存转换结果。
  • 用 QGIS 叠加底图验证:至少抽样检查城市中心、道路交叉口、行政边界。
  • 遇到 GCJ-02、BD-09、地方坐标系时,不要假装它们是普通 EPSG 转换。

FAQ:Scrapy爬取GIS数据坐标偏移常见问题

Scrapy爬取的经纬度为什么在QGIS中偏几百米?

最常见原因是源坐标不是 WGS84,而是 GCJ-02 或 BD-09。QGIS 默认按 EPSG:4326 加载后,会与标准底图产生偏移。应先确认数据来源,再选择对应转换方法。

Proj4能不能把百度坐标直接转成WGS84?

普通 Proj4 投影参数不能直接表达 BD-09 的加密偏移规则。BD-09 到 WGS84 通常需要专门算法或合规服务。Proj4 更适合处理标准坐标参考系统之间的投影转换,例如 EPSG:3857 到 EPSG:4326。

EPSG:4326和WGS84是一回事吗?

在多数 GIS 软件中,EPSG:4326 通常表示 WGS84 地理坐标系,单位为度。但实际项目仍要注意坐标顺序和数据来源,不能仅凭字段名判断。

为什么pyproj转换结果经纬度顺序不对?

可能是轴顺序导致的。建议在 Transformer.from_crs 中设置 always_xy=True,让输入输出始终按 x、y 顺序处理,也就是经度在前、纬度在后。

Scrapy抓到的坐标值是12900000这种大数,应该怎么处理?

这通常不是经纬度,而可能是 EPSG:3857 Web Mercator 或其他投影坐标。应先确认接口中的 wkidepsg 或地图初始化参数,再用 Proj4、pyproj、GDAL 或 PostGIS 转换到目标坐标系。

没有接口文档时,怎么判断坐标系?

可以结合坐标数值范围、网页地图类型、JS 初始化代码、网络请求参数、底图服务地址和抽样叠加验证来判断。若仍无法确定,应把“坐标系未知”作为数据质量风险记录下来,不要直接进入正式分析。

结论:先识别坐标系,再谈Proj4投影转换

Scrapy 爬取 GIS 数据坐标偏移,通常不是爬虫本身的问题,而是源坐标系、目标坐标系和地图底图之间没有对齐。正确流程应是:先识别坐标值特征和接口坐标系,再用 Proj4、pyproj、GeoPandas、GDAL 或 PostGIS 做标准转换,最后在 QGIS 或 WebGIS 中叠加验证。

对于 EPSG:3857、EPSG:4326、CGCS2000 等标准坐标参考系统,Proj4 投影转换非常适合自动化处理。对于 GCJ-02、BD-09 和地方独立坐标系,则必须额外确认转换规则和数据合规要求。只要把源坐标、目标坐标、转换方法和验证过程记录清楚,Scrapy 抓取 GIS 数据后的坐标偏移问题就能被系统性排查和解决。