Чтобы не копировать и не вставлять, я был вынужден изучить краулер JAVA

Java

Я разобрал кое-какую архитектуру Java и материалы интервью (микросервисы, кластеры, распределенное, промежуточное ПО и т.д.), а друзья, кому нужно, могут обратить внимание на официальный аккаунт [Внутренние дела Программиста], и получить самостоятельно без всяких рутин

Автор этой статьи:Внутри программатора

Больше избранных статей

написать впереди

Из-за эпидемии я работаю удаленно из дома. Бизнес компании идет медленно. Честно говоря, работы не так много. Я ем, сплю, посещаю техническое сообщество и пишу блоги каждый день. Утром я хотел вздремнуть, но вдруг пришло голосовое сообщение от начальника отдела и сообщило мне адрес для подключения.woohoo.stats.gov.talents/stats/его больше нет/он…Меня попросили узнать названия провинций и городов, а также коды городов по всей стране и составить словарную таблицу с ограничением по времени в одно утро.

在这里插入图片描述

Разделите потребности

Чтобы построить словарную таблицу для хранения названий провинций и городов по всей стране, дизайн структуры таблицы относительно прост, так что насчет данных о городах?

Есть два решения:

  1. Трудно копировать и вставлять, а сказать можно только несколько сотен.

  2. Напишите сканер раз и навсегда

Но как программист нет ничего, что нельзя было бы решить с помощью программы, хотя работаCtrl+C,Ctrl+VОн используется очень часто, и довольно стыдно копировать и вставлять без технического содержания.

рептилии

Исходя из этого требования, мне нужно только название города. Сканер выбирает Jsoup. Jsoup — это анализатор Java HTML, который может напрямую анализировать URL-адрес и текстовое содержимое HTML. Он предоставляет очень простой API для извлечения данных и манипулирования ими с помощью методов манипулирования, подобных DOM, CSS и jQuery.

Jsoup основан на HTML-страницах.<body>,<td>,<tr>и другие теги для получения текстового контента, поэтому сначала проанализируйте структуру целевой страницы. ОткрытымF12Глядя на структуру страницы, мы обнаружили, что нужные нам целевые данные находятся в пятом<tbody>Этикеткаclassсобственностьprovincetrиз<tr>в этикетке.

在这里插入图片描述
Структура страницы содержимого названия провинции выглядит следующим образом:

<tr class="provincetr">
     <td>
        <a href="11.html">北京市<br></a>
     </td>
     <td>
         <a href="12.html">天津市<br>
     </td>
     .........
</tr>

получить его снова<td>на этикетке<a>Атрибута метки достаточно, название провинции найдено, а дальше смотрим, где находится название города, соответствующее провинции, атрибутhref="11.html"Это соответствующая страница города под провинцией.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("tbody");

        /**
         * 拿到第五个tbody标签
         */
        Element element = elements.get(4);

        /**
         * 拿到tbody标签下所有的子标签
         */
        Elements childrens = element.children();

        /**
         * 当前页面的URL
         */
        String baseUri = element.baseUri();

        for (Element element1 : childrens) {
        
            Elements provincetrs = element1.getElementsByClass("provincetr");
            
            for (Element provincetr : provincetrs) {
            
                Elements tds = provincetr.getElementsByTag("td");
                for (Element td : tds) {

                    String provinceName = td.getElementsByTag("a").text();
                    String href = td.getElementsByTag("a").attr("href");

                    System.out.println(provinceName + "    " + baseUri + "/" + href);

                    map.put(provinceName, null);
                    /**
                      * 组装城市页面的URL,进入城市页面爬城市名称
                      */
                    parseCityName(map, baseUri + "/" + 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("tbody");
        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("citytr");

            for (Element cityTag : citytrs) {
                Elements tds = cityTag.getElementsByTag("td");

                /**
                 * 直辖市,城市名就是本身
                 */
                String cityName = tds.get(1).getElementsByTag("a").text();
                
                if (cityName.equals("市辖区")) {
                    cityName = provinceName;
                }
                String href1 = tds.get(1).getElementsByTag("a").attr("href");

                System.out.println(cityName + " " + 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, "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2018");
        
        System.out.println(JSON.toJSONString(map));
    }
}

3. Выходная строка JSON

В настоящее время, пока требуется название провинции и города, сканер не имеет глубины.Если вам нужна информация, такая как районы и округа, вы можете использовать URL-адрес за городом.35/3508.htmlпродолжать спускаться вниз

{
	"福建省": {
		"龙岩市": "35/3508.html",
		"南平市": "35/3507.html",
		"莆田市": "35/3503.html",
		"福州市": "35/3501.html",
		"泉州市": "35/3505.html",
		"漳州市": "35/3506.html",
		"厦门市": "35/3502.html",
		"三明市": "35/3504.html",
		"宁德市": "35/3509.html"
	},
	"西藏自治区": {
		"拉萨市": "54/5401.html",
		"昌都市": "54/5403.html",
		"日喀则市": "54/5402.html",
		"那曲市": "54/5406.html",
		"林芝市": "54/5404.html",
		"山南市": "54/5405.html",
		"阿里地区": "54/5425.html"
	},
	"贵州省": {
		"贵阳市": "52/5201.html",
		"毕节市": "52/5205.html",
		"铜仁市": "52/5206.html",
		"六盘水市": "52/5202.html",
		"遵义市": "52/5203.html",
		"黔西南布依族苗族自治州": "52/5223.html",
		"安顺市": "52/5204.html",
		"黔东南苗族侗族自治州": "52/5226.html",
		"黔南布依族苗族自治州": "52/5227.html"
	},
	"上海市": {
		"上海市": "31/3101.html"
	},
	"湖北省": {
		"黄冈市": "42/4211.html",
		"孝感市": "42/4209.html",
		"恩施土家族苗族自治州": "42/4228.html",
		"省直辖县级行政区划": "42/4290.html",
		"襄阳市": "42/4206.html",
		"鄂州市": "42/4207.html",
		"十堰市": "42/4203.html",
		"咸宁市": "42/4212.html",
		"黄石市": "42/4202.html",
		"荆州市": "42/4210.html",
		"随州市": "42/4213.html",
		"宜昌市": "42/4205.html",
		"武汉市": "42/4201.html",
		"荆门市": "42/4208.html"
	},
	"湖南省": {
		"湘潭市": "43/4303.html",
		"衡阳市": "43/4304.html",
		"张家界市": "43/4308.html",
		"益阳市": "43/4309.html",
		"岳阳市": "43/4306.html",
		"娄底市": "43/4313.html",
		"株洲市": "43/4302.html",
		"常德市": "43/4307.html",
		"湘西土家族苗族自治州": "43/4331.html",
		"郴州市": "43/4310.html",
		"邵阳市": "43/4305.html",
		"长沙市": "43/4301.html",
		"永州市": "43/4311.html",
		"怀化市": "43/4312.html"
	},
	"广东省": {
		"河源市": "44/4416.html",
		"韶关市": "44/4402.html",
		"茂名市": "44/4409.html",
		"汕头市": "44/4405.html",
		"清远市": "44/4418.html",
		"深圳市": "44/4403.html",
		"珠海市": "44/4404.html",
		"广州市": "44/4401.html",
		"肇庆市": "44/4412.html",
		"中山市": "44/4420.html",
		"江门市": "44/4407.html",
		"云浮市": "44/4453.html",
		"惠州市": "44/4413.html",
		"湛江市": "44/4408.html",
		"东莞市": "44/4419.html",
		"揭阳市": "44/4452.html",
		"阳江市": "44/4417.html",
		"佛山市": "44/4406.html",
		"汕尾市": "44/4415.html",
		"潮州市": "44/4451.html",
		"梅州市": "44/4414.html"
	}

     .......
}

Данные страницы сканирования Jsoup сильно зависят от сети. Например, мой широкополосный доступ Founder за 500 юаней в год 50M вот-вот зависнет, и в среднем это удается только один раз после трех запусков. Это слишком сложно для меня!

Exception in thread "main" 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)

Суммировать

От анализа страницы до написания кода может пройти больше времени, чем простое копирование и вставка, но я все же предпочитаю использовать программу для решения проблемы не из-за того, насколько я усерден, а потому, что я ленив.


Сегодня так много нужно сказать, если эта статья была вам полезна, я надеюсь получить от вас лайк 👍

Ваше одобрение является движущей силой для моего письма!


Я разобрал кое-какую архитектуру Java и материалы интервью (микросервисы, кластеры, распределенное, промежуточное ПО и т.д.), а друзья, кому нужно, могут обратить внимание на официальный аккаунт [Внутренние дела Программиста], и получить самостоятельно без всяких рутин