Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 465|回复: 0

[默认分类] Jsoup获取全国地区数据(省市县镇村)

[复制链接]
  • TA的每日心情
    开心
    2021-12-13 21:45
  • 签到天数: 15 天

    [LV.4]偶尔看看III

    发表于 2018-5-10 10:07:46 | 显示全部楼层 |阅读模式

      最近手头在做一些东西,需要一个全国各地的地域数据,从省市区到县镇乡街道的。各种度娘,各种谷歌,都没找到一个完整的数据。最后功夫不负有心人,总算找到一份相对来说比较完整的数据,但是这里的数据也只是精确到镇级别,没有村一级的数据(后来通过分析数据源我知道了为什么,呵呵),在加上博主提供的有些数据存在冗余,对于有强迫症和追求完美的我,心想着我一定要自己动手去把这部分数据给爬取出来。
      上述博文中的内容还算丰富,博主是用的是php来实现的,作为2015年度编程语言排行榜的第一位,我们也不能示弱啊,下面我就带着大家一起来看看用java怎么从网页当中爬取我们想要的数据...
    第一步、准备工作(数据源+工具):
      数据源(截止目前最全面权威的官方数据):http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/
      爬取数据的工具(爬虫工具):http://jsoup.org/
    第二、数据源分析:
      首先jsoup工具的使用我在这里就不做讲解了,感兴趣的可以自己动手去查阅。
      做开发就应该多去了解一些软件工具的使用,在平常开发过程中遇到了才知道从何下手,鼓励大家多平时留意一些身边的软件工具,以备不时之需。在做这个东西以前,我也不知道jsoup要怎么用,但我知道jsoup可以用来干嘛,在我需要的用到的时候,再去查阅资料,自己学习。
       
      上述的数据源是2013年中华人民共和国国家统计局发布的,其准确性和权威性不言而喻。
      接下来我们分析一下数据源的结构,先从首页说起:   
      通过分析首页源码我们可以得到如下3点:
      
      页面的整个布局是用的table标签来控制的,也就是说我们如果要通过jsoup来选择超链接,那么一定要注意,上图中不是只要标注了省市地区的地方采用的才是表格,整个页面中存在多个表格,因此是不可以直接通过表格
       
       
    1. Document connect = connect("http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/");
    2. Elements rowProvince = connect.select("table");
    复制代码

        来解析数据的。  
      页面中有超链接的部分有多少地方。可能是官方考虑到了你们这种程序员需要获取这样的数据的原因吧,页面很干净,除开下方的备案号是多余的超链接,其他的链接可以直接爬取。
      省份城市的数据规律。包含有效信息的表格的每一行都有一个class属性provincetr,这个属性很重要,至于为什么重要,请接着往下看;每一行数据中存在多个td标签,每一个td标签中包含一个a超链接,而这个超链接正是我们想要的超链接,超链接的文本即使省份(直辖市等)的名称。
      
        
      再次我们再看一下一般的数据页面(一般的数据页面包括市级、县级、镇级这三级数据展示页面):
      之所以要把上述三个页面放在一起,是因为通过分析我们可以发现,这三级数据的数据页面完全一致,唯一不同的就是在HTML源码数据表格中的数据行tr的class属性不一致,分别对应为:citytr,countrytrhe towntr。其他均一致。这样我们就可以用一个通用的方法解决这三个页面的数据爬取。  
      
      最后看看村一级的数据页面:   
        在村一级的数据中,和上述市县镇的数据格式不一致,这一级所表示的数据是最低一级的,所以不存在a链接,因此不能采用上面市县镇数据的爬取方式去爬取;这里展示数据的表格行的class为villagetr,除开这两点以外,在每一行数据中包含三列数据,第一列是citycode,第二列是城乡分类(市县镇的数据格式不存在这一项),第三列是城市名称。
        把握了以上各个要点之外,我们就可以开始编码了。
    第三步、编码实现:  

       
      

       
      
      
       
      
    1. import java.io.BufferedWriter;
    2. import java.io.File;
    3. import java.io.FileWriter;
    4. import java.io.IOException;
    5. import java.util.HashMap;
    6. import java.util.Map;
    7. import org.jsoup.Jsoup;
    8. import org.jsoup.nodes.Document;
    9. import org.jsoup.nodes.Element;
    10. import org.jsoup.select.Elements;
    11. /**
    12. * 全国省市县镇村数据爬取
    13. * @author liushaofeng
    14. * @date 2015-10-11 上午12:19:39
    15. * @version 1.0.0
    16. */
    17. public class JsoupTest
    18. {
    19.     private static Map<Integer, String> cssMap = new HashMap<Integer, String>();
    20.     private static BufferedWriter bufferedWriter = null;
    21.     static
    22.     {
    23.         cssMap.put(1, "provincetr");// 省
    24.         cssMap.put(2, "citytr");// 市
    25.         cssMap.put(3, "countytr");// 县
    26.         cssMap.put(4, "towntr");// 镇
    27.         cssMap.put(5, "villagetr");// 村
    28.     }
    29.     public static void main(String[] args) throws IOException
    30.     {
    31.         int level = 1;
    32.         initFile();
    33.         // 获取全国各个省级信息
    34.         Document connect = connect("http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/");
    35.         Elements rowProvince = connect.select("tr." + cssMap.get(level));
    36.         for (Element provinceElement : rowProvince)// 遍历每一行的省份城市
    37.         {
    38.             Elements select = provinceElement.select("a");
    39.             for (Element province : select)// 每一个省份(四川省)
    40.             {
    41.                 parseNextLevel(province, level + 1);
    42.             }
    43.         }
    44.         closeStream();
    45.     }
    46.     private static void initFile()
    47.     {
    48.         try
    49.         {
    50.             bufferedWriter = new BufferedWriter(new FileWriter(new File("d:\\CityInfo.txt"), true));
    51.         } catch (IOException e)
    52.         {
    53.             e.printStackTrace();
    54.         }
    55.     }
    56.     private static void closeStream()
    57.     {
    58.         if (bufferedWriter != null)
    59.         {
    60.             try
    61.             {
    62.                 bufferedWriter.close();
    63.             } catch (IOException e)
    64.             {
    65.                 e.printStackTrace();
    66.             }
    67.             bufferedWriter = null;
    68.         }
    69.     }
    70.     private static void parseNextLevel(Element parentElement, int level) throws IOException
    71.     {
    72.         try
    73.         {
    74.             Thread.sleep(500);//睡眠一下,否则可能出现各种错误状态码
    75.         } catch (InterruptedException e)
    76.         {
    77.             e.printStackTrace();
    78.         }
    79.         Document doc = connect(parentElement.attr("abs:href"));
    80.         if (doc != null)
    81.         {
    82.             Elements newsHeadlines = doc.select("tr." + cssMap.get(level));//
    83.             // 获取表格的一行数据
    84.             for (Element element : newsHeadlines)
    85.             {
    86.                 printInfo(element, level + 1);
    87.                 Elements select = element.select("a");// 在递归调用的时候,这里是判断是否是村一级的数据,村一级的数据没有a标签
    88.                 if (select.size() != 0)
    89.                 {
    90.                     parseNextLevel(select.last(), level + 1);
    91.                 }
    92.             }
    93.         }
    94.     }
    95.     /**
    96.      * 写一行数据到数据文件中去
    97.      * @param element 爬取到的数据元素
    98.      * @param level 城市级别
    99.      */
    100.     private static void printInfo(Element element, int level)
    101.     {
    102.         try
    103.         {
    104.             bufferedWriter.write(element.select("td").last().text() + "{" + level + "}["
    105.                 + element.select("td").first().text() + "]");
    106.             bufferedWriter.newLine();
    107.             bufferedWriter.flush();
    108.         } catch (IOException e)
    109.         {
    110.             e.printStackTrace();
    111.         }
    112.     }
    113.     private static Document connect(String url)
    114.     {
    115.         if (url == null || url.isEmpty())
    116.         {
    117.             throw new IllegalArgumentException("The input url("" + url + "") is invalid!");
    118.         }
    119.         try
    120.         {
    121.             return Jsoup.connect(url).timeout(100 * 1000).get();
    122.         } catch (IOException e)
    123.         {
    124.             e.printStackTrace();
    125.             return null;
    126.         }
    127.     }
    128. }
    复制代码

       
       
      
    1. 数据爬取过程便是一个漫长的过程,只需要慢慢等待吧,呵呵,由于程序运行时间较长,请不要在控制台打印输出,否则可能会影响程序运行....
    复制代码

    最终获取到数据的格式如下("{}"中表示城市级别,"[]"中内容表示城市编码):

    拿到以上数据以后,自己想干什么都可以自我去实现了,以上的代码可以直接运行,从数据源爬取后,可以直接转换成自己所要的格式。
    后续处理的最终结果,请参见博文:http://www.cnblogs.com/liushaofeng89/p/4937714.html
       
    如果你觉得本博文对你有所帮助,请记得点击右下方的"推荐"哦,么么哒...
      转载请注明出处:http://www.cnblogs.com/liushaofeng89/p/4873086.html
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-30 05:44 , Processed in 0.395276 second(s), 47 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表