Scrapy爬虫怎么读?GIS数据采集实战教学(附:坐标转换代码)

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

引言:很多 GIS 初学者第一次看到“Scrapy”会先问:Scrapy爬虫怎么读?GIS数据采集实战教学(附:坐标转换代码)到底讲的是读音,还是讲爬虫采集?简单说,Scrapy 通常读作“斯克瑞皮”或接近英文音标中的“Scray-pee”,它是 Python 生态中常用的网页数据采集框架。对 GIS 学习者来说,更重要的是:如何用 Scrapy 采集带地址、经纬度、行政区、POI 信息的数据,并把它转换成可用于 QGIS、ArcGIS Pro、GeoPandas 或 WebGIS 的空间数据。

本文以一个常见 GIS 数据采集场景为例:从网页列表中抓取地点名称、地址、坐标字段,清洗后进行坐标转换,最后导出 GeoJSON。重点不是做复杂反爬,而是让你理解 Scrapy 在 GIS 数据工作流中的位置,以及坐标转换为什么不能省略。

Scrapy爬虫怎么读 GIS数据采集 坐标转换流程图
Scrapy 采集 GIS 数据的典型流程:网页数据提取、字段清洗、坐标转换、导出空间数据并在 GIS 软件中验证。

背景:GIS 数据采集为什么会用到 Scrapy

背景:GIS 项目中经常需要补充外部数据,例如门店 POI、公共设施名录、项目公告位置、地名地址表、景区列表、监测站点信息等。这类数据有时以网页列表、分页表格或详情页形式存在,手工复制效率低,也容易漏字段。

Scrapy 的优势在于可以把“打开网页、翻页、进入详情页、提取字段、保存结果”组织成一个稳定的采集流程。相比只用 requests 和 BeautifulSoup,Scrapy 更适合批量任务,因为它自带请求调度、异步下载、数据管道、错误重试等机制。

但 GIS 数据采集有一个额外问题:网页中的坐标不一定是 WGS84。你在网页上看到的经纬度,可能是 GCJ-02、BD-09、火星坐标、百度坐标,也可能是某个投影坐标。坐标体系不确认,数据导入 QGIS 或 ArcGIS Pro 后就可能偏移几百米甚至更多。

原理:Scrapy 爬虫怎么读,以及它在 GIS 工作流中的角色

原理:Scrapy 的英文读音可以理解为“斯克瑞皮”,在中文技术交流中常直接读作“Scrapy 框架”。它不是一个 GIS 专用工具,而是一个 Python 网络爬虫框架。GIS 用户使用 Scrapy,通常是为了获得空间分析所需的原始属性数据和坐标数据。

一个标准的 Scrapy GIS 数据采集流程通常包括以下几个环节:

  1. 分析目标网页结构,确认列表页、分页规则和详情页链接。
  2. 在 Spider 中发送请求并解析 HTML 或 JSON。
  3. 提取地点名称、地址、经度、纬度、行政区划等字段。
  4. 在 Pipeline 中清洗字段、去重、检查空值。
  5. 如果坐标不是目标坐标系,进行坐标转换。
  6. 导出 CSV、GeoJSON、Shapefile 或写入 PostGIS。
  7. 在 QGIS、ArcGIS Pro 或 WebGIS 中加载验证。

这里最容易被忽略的是“坐标转换”和“结果验证”。爬虫只负责把数据拿下来,不能自动保证空间位置正确。GIS 工程里更看重的是数据能否落到正确位置、字段是否规范、坐标系是否明确。

步骤:用 Scrapy 采集 GIS POI 数据的实战流程

步骤:下面用一个简化示例说明如何搭建 Scrapy 采集流程。假设目标网页中包含地点名称、地址、经度和纬度字段。真实项目中请遵守网站 robots 协议、数据使用条款和访问频率限制。

1. 安装 Scrapy 并创建项目

pip install scrapy
scrapy startproject gis_poi_spider
cd gis_poi_spider
scrapy genspider poi example.com

创建后,项目中常用目录包括 spiders、items.py、pipelines.py 和 settings.py。GIS 初学者可以先把重点放在 spiders 和 pipelines 两个文件上:前者负责提取网页数据,后者负责清洗和转换。

2. 定义 GIS 数据字段

在 items.py 中定义需要采集的字段。不要只保存名称和坐标,建议同时保存地址、来源链接、坐标类型和采集时间,方便后续排错。

import scrapy

class PoiItem(scrapy.Item):
    name = scrapy.Field()
    address = scrapy.Field()
    lon = scrapy.Field()
    lat = scrapy.Field()
    coord_type = scrapy.Field()
    source_url = scrapy.Field()

3. 编写 Spider 提取网页字段

下面示例演示从页面中提取 POI 信息。实际网页的 CSS 选择器需要根据页面结构调整。如果目标接口返回 JSON,也可以直接解析 response.json()。

import scrapy
from gis_poi_spider.items import PoiItem

class PoiSpider(scrapy.Spider):
    name = "poi"
    allowed_domains = ["example.com"]
    start_urls = ["https://example.com/poi/list"]

    def parse(self, response):
        for row in response.css(".poi-item"):
            item = PoiItem()
            item["name"] = row.css(".name::text").get()
            item["address"] = row.css(".address::text").get()
            item["lon"] = row.css(".lon::text").get()
            item["lat"] = row.css(".lat::text").get()
            item["coord_type"] = "gcj02"
            item["source_url"] = response.url
            yield item

        next_page = response.css("a.next::attr(href)").get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

这里的 coord_type 很重要。如果网页说明坐标为 GCJ-02,就不要在导出时当作 WGS84 使用。很多 WebGIS 底图、国内在线地图和业务系统坐标并不完全一致,必须先确认来源。

4. 在 Pipeline 中清洗字段并转换坐标

下面给出常用的 GCJ-02 转 WGS84 近似代码。它适用于国内常见坐标偏移纠正场景,但请注意:如果源数据不是 GCJ-02,就不能套用这段代码。

import math

PI = math.pi
A = 6378245.0
EE = 0.00669342162296594323

def out_of_china(lon, lat):
    return lon < 72.004 or lon > 137.8347 or lat < 0.8293 or lat > 55.8271

def transform_lat(x, y):
    ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y
    ret += 0.1 * x * y + 0.2 * math.sqrt(abs(x))
    ret += (20.0 * math.sin(6.0 * x * PI) + 20.0 * math.sin(2.0 * x * PI)) * 2.0 / 3.0
    ret += (20.0 * math.sin(y * PI) + 40.0 * math.sin(y / 3.0 * PI)) * 2.0 / 3.0
    ret += (160.0 * math.sin(y / 12.0 * PI) + 320 * math.sin(y * PI / 30.0)) * 2.0 / 3.0
    return ret

def transform_lon(x, y):
    ret = 300.0 + x + 2.0 * y + 0.1 * x * x
    ret += 0.1 * x * y + 0.1 * math.sqrt(abs(x))
    ret += (20.0 * math.sin(6.0 * x * PI) + 20.0 * math.sin(2.0 * x * PI)) * 2.0 / 3.0
    ret += (20.0 * math.sin(x * PI) + 40.0 * math.sin(x / 3.0 * PI)) * 2.0 / 3.0
    ret += (150.0 * math.sin(x / 12.0 * PI) + 300.0 * math.sin(x / 30.0 * PI)) * 2.0 / 3.0
    return ret

def gcj02_to_wgs84(lon, lat):
    if out_of_china(lon, lat):
        return lon, lat

    dlat = transform_lat(lon - 105.0, lat - 35.0)
    dlon = transform_lon(lon - 105.0, lat - 35.0)
    radlat = lat / 180.0 * PI
    magic = math.sin(radlat)
    magic = 1 - EE * magic * magic
    sqrtmagic = math.sqrt(magic)

    dlat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * PI)
    dlon = (dlon * 180.0) / (A / sqrtmagic * math.cos(radlat) * PI)

    mglat = lat + dlat
    mglon = lon + dlon
    return lon * 2 - mglon, lat * 2 - mglat

在 pipelines.py 中调用转换函数,并处理空值、字符串转浮点数、非法坐标等问题。

from itemadapter import ItemAdapter
from .coord_convert import gcj02_to_wgs84

class GisPoiSpiderPipeline:
    def process_item(self, item, spider):
        adapter = ItemAdapter(item)

        name = adapter.get("name")
        lon = adapter.get("lon")
        lat = adapter.get("lat")

        if not name or lon is None or lat is None:
            raise ValueError("缺少名称或坐标字段")

        lon = float(str(lon).strip())
        lat = float(str(lat).strip())

        if not (-180 <= lon <= 180 and -90 <= lat <= 90):
            raise ValueError("经纬度范围异常")

        if adapter.get("coord_type") == "gcj02":
            lon, lat = gcj02_to_wgs84(lon, lat)
            adapter["coord_type"] = "wgs84"

        adapter["lon"] = round(lon, 8)
        adapter["lat"] = round(lat, 8)

        return item

然后在 settings.py 中启用 Pipeline:

ITEM_PIPELINES = {
    "gis_poi_spider.pipelines.GisPoiSpiderPipeline": 300,
}

5. 导出 CSV 并转换为 GeoJSON

Scrapy 可以直接导出 CSV:

scrapy crawl poi -O poi.csv

如果想生成 GeoJSON,可以用 GeoPandas 读取 CSV 并创建点要素。GeoJSON 默认常用 WGS84 经纬度坐标,适合在 QGIS、Leaflet、OpenLayers 中快速验证。

import pandas as pd
import geopandas as gpd

df = pd.read_csv("poi.csv")

gdf = gpd.GeoDataFrame(
    df,
    geometry=gpd.points_from_xy(df["lon"], df["lat"]),
    crs="EPSG:4326"
)

gdf.to_file("poi.geojson", driver="GeoJSON")

6. 在 QGIS 中验证结果

导出 GeoJSON 后,不要急着进入分析。建议先在 QGIS 中做三项检查:

  1. 加载 poi.geojson,确认图层坐标参考系为 EPSG:4326。
  2. 叠加可信底图或已有边界数据,观察点位是否整体偏移。
  3. 抽查 5 到 10 个点,把名称或地址与地图位置对照。

如果点位整体偏移,优先检查坐标类型是否误判;如果只有个别点异常,通常是源网页字段错误、经纬度反了、地址和坐标不匹配,或页面解析时取错字段。

常见坑:Scrapy GIS 数据采集最容易出错的地方

常见坑:Scrapy 能帮你批量采集数据,但不能自动保证 GIS 数据质量。下面这些问题在实际项目中非常常见。

1. 把 GCJ-02 或 BD-09 当成 WGS84

这是最典型的坐标偏移来源。WGS84 是 GPS 和 GeoJSON 常用坐标系;GCJ-02 常见于国内互联网地图;BD-09 常见于百度地图生态。如果坐标体系混用,点位会出现系统性偏移。

2. 经度纬度顺序写反

GeoJSON 坐标顺序是 longitude、latitude,也就是经度在前、纬度在后。很多表格中会写成 lat、lon,写代码时很容易反过来。一旦写反,中国区域的点可能跑到错误位置,甚至无法显示在正常范围内。

3. 只导出属性表,不声明 CRS

CSV 本身没有强制坐标参考系信息。你可以在 CSV 中存 lon 和 lat,但导入 GIS 软件时必须明确它们是什么坐标系。GeoJSON、GeoPackage、PostGIS 表更适合保存空间数据和坐标系信息。

4. 页面结构变动导致字段错位

网页改版后,CSS 选择器可能仍然能抓到文本,但抓到的不是原来的字段。比如原本抓经度的位置变成了联系电话。GIS 数据采集应保留 source_url,并在导出前检查字段类型和范围。

5. 忽略访问频率和合法性

Scrapy 采集速度较快,必须设置合理下载延迟,避免对目标站点造成压力。涉及商业平台、个人信息、敏感数据或受版权限制的数据时,应先确认授权和使用边界。

DOWNLOAD_DELAY = 1
CONCURRENT_REQUESTS = 4
ROBOTSTXT_OBEY = True

方法比较:Scrapy、requests、GeoPandas 和 PostGIS 怎么选

方法比较:GIS 数据采集不是只有 Scrapy 一种做法。选择工具时,应看数据规模、网页复杂度、是否需要空间入库、是否需要定期更新。

方法 适合场景 优点 限制
requests + BeautifulSoup 少量页面、一次性采集 代码简单,上手快 分页、重试、并发和管道能力弱
Scrapy 批量网页采集、分页列表、详情页采集 结构清晰,适合长期维护 需要理解项目结构和选择器
GeoPandas 采集后空间数据整理、导出 GeoJSON 适合 GIS 数据清洗和格式转换 不负责网页采集
PostGIS 大规模空间数据入库和查询 适合空间索引、空间查询、多人协作 需要数据库部署和 SQL 基础

如果你只是采集几十条数据,requests 就够了;如果要采集多页列表、定期更新、字段较多,Scrapy 更合适;如果后续要做缓冲区分析、空间连接、范围查询,可以把结果写入 PostGIS。

检查清单:采集后的 GIS 数据是否可用

检查清单:完成 Scrapy 爬虫后,建议按下面清单逐项检查,而不是只看“程序是否跑完”。

  • 字段完整性:name、address、lon、lat、source_url 是否都有值。
  • 坐标范围:经度是否在 -180 到 180,纬度是否在 -90 到 90。
  • 坐标类型:是否明确 WGS84、GCJ-02、BD-09 或投影坐标。
  • 经纬度顺序:GeoJSON 中是否为 lon、lat。
  • 重复数据:同名同地址或同坐标点是否需要去重。
  • 空间验证:是否在 QGIS 或 ArcGIS Pro 中叠加底图检查。
  • 异常点:是否存在落到海里、国外或行政区外的点。
  • 来源记录:是否保留 source_url、采集时间和坐标来源说明。
  • 使用合规:是否遵守网站条款、授权范围和数据安全要求。

GIS 数据采集的目标不是“爬到数据”,而是“得到可解释、可验证、可复用的空间数据”。坐标系、字段质量和来源记录,往往比爬虫速度更重要。

FAQ:Scrapy 爬虫和 GIS 坐标转换常见问题

FAQ:下面整理几个 GIS 学习者在使用 Scrapy 做数据采集时经常遇到的问题。

Scrapy 爬虫怎么读?

Scrapy 通常可以读作“斯克瑞皮”,也有人直接读英文单词 Scrapy。技术交流中读音不是重点,重点是它是一个 Python 爬虫框架,适合批量采集网页数据。

Scrapy 采集到的坐标可以直接放进 QGIS 吗?

不一定。你必须先确认坐标类型。如果是 WGS84 经纬度,可以按 EPSG:4326 加载;如果是 GCJ-02 或 BD-09,需要先转换或在对应地图环境中使用。否则会出现点位偏移。

GIS 数据采集一定要用 Scrapy 吗?

不一定。少量页面可以用 requests;结构化 API 可以直接请求 JSON;采集后整理空间数据可以用 GeoPandas;需要入库管理可以用 PostGIS。Scrapy 更适合多页面、批量、可维护的网页采集任务。

GCJ-02 转 WGS84 代码是不是绝对精确?

不是。常见转换代码通常是工程近似转换,能解决大多数可视化偏移问题,但不适合高精度测绘、法律边界、工程放样等场景。严肃测绘任务应使用合规数据源和专业坐标转换参数。

为什么我的 GeoJSON 在 WebGIS 中显示位置不对?

常见原因包括坐标类型错误、经纬度顺序写反、数据不是 EPSG:4326、前端地图底图坐标系不同、原始网页坐标字段取错。建议先用 QGIS 打开 GeoJSON 验证,再排查前端代码。

采集网页数据会不会有法律风险?

可能会。采集前应查看网站 robots 协议、服务条款和数据版权说明。不要采集个人隐私、敏感数据或未授权商业数据。项目中应设置合理访问频率,并保留数据来源记录。

结论:把 Scrapy 用成 GIS 数据生产工具,而不是只会跑爬虫

结论:Scrapy爬虫怎么读并不复杂,可以理解为“斯克瑞皮”。对 GIS 读者来说,更关键的是掌握完整的数据采集工作流:用 Scrapy 提取网页 POI,清洗字段,判断坐标类型,必要时进行坐标转换,再导出 GeoJSON 并在 QGIS 中验证。

如果你只记住一条原则,就是:爬虫负责采集,GIS 负责验证空间正确性。凡是带坐标的数据,都要检查坐标系、经纬度顺序、空间偏移和异常点。这样采集到的数据才能真正用于制图、空间分析、WebGIS 展示和后续数据库管理。