npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

region-data

v1.2.3

Published

爬取省市区数据,生成多种格式的文件

Downloads

70

Readme

说明

中国行政区划数据(不包含港澳台),包含省市区三级数据。

区划代码省市区仅包含 6 位,镇则包含 9 位。

本库区划数据保证都有三级,可能是“省>市>区”、“省>市>镇”、“直辖市>直辖区>区”或者“直辖市>县>具体县”.

最新爬取时间:2024-03-18

安装

npm install region-data --save

使用

// ESM
import { getRegionData } from 'region-data'

// CommonJS
const { getRegionData } = require('region-data')

// 获取数据
const regionData = getRegionData()

API

getProvinces()

获取省份数据。

getCities()

获取省份+城市数据。

getCounties()

获取省份+城市+县数据。

getRegionData()

同 getCounties()。

mapProvinces

省份code和name的映射对象。

mapCities

城市code和name的映射对象。

mapCounties

县code和name的映射对象。

mapAllArea

省份+城市+县code和name的映射对象。

业务流程


开启进程 ──> 交互式配置 ──> 爬取数据/读取缓存 ──> 写入文件 ──> (结束)
                            │
                            v
                  ┌──> 爬取年份>省>市>区>镇>村
          重试3次  │         │
                  │         v
                  └───Y─<网络错误>─N──────>(结束)

目录结构


region-data
  ├─ dist  // 生成的区划数据目录,npm会发布此目录
  │   ├─ region.cjs  // CommonJS规范默认导入的文件
  │   └─ region.mjs  // ESM规范默认导入的文件
  │
  ├─ src  // 核心源码目录
  │   ├─ channels  // 渠道目录,主要有国家统计局和民政部,您可以选择不同的渠道爬取数据
  │   │     ├─ mca.js  // 民政局渠道
  │   │     └─ stats.js  // 国家统计局渠道
  │   │
  │   ├─ spiders  // 爬虫目录,各渠道的爬虫程序都在这个目录
  │   │     ├─ city.js  // 市级爬虫程序
  │   │     ├─ county.js  // 县级爬虫程序
  │   │     ├─ mca.js  // 民政部爬虫程序
  │   │     ├─ province.js  // 省级爬虫程序
  │   │     ├─ town.js  // 镇级爬虫程序
  │   │     ├─ village.js  // 乡级爬虫程序
  │   │     └─ year.js  // 年份爬虫程序
  │   │
  │   ├─ templates  // 模板文件
  │   │     └─ region.ejs  // region.cjs和region.mjs文件的模板,用来填充省市区数据以及区分不同模块的导出
  │   │
  │   ├─ config.js  // 全局的通用配置文件
  │   ├─ configure.js  // 命令行交互配置
  │   ├─ const.js  // 常量文件
  │   ├─ crawling.js  // 爬虫主文件和入口文件,负责调用不同的渠道爬取数据、缓存数据、格式化数据
  │   ├─ CustomError.js  // 自定义错误对象
  │   ├─ log.js  // 处理错误信息的打印
  │   ├─ request.js  // 对fetch的简单封装,为其添加超时中断以及重试功能
  │   ├─ start.js  // node进程主要调用的文件,控制整个进程的生命周期流程
  │   └─ writeFile.js  // 将爬取到的数据写入文件,可以写成多种格式的json文件和js文件
  │
  ├─ temp  // 爬取的原始数据,生成文件时可以复用这些数据,避免重复发起请求
  │   └─ region.json  // 国际统计局省市区三级数据,包括名称、编码和页面URL
  │
  └─ index.js  // node进程入口文件,里面主要调用start.js运行程序

数据来源

特殊区划说明

  • 直辖市和省一个级别,其本身又是一个市,国家统计局展示的其下一级名称为“市辖区”,转换时需将“市辖区”转换为直辖市的名称。
  • 另外,重庆市下面除了有“市辖区”,还有一个“县”,就不进行转换了,让“县”作为二级。
  • “东莞市、中山市、儋州市、嘉峪关市”下面没有区,直接就到镇,需将镇转换为三级,往下依次上推。
  • 而“嘉峪关市”又很特殊,国家统计局网站中此市下面是“市辖区”,获取数据时还需要往下一级。
  • 在国家统计局网站中,“雄安新区”是没有下一级的,而实际上,“容城县”、“安新县”、“雄县”都已归“雄安新区”管理,即使从区划代码上看是属于“保定市”;因此本库中暂不包含“雄安新区”,“容城县”、“安新县”、“雄县”仍归于“保定市”下面。

配置

网络配置

  • 超时时间(10000)
  • 重试次数(3)

数据配置

  • 数据来源(国家统计局、民政局)
  • 年份(最新年份、具体年份)
  • 区划级别(省、市、区、镇、村委会)

国家统计局区划和城乡划分页面 html 源码规则

年份

  • 页面链接http://www.stats.gov.cn/sj/tjbz/qhdm/

  • 年份、url

    // 浏览器控制台
    $$('.list-content li a:first-child').map((anchor) => {
      return {
        name: anchor.textContent.trim(),
        url: anchor.href,
      }
    })
    
    // 服务器 cheerio
    $('.list-content li a:first-child')
      .toArray()
      .map((anchor) => {
        return {
          name: $(anchor).text().trim(),
          url: $(anchor).attr('href'),
        }
      })

省、自治区

  • 页面链接new URL(<年份url>, <年份页面url>).href

  • 区划代码、名称、url

    // 浏览器控制台
    $$('.provincetr a').map((anchor) => ({
      code: '',
      name: anchor.textContent.trim(),
      url: anchor.href,
    }))
    
    // 服务器 cheerio
    $('.provincetr a')
      .toArray()
      .map((anchor) => ({
        code: '',
        name: $(anchor).text().trim(),
        url: $(anchor).attr('href'),
      }))

市、自治州

  • 页面链接new URL(<省url>, <年份url>).href

  • 区划代码、名称、url

    // 浏览器控制台
    $$('.citytr').map((el) => {
      const anchors = el.querySelectorAll('a')
      return {
        code: anchors[0].textContent.trim(),
        url: anchors[0].href,
        name: anchors[1].textContent.trim(),
      }
    })
    
    // 服务器 cheerio
    $('.citytr')
      .toArray()
      .map((el) => {
        const anchors = $(el).find('a')
        return {
          code: anchors.eq(0).text().trim(),
          url: anchors.eq(0).attr('href'),
          name: anchors.eq(1).text().trim(),
        }
      })

区、县

  • 页面链接new URL(<市url>, <省url>).href

  • 区划代码、名称、url

    // 浏览器控制台
    $$('.countytr, .towntr')
      .map((el) => {
        const anchors = el.querySelectorAll('a')
        // 市辖区
        if (anchors.length === 0) {
          return false
        }
        return {
          code: anchors[0].textContent.trim(),
          name: anchors[1].textContent.trim(),
          url: anchors[1].href,
        }
      })
      .filter(Boolean)
    
    // 服务器 cheerio
    $('.countytr, .towntr')
      .toArray()
      .map((el) => {
        const anchors = $(el).find('a')
        // 市辖区
        if (anchors.length === 0) {
          return false
        }
        return {
          code: anchors.eq(0).text().trim(),
          name: anchors.eq(1).text().trim(),
          url: anchors.eq(1).attr('href'),
        }
      })
      .filter(Boolean)

镇、街道、乡

  • 页面链接new URL(<区url>, <市url>).href

  • 区划代码、名称、url

    // 浏览器控制台
    $$('.towntr, .villagetr').map((el) => {
      const anchors = el.querySelectorAll('a')
    
      if (anchors.length !== 0) {
        return {
          code: anchors[0].textContent.trim(),
          name: anchors[1].textContent.trim(),
          url: anchors[1].href,
        }
      }
    
      const td = el.querySelectorAll('td')
      return {
        code: td[0].textContent.trim(),
        name: td[2].textContent.trim(),
      }
    })
    
    // 服务器 cheerio
    $('.towntr, .villagetr')
      .toArray()
      .map((el) => {
        const anchors = $(el).find('a')
        if (anchors.length !== 0) {
          return {
            code: anchors.eq(0).text().trim(),
            name: anchors.eq(1).text().trim(),
            url: anchors.eq(1).attr('href'),
          }
        }
    
        const td = $(el).find('td')
        return {
          code: td.eq(0).text().trim(),
          name: td.eq(2).text().trim(),
        }
      })

村委会、居委会、社区

  • 页面链接new URL(<镇url>, <区url>).href

  • 区划代码、名称、url

    // 浏览器控制台
    $$('.villagetr').map((el) => {
      const td = el.querySelectorAll('td')
      return {
        code: td[0].textContent.trim(),
        name: td[2].textContent.trim(),
      }
    })
    
    // 服务器 cheerio
    $('.villagetr')
      .toArray()
      .map((el) => {
        const td = $(el).find('td')
        return {
          code: td.eq(0).text().trim(),
          name: td.eq(2).text().trim(),
        }
      })

民政局行政区划代码页面 html 源码规则

  • 页面链接https://www.mca.gov.cn/mzsj/xzqh/2022/202201xzqh.html

  • 区划代码、名称

    // 浏览器控制台
    $$('table tr')
      .map((el) => {
        const td = el.querySelectorAll('td')
        return {
          code: td[1]?.textContent.trim() || '',
          name: td[2]?.textContent.trim() || '',
        }
      })
      .filter((item) => /^\d{6}$/.test(item.code))
    
    // 服务器 cheerio
    $('table tr')
      .toArray()
      .map((el) => {
        const td = $(el).find('td')
        return {
          code: td.eq(1).text().trim() || '',
          name: td.eq(2).text().trim() || '',
        }
      })
      .filter((item) => /^\d{6}$/.test(item.code))

错误类型

  • 请求终止 (AbortError)
  • fetch 配置问题 (TypeError)
  • 网络问题 (TypeError)
  • 非 200 响应 (自定义错误事件)
  • 请求超时 (自定义错误事件)
  • 爬取的网站结构变化 (自定义错误事件)

生成的文件类型和结构

  • nested.json:省市区嵌套的多层结构。

    [
      {
        "name": "北京市",
        "code": 110000,
        "children": [
          {
            "name": "北京市",
            "code": 110100,
            "children": [
              {
                "name": "东城区",
                "code": 110101
              }
            ]
          }
        ]
      }
    ]
  • nested-abbr.json:结构与nested.json相同,但是将对象的键名缩写为一个字符,iitems

    [
      {
        "n": "北京市",
        "c": 110000,
        "i": [
          {
            "n": "北京市",
            "c": 110100,
            "i": [
              {
                "n": "东城区",
                "i": 110101
              }
            ]
          }
        ]
      }
    ]
  • flat.json:省市区平铺的一层结构。

    [
      {
        "name": "北京市",
        "code": 110000
      },
      {
        "name": "北京市",
        "code": 110100
      },
      {
        "name": "东城区",
        "code": 110101
      }
    ]
  • flat-abbr.json:结构同flat.json,但是将对象的键名缩写为一个字符。

    [
      {
        "n": "北京市",
        "c": 110000
      },
      {
        "n": "北京市",
        "c": 110100
      },
      {
        "n": "东城区",
        "c": 110101
      }
    ]
  • map.json:区划代码作为键,名称作为值的对象结构。

    {
      "110000": "北京市",
      "110100": "北京市",
      "110101": "东城区"
    }
  • hierarchic.json:省市区分别处于不同的对象中。

    {
      "provinces": {
        "110000": "北京市",
        "120000": "天津市",
        "130000": "河北省"
      },
      "cities": {
        "110100": "北京市",
        "120100": "天津市",
        "130100": "石家庄市"
      },
      "counties": {
        "110101": "东城区",
        "110102": "西城区",
        "110105": "朝阳区"
      }
    }
  • region.js:原始的数据结构同hierarchic.json,其内部提供getRegionData函数会将数据转换为nested.json的结构;这是一种用时间换空间的做法,函数内部缓存了转换后的数据。

    // ESM
    import { getRegionData } from 'region-data'
    
    // CommonJS
    const { getRegionData } = require('region-data')
    
    // 获取数据
    const regionData = getRegionData()

省市区区划代码库 region-data 的开发过程

省市区区划代码库 region-data 的开发过程