网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。

如何开始

做一件事情,难得不是做什么,难得是怎么做,怎么开始。良好的开端成功的一半,下面让我们一起头脑风暴一下,这件事情应该怎么做。

总共有31个省,每个省有很多市,每个市有很多县,每个县有很多乡镇,每个乡镇有很多居委会。既然这样,第一反应肯定是循环,遍历。那么接下来,应该怎么循环。先将这个页面的静态页面get到,然后将东西保存并获取链接,再然后循环爬取。

想到了就干,但是写完之后碰到了很多问题。

  1. 文件怎么保存,总共有6级嵌套。
  2. 循环结束才可以保存文件,如果循环中抛异常会导致文件损坏。

关于这两个问题,我的解决方法是:

  1. 文件夹下保存一个省汇总的xls,将省的信息保存下来,包括名称、代号、链接。然后创建每个省的文件夹,在这个文件夹中保存一个市汇总的xls,保存市的名称、代号、链接。以此类推。
  2. 先将省列表爬完,期间不去爬市的信息,得到省的链接。然后再用保存下来的链接去获取每个省的市的信息,期间不去管下一级的信息。一次类推,很大程度上杜绝了文件损坏的发生。

代码分析

在详细研究了各个层次的请求参数之后,其中市县乡的爬取可以提取一个方法。我将请求提取了一个方法。

Get 请求方法

代码:

public String GetMethod(URL url, String encode) {BufferedReader in = null;String ret = null;HttpURLConnection conn = null;try {conn = (HttpURLConnection) url.openConnection();if (conn != null) {conn.setDoOutput(true);conn.setDoInput(true);conn.setRequestMethod("GET");conn.connect();}in = new BufferedReader(new InputStreamReader(conn.getInputStream(), encode));String line = null;while ((line = in.readLine()) != null) {ret += line;}return ret;} catch (Exception e) {e.printStackTrace();return "Error :" + e.getMessage();} finally {try {if (in != null) {in.close();}conn.disconnect();} catch (final IOException ex) {ex.printStackTrace();}}
}

这个方法很简单的 get 请求。但是细心的同学可能注意到了,我给这个方法传了一个 String encode的参数。这就是我遇到的第一个坑,因为网页编码格式是 GB2312 ,而JAVA默认格式是UTF-8,所以没有传这个参数的时候默认获取UTF-8的编码信息,存下来之后发现全部汉字变成了乱码。

获取省份信息

代码:

private static String getProvince() {WebUtils webUtils = new WebUtils();WritableWorkbook book = null;try {File file = new File(parentPath + File.separator + "汇总.xls");if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}book = Workbook.createWorkbook(file);WritableSheet sheet = book.createSheet("省份", 0);WritableFont font = new WritableFont(WritableFont.ARIAL, 12);WritableCellFormat cellFormat = new WritableCellFormat(font);sheet.setColumnView(0, 30);sheet.setColumnView(1, 10);sheet.setColumnView(2, 90);sheet.addCell(new Label(0, 0, "名称", cellFormat));sheet.addCell(new Label(1, 0, "代码", cellFormat));sheet.addCell(new Label(2, 0, "链接", cellFormat));String ret = webUtils.GetMethod(new URL(baseUrl + "index.html"), "GB2312");Document doc = Jsoup.parse(ret);Elements provincetrEles = doc.getElementsByClass("provincetr");int i = 1;for (Element element : provincetrEles) {Elements tdEles = element.getElementsByTag("td");for (Element td : tdEles) {String href = td.select("a").attr("href");String name = td.select("a").text().trim();String areaCode = "";try {areaCode = href.substring(0, href.indexOf("."));} catch (Exception e) {// TODO: handle exception}if (href != "" && areaCode != null && name != "") {sheet.addCell(new Label(0, i, name, cellFormat));sheet.addCell(new Label(1, i, areaCode, cellFormat));sheet.addCell(new Label(2, i, baseUrl + href, cellFormat));System.out.println("-[Province]  " + i + "  SAVED:" + name + " - " + href);i++;}}}return file.getAbsolutePath();} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (RowsExceededException e) {e.printStackTrace();} catch (WriteException e) {e.printStackTrace();} finally {if (book != null) {try {book.write();book.close();System.out.println("Complete".toUpperCase());} catch (WriteException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}return null;
}

这里面包含了自己写的GetMethod 方法,用jxl 来操作xls 表格,用Jsoup来解析get下来的静态页面。值得注意的是,一定要记得释放资源。

获取市县乡

这三个获取大同小异,可以提取一个公共方法。

公共方法

private static void creatXls(String path, String name, String url, String className) {WritableWorkbook book = null;try {File file = new File(path + File.separator + "汇总.xls");if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}book = Workbook.createWorkbook(file);WritableSheet sheet = book.createSheet(name, 0);WritableFont font = new WritableFont(WritableFont.ARIAL, 12);WritableCellFormat cellFormat = new WritableCellFormat(font);sheet.setColumnView(0, 30);sheet.setColumnView(1, 30);sheet.setColumnView(2, 90);sheet.addCell(new Label(0, 0, "名称", cellFormat));sheet.addCell(new Label(1, 0, "统计用区划代码", cellFormat));sheet.addCell(new Label(2, 0, "链接", cellFormat));WebUtils webUtils = new WebUtils();String ret = webUtils.GetMethod(new URL(url), "GB2312");Document doc = Jsoup.parse(ret);Elements trElements = doc.getElementsByClass(className);int i = 1;for (Element tr : trElements) {Elements tdElements = tr.getElementsByTag("td");if (tdElements.size() != 0) {String href = tdElements.first().select("a").attr("href");String areaCode = tdElements.first().select("a").text().trim();String cityName = tdElements.last().select("a").text().trim();if (href != "" && areaCode != null && cityName != "") {sheet.addCell(new Label(0, i, cityName, cellFormat));sheet.addCell(new Label(1, i, areaCode, cellFormat));sheet.addCell(new Label(2, i, url.substring(0, url.lastIndexOf("/") + 1) + href,
cellFormat));System.out.println("  --[" + className + "]  " + i + "  SAVED:" + cityName + " - " +
areaCode+ " - " + href);i++;}}}} catch (Exception e) {e.printStackTrace();} finally {if (book != null) {try {book.write();book.close();} catch (WriteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}

没什么特别的,跟获取省的差不多。其中传入参数参见下表

  • String path 上级爬取之后创建的新文件夹路径
  • String name 这个传不传无所谓,是即将爬的单位名
  • String url 即将爬取的链接
  • String className 静态页面中的class名

获取市

private static String getCity(File file, String code) throws Exception {if (!file.exists()) {throw new Exception("【Error】文件不存在:" + file.getName());}try {String[] nameList = readXLS(file, 0, 1);String[] codeList = readXLS(file, 1, 1);String[] urlList = readXLS(file, 2, 1);if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}if (code != null) {// TODO 单独获取int index = Arrays.binarySearch(codeList, code);if (index < 0) {throw new Exception("省份代码不存在");}String path = file.getParent() + File.separator + nameList[index];creatXls(path, nameList[index], urlList[index], "citytr");return path + File.separator + "汇总.xls";} else {String ret = "";for (int i = 0; i < codeList.length; i++) {if (nameList[i] != null && nameList[i] != "" && nameList[i] != "null") {String path = file.getParent() + File.separator + nameList[i];creatXls(path, nameList[i], urlList[i], "citytr");ret = ret + path + File.separator + "汇总.xls" + SPLITE_SYMBOL;}}return ret;}} catch (Exception e) {e.printStackTrace();}return null;
}

获取县

private static String getCountry(File file, String code) throws Exception {if (!file.exists()) {throw new Exception("【Error】文件不存在:" + file.getAbsolutePath());}try {String[] nameList = readXLS(file, 0, 1);String[] codeList = readXLS(file, 1, 1);String[] urlList = readXLS(file, 2, 1);if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}if (code != null) {// TODO 单独获取int index = Arrays.binarySearch(codeList, code);if (index < 0) {throw new Exception("市代码不存在");}String path = file.getParent() + File.separator + nameList[index];creatXls(path, nameList[index], urlList[index], "countytr");return path + File.separator + "汇总.xls";} else {String ret = "";for (int i = 0; i < codeList.length; i++) {if (nameList[i] != null && nameList[i] != "" && nameList[i] != "null") {String path = file.getParent() + File.separator + nameList[i];creatXls(path, nameList[i], urlList[i], "countytr");ret = ret + path + File.separator + "汇总.xls" + SPLITE_SYMBOL;}}return ret;}} catch (Exception e) {e.printStackTrace();}return null;
}

获取乡镇

private static String getTown(File file, String code) throws Exception {if (!file.exists()) {throw new Exception("【Error】文件不存在:" + file.getName());}if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}try {String[] nameList = readXLS(file, 0, 1);String[] codeList = readXLS(file, 1, 1);String[] urlList = readXLS(file, 2, 1);if (code != null) {// TODO 单独获取int index = Arrays.binarySearch(codeList, code);if (index < 0) {throw new Exception("县代码不存在");}String path = file.getParent() + File.separator + nameList[index];creatXls(path, nameList[index], urlList[index], "towntr");return path + File.separator + "汇总.xls";} else {String ret = "";for (int i = 0; i < codeList.length; i++) {if (nameList[i] != null && nameList[i] != "" && nameList[i] != "null") {String path = file.getParent() + File.separator + nameList[i];creatXls(path, nameList[i], urlList[i], "towntr");ret = ret + path + File.separator + "汇总.xls" + SPLITE_SYMBOL;}}return ret;}} catch (Exception e) {e.printStackTrace();}return null;
}

获取居委会

居委会内容比其他的多点,其他的大差不差,但是还是不能跟上面的提取公共方法,起码我不会。但是为了让单独获取和批量获取复用,我还是提取了一个方法。

公共方法

private static void createVillageXLS(File file, String url) {WritableWorkbook book = null;try {String ret = null;book = Workbook.createWorkbook(file);WritableSheet sheet = book.createSheet(file.getName().replace(".xls", ""), 0);WebUtils webUtils = new WebUtils();ret = webUtils.GetMethod(new URL(url), "GB2312");WritableFont font = new WritableFont(WritableFont.ARIAL, 12);WritableCellFormat cellFormat = new WritableCellFormat(font);sheet.setColumnView(0, 30);sheet.setColumnView(1, 30);sheet.setColumnView(2, 30);sheet.addCell(new Label(0, 0, "名称", cellFormat));sheet.addCell(new Label(1, 0, "统计用区划代码", cellFormat));sheet.addCell(new Label(2, 0, "城乡分类代码", cellFormat));Document doc = Jsoup.parse(ret);Elements trElements = doc.getElementsByClass("villagetr");int i = 1;for (Element tr : trElements) {Elements tdElements = tr.getElementsByTag("td");if (tdElements.size() != 0) {String[] strs = tdElements.text().split(" ");String areaCode = strs[0];String classifiCode = strs[1];String villageName = strs[2];sheet.addCell(new Label(0, i, villageName, cellFormat));sheet.addCell(new Label(1, i, areaCode, cellFormat));sheet.addCell(new Label(2, i, classifiCode, cellFormat));System.out.println(" --[Village] " + i + " SAVED:" + villageName + " - " + areaCode + " - " +
classifiCode);i++;}}} catch (Exception e) {e.printStackTrace();} finally {if (book != null) {try {book.write();book.close();} catch (WriteException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
}

获取居委会

private static void getVillage(File file, String code) throws Exception {if (!file.exists()) {throw new Exception("【Error】文件不存在:" + file.getName());}try {String[] nameList = readXLS(file, 0, 1);String[] codeList = readXLS(file, 1, 1);String[] urlList = readXLS(file, 2, 1);String path = null;if (code != null) {int index = Arrays.binarySearch(codeList, code);if (index < 0) {throw new Exception("县代码不存在");}path = file.getParent() + File.separator + nameList[index];file = new File(path, "村委会汇总.xls");if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}createVillageXLS(file, urlList[index]);} else {for (int i = 0; i < codeList.length; i++) {if (nameList[i] != null && nameList[i] != "" && nameList[i] != "null") {path = file.getParent() + File.separator + nameList[i];file = new File(path, "村委会汇总.xls");if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}createVillageXLS(file, urlList[i]);}}}Thread.sleep(DELAY_TIME);} catch (Exception e) {e.printStackTrace();}
}

其他方法

静态changliang

    private static String baseUrl = "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/";private static final String parentPath = System.getProperty("user.dir") + File.separator + "NBS";private static final int DELAY_TIME = 100;

程序入口

public static void run() {Scanner scanner = new Scanner(System.in);System.out.println("欢迎使用");String code = null;while (true) {System.out.println("请选择爬取方式:");System.out.println("手动选择爬取  --  1");System.out.println("自动全部爬取  --  2");System.out.println("请输入编码:(只输入数字)");code = scanner.nextLine().trim();if (code.matches("\\d")) {if (code.equals("1") || code.equals("2")) {break;} else {System.out.println("只能输入1或者2,请重新输入");continue;}} else {System.out.println("输入错误,请重新输入");}}chooseType(code, scanner);System.out.println("获取完毕,按Enter关闭此软件");scanner.nextLine();scanner.close();
}

方式选择

private static void chooseType(String i, Scanner scanner) {switch (i) {case "1":try {// System.out.println("请按 Enter 继续...");// scanner.nextLine();// 获取省String provinceSum = getProvince();if (provinceSum == null || provinceSum == "") {System.out.println("加载失败");return;}// 获取市File file = new File(provinceSum);String code = topLine(file, scanner);String citySum = getCity(file, code);if (citySum == null || citySum == "") {System.out.println("加载失败");return;}// 获取县file = new File(citySum);code = topLine(file, scanner);String countrySum = getCountry(file, code);if (countrySum == null || countrySum == "") {System.out.println("加载失败");return;}// 获取乡镇file = new File(countrySum);code = topLine(file, scanner);String townSum = getTown(file, code);if (townSum == null || townSum == "") {System.out.println("加载失败");return;}// 获取村委会file = new File(townSum);code = topLine(file, scanner);getVillage(file, code);} catch (Exception e) {e.printStackTrace();}break;case "2":try {for (int j = 0; j < 3; j++) {if (j > 0) {System.out.println("请再次确认" + (3 - j) + "遍");}System.out.println("****************************");System.out.println("***********请注意************");System.out.println("*******将会全部批量爬取*******");System.out.println("********会消耗大量时间********");System.out.println("****爬取过程中会有可能封ip*****");System.out.println("******所以有可能中途失败*******");System.out.println("*****如果中途停止,请等待******");System.out.println("****************************");System.out.println("*****如果你已了解相关危险******");System.out.println("********请按Enter继续********");System.out.println("*****************************");scanner.nextLine();}// 获取省String provinceSum = getProvince();if (provinceSum == null || provinceSum == "") {System.out.println("加载失败");return;}File file = new File(provinceSum);String[] citySums = getCity(file, null).split(SPLITE_SYMBOL);for (String city : citySums) {System.out.println("************CITY***************");file = new File(city);String[] countrySums = getCountry(file, null).split(SPLITE_SYMBOL);for (String country : countrySums) {System.out.println("************COUNTRY***************");file = new File(country);String[] townSums = getTown(file, null).split(SPLITE_SYMBOL);for (String town : townSums) {System.out.println("************TOWN***************");file = new File(town);getVillage(file, null);}}}} catch (Exception e) {e.printStackTrace();}break;}
}

选择头

private static String topLine(File file, Scanner scanner) throws Exception {String[] nameList = readXLS(file, 0, 1);String[] codeList = readXLS(file, 1, 1);String code = null;while (true) {for (int i = 0; i < codeList.length; i++) {System.out.println(nameList[i] + "  --  " + i);}System.out.println("请输入编码:(只输入数字)");code = scanner.nextLine();if (code.trim().matches("\\d+")) {return codeList[Integer.parseInt(code)];} else {System.out.println("输入错误,请重新输入:");}}
}

读取xls 文件

private static String[] readXLS(File file, int lieStart, int hangStart) throws Exception {String[] result;try {FileInputStream fis = new FileInputStream(file);Workbook wb = Workbook.getWorkbook(fis);Sheet sheet = wb.getSheet(0);int hangEnd = sheet.getRows();result = new String[hangEnd - hangStart + 1];for (int i = hangStart; i < hangEnd; i++) {result[i] = sheet.getCell(lieStart, i).getContents();}fis.close();wb.close();return result;} catch (Exception e) {e.printStackTrace();throw new Exception("【异常】" + e.getMessage());}
}

如何结束

本来到这里已经结束了,但是我这个人呢,就是想搞一些有意思的。如果我想在一台没装JDK的windows电脑上运行,可不可行?经过度娘畅游之后,还是被我发现了一些方法。这里只是顺带提一下,有机会以后再讲,或者各位看官也可以自己去找一下。下面我粗略的说一下方法:

这些代码我是用Vscode写的(别问为什么不用那么多的IDEA,因为我主力是安卓并且不喜欢装软件),Maven项目。但是呢,我又不会用这个导出为可执行jar文件。所以搬出了老大哥Eclipse,用Eclipse导出为可执行Jar文件。

但是如果电脑没有装JDK,那不是费了吗。jar文件执行不了。所有我又找到了一个软件 exe4j 可以将Jar文件转为exe文件。这样就完美了,转exe的时候将JRE放进去。这样就可以满足要求了。


废话时间
这玩意儿虽说简单,我再下班时间写,还写了两天。学艺不精啊,还需要多磨练。
加油吧,少年


需要源码可以私信联系我

【Howe学爬虫】全国统计用区划代码爬取相关推荐

  1. 获取全国统计用区划代码和城乡划分代码并写入数据库

    背景:业务需要全国省市区的划分以及3级级联,正好想起2018年曾经抓取过国家统计局网站的去全国统计用区划代码和城乡划分代码,原资源的地址:2018年全国统计用区划代码和城乡划分代码.sql-MySQL ...

  2. 城市筛选数据(根据2020年度全国统计用区划代码和城乡划分代码更新维护的标准)

    根据2020年度全国统计用区划代码和城乡划分代码更新维护的标准,整理的城市联动筛选数据: /* 根据2020年度全国统计用区划代码和城乡划分代码更新维护的标准 */ var cityList = [{ ...

  3. 国家统计局统计用区划代码抓取记

    本来只是考虑在程序中建一个省地县的树状选取构件,想从网上找一个地名库,结果不是要积分就是要会员.看到网上有介绍利用国家统计局统计用区划代码抓取的,心想自己也可以抓取试试,结果在这个歧路上越走越远,用了 ...

  4. python游戏辅助lol_Python爬虫实战,60行代码爬取英雄联盟全英雄全皮肤,找寻曾今那些被删除的绝版皮肤...

    学了一周多的爬虫课后终于按捺不住了,小编决定自己手动编写爬虫程序,刚好LJ在鼓励学员分享成果,优秀作品有奖励,就把自己用Python编程爬取各大游戏高清壁纸的过程整理了出来进行投稿,与大家一起分享. ...

  5. 为什么要学爬虫---王者荣耀皮肤图片的爬取

    一. 爬虫是什么? 网络爬虫是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本. 简单的说:就是用事先写好的程序去抓取网络上所需的数据.编写网络爬虫的程序员叫做爬虫工程师. . 二. 爬虫的四个 ...

  6. Python爬虫练习五:爬取 2017年统计用区划代码和城乡划分代码(附代码与全部数据)

    本文仅供学习,需要数据的文末有链接下载,请不要重复爬取. 最近工作中,因为统计用区划代码和城乡划分代码更新了最新的2017版,需要爬取最新的数据.于是乎,本次花了一定精力,将整个2017版数据完完整整 ...

  7. python爬虫练习五(补充): 2018年统计用区划代码和城乡划分代码(附代码与全部数据)

    之前爬取过2017年的数据 详见 Python爬虫练习五:爬取 2017年统计用区划代码和城乡划分代码(附代码与全部数据) ,下面有评论说广东省的数据缺少了东莞与中山两个市的数据,检查网页结构发现确实 ...

  8. [数据][json格式] 2016年统计用区划代码和城乡划分代码

    [数据][json格式] 2016年统计用区划代码和城乡划分代码 2013 年的时候写过一篇 [数据][xml格式] 2012年统计用区划代码和城乡划分代码. 到了今天,我需要某省的省市县乡村五级数据 ...

  9. Python获取[2016年统计用区划代码和城乡划分代码(截止2016年07月31日)]

    #!usr/bin/env python #-*- coding:utf-8 -*- import requests import re import time ##系统初始化 urlHeader=& ...

最新文章

  1. boost asio io_service学习笔记
  2. Tracer cannot set value trace for type None. Supported types are tensor, tensor list, and tuple
  3. 飞鹤、蒙牛、伊利、澳优、合生元、贝因美,谁是未来国产奶粉的扛把子?
  4. Java EE的三层架构
  5. Oracle入门(十四.10)之显式游标简介
  6. CSDN下载码如何使用以及免积分下载
  7. DNS域名解析服务介绍
  8. muma很可能在陪你玩游戏
  9. httpclient3 自动登陆淘宝, 开心网
  10. word里的图片用计算机画图,word绘图教程:图形工具介绍和使用方法-word技巧-电脑技巧收藏家...
  11. java拆弹,CSAPP lab2 二进制拆弹 binary bombs phase_2
  12. Zabbix整合ELK实现日志数据的分析实时监控
  13. 【解决方案】logging: 中文log乱码
  14. 办公利器!用Python批量识别发票并录入到Excel表格
  15. windows测试linux端口,windows、Linux 测试服务器、电脑的某些个端口是否打开
  16. 致我们终将逝去的高考
  17. 【论文整理1】On the Continuity of Rotation Representations in Neural Networks
  18. python手持弹幕LED滚动字幕
  19. Cisdem Unarchiver for Mac(快速解压缩文件)
  20. 软件项目文档规划的大道至简、小道至繁原则

热门文章

  1. 红猫linux系统下载教程,RedCat_NSS_红猫linux软路由安装使用手册.doc
  2. vue+elementUI实现的日期选择组件
  3. Object常用的几种方法
  4. c语言qsort函数的应用
  5. 使用Qt打造属于自己的串口调试助手
  6. 优雅安装电脑或手机实用软件与分享(持续更新)
  7. 智慧养老政策建议及智慧养老发展过程中存在问题
  8. 洛谷P1486 [NOI2004] 郁闷的出纳员 题解
  9. 百度云无法在网页上调用客户端进行下载文件(已安装最新版)
  10. StarUML——一款在MAC上的UML软件