Jsoup是一个Java HTML解析器,可以直接解析一个URL地址和HTML文本内容。它提供了一个非常省力的API,可以通过DOM、CSS和类似jQuery的操作方法来获取和操作数据。
最近在做手头的事情,需要一个全国各地的地理数据,从省市到县、镇、乡。各种杜娘和各种谷歌都没有找到完整的数据。最终那些自助的人终于找到了一个比较完整的数据,但是这里的数据只精确到了镇一级,并没有村一级的数据(后来通过分析数据来源才知道为什么,呵呵)。另外,博主提供的一些数据是多余的,对于我这个有强迫症,追求完美的人来说,我想我必须自己爬出这部分数据。
上面博文中的内容相当丰富,博主用php实现。作为2015年编程语言排名第一,我们不能示弱。现在我将带你看看如何用java从网页上抓取我们想要的数据。
第一步:准备(数据源工具):
数据来源(迄今为止最全面、最权威的官方数据):http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/.
数据爬行工具(爬虫工具):http://jsoup.org/.
二、数据源分析:
首先,我在这里不会解释jsoup工具的使用,但是有兴趣的可以自己参考。
在做开发的时候,你应该多了解一些软件工具的使用。当你在正常的开发过程中遇到他们,你就会知道从哪里开始。鼓励大家在紧急情况下多关注身边的一些软件工具。在做这个事情之前,我不知道怎么用jsoup,但是我知道jsoup可以用来做什么。需要用的时候,我会查阅资料,自己学习。
以上数据来源于中华人民共和国国家统计局2013年发布,其准确性和权威性不言而喻。
接下来,我们从主页:开始分析数据源的结构。
通过分析主页源代码,我们可以得到以下三点:
1.页面的整个布局是由table标签控制的,也就是说,如果我们想通过jsoup选择超链接,就必须注意上图中不仅标注了省市,还使用了表格。整个页面的表格很多,不可能直接传递表格。
document connect=connect(' http://www . stats . gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2013/');elements RowProvince=connect . select(' table ');
解析数据。
2.页面的超链接部分有多少位置。或许官方已经考虑过像你这样的程序员需要获取这样数据的原因。页面非常干净,除了下面的记录号是多余的超链接,其他链接都可以直接抓取。
3.省市数据规律性。包含有效信息的表的每一行都有一个provincetr类属性,这非常重要。至于为什么重要,请往下看。每行数据中有多个td标签,每个td标签包含一个A超链接,这正是我们想要的。超链接的文本甚至是省(市等)的名称。).
我们再来看一下通用数据页面(通用数据页面包括三级数据显示页面:市级、县级、镇级):
之所以将以上三页放在一起,是因为通过分析可以发现,这三级数据的数据页是完全一致的,唯一不同的是html源数据表中数据行tr的类属性不一致,分别对应:city tr,countrhe town tr。其他都一样。这样,我们就可以用一个通用的方法来解决这三个页面的数据抓取。
接下来,我们从主页:开始分析数据源的结构。
最后,查看村级数据页面:
在村级数据中,数据格式与上述市县镇不一致,该级代表的数据为最低级别,因此没有A链接,无法抓取上述市县镇的数据。显示此处数据的表行的类是villagetr。除去这两点,每行包含三列数据,第一列是城市代码,第二列是城乡分类(城市、县、镇的数据格式中不存在此项),第三列是城市名称。
掌握了以上几点之后,我们就可以开始编码了。
第三步,编码实现:
导入Java。io。bufferedwriter导入Java。io。文件;导入Java。io。FileWriter导入Java。io。ioexception导入Java。乌提尔。HashMap导入Java。乌提尔。地图;导入组织。js oup。js oup导入组织。js oup。节点。文件;导入组织。js oup。节点。元素;导入组织。js oup。选择。元素;/** * 全国省市县镇村数据爬取* @作者刘少峰* @日期-上午: * @版本.*/public类JsoupTest { private static MapInteger,String cssMap=new HashMapInteger,String();私有静态BufferedWriter BufferedWriter=null;静态{ cssMap.put(,'省份tr ');//省cssMap.put(,' city tr ');//市cssMap.put(,' country tr ');//县cssMap.put(,' towntr ');//镇cssMap.put(,' villa getr ');//村}公共静态void main(String[]args)引发IOexception { int level=;initFile();//获取全国各个省级信息document connect=connect(' http://www。统计数据。gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/');元素行省=连接。选择(' tr .cssmap。get(level));用于(元素省元素:行省)//遍历每一行的省份城市{ Elements select=Currency元素。选择(' a ');对于(元素省:选择)//每一个省份(四川省){ parseNextLevel(省、级);} }关闭流();} private static void initFile(){ try { bufferedWriter=new bufferedWriter(new File writer(new File(' d : city info。txt ')、true));} catch(IOexception e){ e . print stack trace();} }私有静态void closeStream(){ if(bufferedWriter!=null){ try { bufferedwriter。close();} catch(IOexception e){ e . print stack trace();} bufferedWriter=null} }私有静态空解析下一级(元素父元素,整数级)引发IOException { try { thread。sleep();//睡眠一下,否则可能出现各种错误状态码} catch(中断异常e){ e . print stack trace();} Document doc=connect(ParentElement。attr(' ABS : href ');如果(医生!=空){元素新闻标题=文档选择(“tr”CSS map。get(level));////获取表格的一行数据用于(元素元素:新闻健康){打印信息(元素,级别);元素选择=元素。选择(' a ');//在递归调用的时候,这里是判断是否是村一级的数据,村一级的数据没有a标签if (select.size()!=) { parseNextLevel(select.last(),level);} } } } /** * 写一行数据到数据文件中去* @param元素爬取到的数据元素* @参数级别城市级别*/私有静态无效打印信息(元素元素,int级别){尝试{ bufferedwriter。写入(元素。选择(' TD ').最后()。text()' { ' level ' }['元素。选择(' TD ').第一个()。text()']');bufferedwriter。newline();bufferedwriter。flush();} catch(IOexception e){ e . print stack trace();} }私有静态文档连接(字符串URL){ if(URL==null | | URL。isempty()){ 0抛出新的IllegalArgumentException('输入网址(网址)无效!');}请尝试{返回Jsoup.connect(url).超时(*)。get();} catch(IOexception e){ e . print stack trace();返回null} } }数据爬取过程便是一个漫长的过程,只需要慢慢等待吧,呵呵,由于程序运行时间较长,请不要在控制台打印输出,否则可能会影响程序运行.
最终获取到数据的格式如下('{}'中表示城市级别,'[]'中内容表示城市编码):
市辖区{3} [11010000000]东城区{4} [110100000]东华门街道办事处{5} [1101001000]多福巷社区居委会{6} [110101001]银闸社区居委会{6} [110101]。[110101001006]南池子社区居委会{6}[110101001007]黄图岗社区居委会{6}[110101001008]灯市口社区居委会{6}[110101001009]正义路社区居委会{6}[110101001010]。[110101001013]九韶社区居委会{6}[110101001014]王府井社区居委会{ 6 }[11010101001015]景山街道办事处{5}[110101002000]龙佛寺社区居委会{6} [110101002000]。[110101002003]中谷社区居委会{6}[110101002004]贾伟社区居委会{6}[110101002005]王治马社区居委会{6}[110101002006]景山东街社区居委会{6} [110101002000]。交道口街道办事处{5}[110101003000]胶东社区居委会{6}[110101003001]福祥社区居委会{6}[110101003002]大兴社区居委会{ 6 }[11010101003003]付雪社区居委会{6} [111]。居尔社区居委会{6}[110101003008]南锣鼓巷社区居委会{6}[110101003009]安定门街道办事处{ 5 }[11010101004000]焦北头头社区居委会{6}[110101004001]北锣鼓巷社区居委会{6}。
.
得到上面的数据后,就可以为所欲为了,上面的代码可以直接运行。从数据源爬取后,可以直接转换成你想要的格式。