|
整理了一些Java方面的架构、面试资料(微服务、集群、分布式、中间件等),有需要的小伙伴可以关注公众号【程序员内点事】,无套路自行领取 本文作者:程序员内点事
更多精选
- 技术部突然宣布:JAVA开发人员全部要会接口自动化测试框架
- 3万字总结,Mysql优化之精髓
写在前边
受疫情影响一直在家远程办公,公司业务进展的缓慢,老实讲活并没有那么多,每天吃饭、睡觉、逛技术社区、写博客,摸鱼摸得爽的很。早上本来还想在来个回笼觉,突然部门经理的语音消息就过来了,甩给我一个连接地址 http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018/,要我把全国的省市名称和区域代码弄出来,建一个字典表,时限一上午。

分下一下需求
要全国的省、市名称,建一张字典表进行存储,表结构设计相对容易,那么城市数据该怎么搞?
有两种解决办法:
- 辛苦点复制粘贴,说多了也就几百个而已
- 写个爬虫工具,一劳永逸
但作为一个程序员没有什么是不能用程序解决的,虽然工作Ctrl+C 、 Ctrl+V用的不少,像这种没有技术含量的复制粘贴还是挺丢面子的。
爬虫搞起
基于这个需求只想要城市名称,爬虫工具选的是Jsoup,Jsoup是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
Jsoup是根据HTML页面的<body>、<td>、<tr>等标签来获取文本内容的,所以先分析一下目标页面结构。打开F12查看页面结构发现,我们要的目标数据在第5个<tbody>标签 class 属性为provincetr 的 <tr> 标签里。

省份名称内容的页面结构如下:
<tr class=&#34;provincetr&#34;>
<td>
<a href=&#34;11.html&#34;>北京市<br></a>
</td>
<td>
<a href=&#34;12.html&#34;>天津市<br>
</td>
.........
</tr>
再拿到<td>标签中<a>标签属性就可以了,省份名称找到了,再看看省对应的城市名在哪里,属性href=&#34;11.html&#34; 就是省份下对应的城市页面Url http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018/11.html
找城市名称跟上边分析过程一样,这里就不再赘述了,既然数据位置已经确定了,那么就开始具体的爬取工作了。

爬虫实现
1、引入Jsoup依赖
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.7.3</version>
</dependency>
2、代码编写
代码的实现比较简单就两个方法而已,没有什么难度主要是得细心,捋清页面标签的嵌套结构就可以了。按照之前分析的逻辑一步一步走
/**
* @author xin
* @description 解析省份
* @date 2019/11/4 19:24
*/
public static void parseProvinceName(Map<String, Map<String, String>> map, String url) throws IOException {
/**
* 获取页面文档数据
*/
Document doc = Jsoup.connect(url).get();
/**
* 获取页面上所有的tbody标签
*/
Elements elements = doc.getElementsByTag(&#34;tbody&#34;);
/**
* 拿到第五个tbody标签
*/
Element element = elements.get(4);
/**
* 拿到tbody标签下所有的子标签
*/
Elements childrens = element.children();
/**
* 当前页面的URL
*/
String baseUri = element.baseUri();
for (Element element1 : childrens) {
Elements provincetrs = element1.getElementsByClass(&#34;provincetr&#34;);
for (Element provincetr : provincetrs) {
Elements tds = provincetr.getElementsByTag(&#34;td&#34;);
for (Element td : tds) {
String provinceName = td.getElementsByTag(&#34;a&#34;).text();
String href = td.getElementsByTag(&#34;a&#34;).attr(&#34;href&#34;);
System.out.println(provinceName + &#34; &#34; + baseUri + &#34;/&#34; + href);
map.put(provinceName, null);
/**
* 组装城市页面的URL,进入城市页面爬城市名称
*/
parseCityName(map, baseUri + &#34;/&#34; + href, provinceName);
}
}
}
}
在抓取城市名称的时候有一点要注意,直辖市城市的省份和城市名称是一样的
/**
* @author xin
* @description 解析城市名称
* @date 2019/11/4 19:26
*/
public static void parseCityName(Map<String, Map<String, String>> map, String url, String provinceName) throws IOException {
Document doc = Jsoup.connect(url).get();
Elements elements = doc.getElementsByTag(&#34;tbody&#34;);
Element element = elements.get(4);
Elements childrens = element.children();
/**
*
*/
String baseUri = element.baseUri();
Map<String, String> cityMap = new HashMap<>();
for (Element element1 : childrens) {
Elements citytrs = element1.getElementsByClass(&#34;citytr&#34;);
for (Element cityTag : citytrs) {
Elements tds = cityTag.getElementsByTag(&#34;td&#34;);
/**
* 直辖市,城市名就是本身
*/
String cityName = tds.get(1).getElementsByTag(&#34;a&#34;).text();
if (cityName.equals(&#34;市辖区&#34;)) {
cityName = provinceName;
}
String href1 = tds.get(1).getElementsByTag(&#34;a&#34;).attr(&#34;href&#34;);
System.out.println(cityName + &#34; &#34; + href1);
cityMap.put(cityName, href1);
}
}
map.put(provinceName, cityMap);
}
public class test2 {
public static void main(String[] args) throws IOException {
Map<String, Map<String, String>> map = new HashMap<>();
parseProvinceName(map, &#34;http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018&#34;);
System.out.println(JSON.toJSONString(map));
}
}
3、输出JSON字符串
当前只要省份和城市名称,爬虫没有什么深度,如果还需要区县等信息,可以根据市后边的url 35/3508.html 继续往下爬取
{
&#34;福建省&#34;: {
&#34;龙岩市&#34;: &#34;35/3508.html&#34;,
&#34;南平市&#34;: &#34;35/3507.html&#34;,
&#34;莆田市&#34;: &#34;35/3503.html&#34;,
&#34;福州市&#34;: &#34;35/3501.html&#34;,
&#34;泉州市&#34;: &#34;35/3505.html&#34;,
&#34;漳州市&#34;: &#34;35/3506.html&#34;,
&#34;厦门市&#34;: &#34;35/3502.html&#34;,
&#34;三明市&#34;: &#34;35/3504.html&#34;,
&#34;宁德市&#34;: &#34;35/3509.html&#34;
},
&#34;西藏自治区&#34;: {
&#34;拉萨市&#34;: &#34;54/5401.html&#34;,
&#34;昌都市&#34;: &#34;54/5403.html&#34;,
&#34;日喀则市&#34;: &#34;54/5402.html&#34;,
&#34;那曲市&#34;: &#34;54/5406.html&#34;,
&#34;林芝市&#34;: &#34;54/5404.html&#34;,
&#34;山南市&#34;: &#34;54/5405.html&#34;,
&#34;阿里地区&#34;: &#34;54/5425.html&#34;
},
&#34;贵州省&#34;: {
&#34;贵阳市&#34;: &#34;52/5201.html&#34;,
&#34;毕节市&#34;: &#34;52/5205.html&#34;,
&#34;铜仁市&#34;: &#34;52/5206.html&#34;,
&#34;六盘水市&#34;: &#34;52/5202.html&#34;,
&#34;遵义市&#34;: &#34;52/5203.html&#34;,
&#34;黔西南布依族苗族自治州&#34;: &#34;52/5223.html&#34;,
&#34;安顺市&#34;: &#34;52/5204.html&#34;,
&#34;黔东南苗族侗族自治州&#34;: &#34;52/5226.html&#34;,
&#34;黔南布依族苗族自治州&#34;: &#34;52/5227.html&#34;
},
&#34;上海市&#34;: {
&#34;上海市&#34;: &#34;31/3101.html&#34;
},
&#34;湖北省&#34;: {
&#34;黄冈市&#34;: &#34;42/4211.html&#34;,
&#34;孝感市&#34;: &#34;42/4209.html&#34;,
&#34;恩施土家族苗族自治州&#34;: &#34;42/4228.html&#34;,
&#34;省直辖县级行政区划&#34;: &#34;42/4290.html&#34;,
&#34;襄阳市&#34;: &#34;42/4206.html&#34;,
&#34;鄂州市&#34;: &#34;42/4207.html&#34;,
&#34;十堰市&#34;: &#34;42/4203.html&#34;,
&#34;咸宁市&#34;: &#34;42/4212.html&#34;,
&#34;黄石市&#34;: &#34;42/4202.html&#34;,
&#34;荆州市&#34;: &#34;42/4210.html&#34;,
&#34;随州市&#34;: &#34;42/4213.html&#34;,
&#34;宜昌市&#34;: &#34;42/4205.html&#34;,
&#34;武汉市&#34;: &#34;42/4201.html&#34;,
&#34;荆门市&#34;: &#34;42/4208.html&#34;
},
&#34;湖南省&#34;: {
&#34;湘潭市&#34;: &#34;43/4303.html&#34;,
&#34;衡阳市&#34;: &#34;43/4304.html&#34;,
&#34;张家界市&#34;: &#34;43/4308.html&#34;,
&#34;益阳市&#34;: &#34;43/4309.html&#34;,
&#34;岳阳市&#34;: &#34;43/4306.html&#34;,
&#34;娄底市&#34;: &#34;43/4313.html&#34;,
&#34;株洲市&#34;: &#34;43/4302.html&#34;,
&#34;常德市&#34;: &#34;43/4307.html&#34;,
&#34;湘西土家族苗族自治州&#34;: &#34;43/4331.html&#34;,
&#34;郴州市&#34;: &#34;43/4310.html&#34;,
&#34;邵阳市&#34;: &#34;43/4305.html&#34;,
&#34;长沙市&#34;: &#34;43/4301.html&#34;,
&#34;永州市&#34;: &#34;43/4311.html&#34;,
&#34;怀化市&#34;: &#34;43/4312.html&#34;
},
&#34;广东省&#34;: {
&#34;河源市&#34;: &#34;44/4416.html&#34;,
&#34;韶关市&#34;: &#34;44/4402.html&#34;,
&#34;茂名市&#34;: &#34;44/4409.html&#34;,
&#34;汕头市&#34;: &#34;44/4405.html&#34;,
&#34;清远市&#34;: &#34;44/4418.html&#34;,
&#34;深圳市&#34;: &#34;44/4403.html&#34;,
&#34;珠海市&#34;: &#34;44/4404.html&#34;,
&#34;广州市&#34;: &#34;44/4401.html&#34;,
&#34;肇庆市&#34;: &#34;44/4412.html&#34;,
&#34;中山市&#34;: &#34;44/4420.html&#34;,
&#34;江门市&#34;: &#34;44/4407.html&#34;,
&#34;云浮市&#34;: &#34;44/4453.html&#34;,
&#34;惠州市&#34;: &#34;44/4413.html&#34;,
&#34;湛江市&#34;: &#34;44/4408.html&#34;,
&#34;东莞市&#34;: &#34;44/4419.html&#34;,
&#34;揭阳市&#34;: &#34;44/4452.html&#34;,
&#34;阳江市&#34;: &#34;44/4417.html&#34;,
&#34;佛山市&#34;: &#34;44/4406.html&#34;,
&#34;汕尾市&#34;: &#34;44/4415.html&#34;,
&#34;潮州市&#34;: &#34;44/4451.html&#34;,
&#34;梅州市&#34;: &#34;44/4414.html&#34;
}
.......
}
Jsoup爬取页面数据对网络的依赖比较高,像我500块一年50M的方正宽带快要卡出翔了,平均执行三次才有一次成功,我太难了!
Exception in thread &#34;main&#34; java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286)
at java.io.BufferedInputStream.read(BufferedInputStream.java:345)
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:735)
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:678)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1569)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1474)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:443)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:465)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:424)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:178)
at org.jsoup.helper.HttpConnection.get(HttpConnection.java:167)
at com.xinzf.project.jsoup.test2.parseProvinceName(test2.java:32)
at com.xinzf.project.jsoup.test2.main(test2.java:17)
总结
从分析页面到编写代码花费的时间,可能要比简单的复制粘贴还要长,但我依然选择用程序解决问题,并不是因为我有多勤快,反而是因为我很懒,你品,你细品!
<hr/>今天就说这么多,如果本文对您有一点帮助,希望能得到您一个点赞 哦
您的认可才是我写作的动力! |
|