Crawler Practice — сканирование информации о членах группы Facebook на основе Jsoup

Apache Chrome HTML Facebook

Сканирование информации о членах группы Facebook на основе Jsoup

Мы знаем, что большая часть контента таких приложений, как Toutiao и UC Toutiao, создается в результате сканирования. Мы можем использовать множество языков для реализации поисковых роботов, таких как C/C++, Java, Python, PHP, NodeJS и т. д. Также существует множество широко используемых фреймворков, таких как Python Scrapy, NodeJS cheerio, Java Jsoup и т. д. . В этой статье будет показано, как реализовать имитацию входа в Facebook через Jsoup, сканировать информацию об участниках определенной группы и экспортировать в Excel.

Keywords: Netbeans, JSwing, Jsoup, Apache POI , Jackson

Source Code: FacebookGrabber

1. Имитация входа в Facebook

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

Посетите https://www.facebook.com/login и проверьте элемент html через хром, чтобы увидеть:

Facebook登录页面

Зная соответствующий URL-адрес для входа и параметры запроса, теперь мы создаем запрос на вход через Jsoup для получения информации о пользовательских файлах cookie.

Здесь я написал базовый класс, который удобен для вызовов запроса и автоматически сохраняет куки, полученные каждым запросом, и прикрепляет его к следующему запросу:

    private Connection getConnection(String url) {
        return Jsoup.connect(url)
                .timeout(TIMEOUT_CONNECTION)
                .userAgent(userAgent)
                .followRedirects(true)
                .ignoreContentType(true);
    }

    protected Document requestDocument(String url, String httpMethod, Map<String, String> data) throws Exception {
        Connection connection = getConnection(url);
        if (data != null && data.size() > 0) {
            connection.data(data);
        }
        if (cookies != null) {
            connection.cookies(cookies);
        }
        Document resultDocument = HTTP_POST.equalsIgnoreCase(httpMethod) ? connection.post() : connection.get();
        return resultDocument;
    }

    protected Response requestBody(String url, String httpMethod, Map<String, String> data) throws Exception {
        Connection connection = getConnection(url);
        if (data != null && data.size() > 0) {
            connection.data(data);
        }
        if (cookies != null) {
            connection.cookies(cookies);
        }
        connection.method(HTTP_POST.equalsIgnoreCase(httpMethod) ? Connection.Method.POST : Connection.Method.GET);
        Connection.Response res = connection.execute();
        if (res.cookies() != null && !res.cookies().isEmpty()) {
            cookies = res.cookies();
        }
        return res;
    }

На основе базового класса инкапсулированного запроса следующая реализацияИмитация входаСтало проще:

    public FbUserInfo login(String email, String pass) throws Exception {
        // Urls.LOGIN = "https://www.facebook.com/login.php?login_attempt=1";
        Response initResponse = requestBody(Urls.LOGIN, HTTP_GET, null); //fetching cookie and saving

        Map<String, String> loginParams = new HashMap<>();
        loginParams.put("email", email);
        loginParams.put("pass", pass);
        Response loginResponse = requestBody(Urls.LOGIN, HTTP_POST, loginParams);
        Document loginDoc = loginResponse.parse();

        FbUserInfo userInfo = null;
        String userId = loginResponse.cookies().get("c_user"); // current login userId
        if (userId != null && userId.length() > 0) {
            userInfo = new FbUserInfo();
            userInfo.setId(userId);
        }
        return userInfo;
    }

2. Получить список администраторов и участников группы

Следующее будетHomesteads.and.SustainabilityНапример, чтобы продемонстрировать, как получить список администраторов и участников соответствующей группы.

Посетите https://www.facebook.com/groups/Homesteads.and.Sustainability/members/ , проанализируйте запросы и возвращенные ответы, просмотрев сеть Chrome:

members_response

Скопируйте html в ответ и найдите html-информацию, соответствующую администратору и участнику, ища имя человека, отображаемое на первой странице загрузки (находится в элементе с идентификатором groupsMemberBrowser):

members_html

После форматирования текста выполните детальный анализ: Отличить администраторов от обычных участников и получить их идентификаторы можно с помощью правил префиксов, показанных на следующем рисунке:

admins_members

Примерный код выглядит следующим образом:

    private List<FbGroupUserInfo> getMembers(Element groupsMembersElement, int role) {
        String prefix = role == Constants.GROUP_ROLE_ADMIN ? "admins_moderators_" : "recently_joined_";
        List<FbGroupUserInfo> userInfoList = new ArrayList<>();
        Elements memberElements = groupsMembersElement.select(String.format("div[id^=%s]", prefix));
        if (memberElements != null && memberElements.size() > 0) {
            for (Element e : memberElements) {
                String id = e.attr("id").replace(prefix, "");
                String nickName = e.select("a img").first().attr("aria-label");
                String userName = e.select("a").first().attr("href").split("\\?")[0];
                userName = userName.substring(userName.lastIndexOf("/"));
                Elements joinElements = e.getElementsByClass("_60rj");
                String joinDate = joinElements.size() > 0 && role == Constants.GROUP_ROLE_GENERAL
                        ? joinElements.first().text().trim() : "";
                FbGroupUserInfo userInfo = new FbGroupUserInfo();
                userInfo.setId(id);
                userInfo.setNickName(nickName);
                userInfo.setUserName(userName);
                userInfo.setJoinInfo(joinDate);
                userInfo.setRole(role);
                userInfoList.add(userInfo);
            }
        }
        return userInfoList;
    }

Выше мы получили соответствующую информацию об участнике только путем анализа html-текста, к которому обращались в первый раз, но мы знаем, что в группе гораздо больше участников.Спустившись вниз, мы инициируем запрос на получение дополнительной информации об участнике:

request_more

Итак, откуда берется этот URL-адрес, запрашивающий дополнительные данные? Оглядываясь назад на html, возвращенный в первый раз, вы можете увидеть:

more_url

Код URL-адреса для получения и загрузки дополнительных данных примерно выглядит следующим образом:

    private String getMoreItemUrl(Element groupsMembersElement, int role) {
        String prefix = role == Constants.GROUP_ROLE_ADMIN ? "group_admins_moderators" : "group_confirmed_members";
        String moreItemUrl = "";
        try {
            moreItemUrl = groupsMembersElement.select(String.format("a[href^=/ajax/browser/list/%s/]", prefix))
                    .first().attr("href");
        } catch (Exception e) {
        }
        return moreItemUrl;
    }

В первый раз мы можем узнать первую группу участников и первую загрузку дополнительных URL-адресов, проанализировав первый html, затем второй раз и каждый раз после этого возвращаемые данные json. Аналогичным образом, анализируя возвращаемый формат json, можно увидеть, что информация о членах в json также возвращается в виде html-текста, и цикл продолжается до тех пор, пока не перестанут загружаться URL-адреса, чтобы можно было получить информацию обо всех членах. псевдоним тоже.

Получите элемент groupMembersElement в данных, возвращаемых ajax, общий код выглядит следующим образом.

 ajaxString = ajaxString.substring(ajaxString.indexOf("{"));
 ObjectMapper m = new ObjectMapper();
 JsonNode rootNode = m.readValue(ajaxString, JsonNode.class);
 String html = rootNode.get("domops").get(0).get(3).get("__html").getValueAsText();
 Element groupMembersElement = Jsoup.parse(html, "", Parser.xmlParser());

Однако недостаточно получить id и никнейм участника, нам нужно получить более подробную информацию об участнике: родной город, место жительства, пол и т.д.

3. Получите информацию о пользователе

Ниже сown a salonВозьмите этого пользователя Facebook в качестве примера, чтобы продемонстрировать, как получить информацию о пользователе.

Здесь, посетив страницу мобильной версии (страница мобильной версии удобнее для получения информации) https://m.facebook.com/profile.php?v=info&id=100005502877898

profile mobile site
profile current city

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

    public FbUserInfo getUserInfo(String userId) throws Exception {
        // Urls.USER_PROFILE = https://m.facebook.com/profile.php?v=info&id=%s;
        String url = String.format(Urls.USER_PROFILE, userId);
        Document doc = requestDocument(url, HTTP_GET, null);

        Elements userNameElements = doc.select("div[title=Facebook] div div");
        Elements genderElements = doc.select("div[title=Gender] div div");
        Elements hometownElements = doc.select("h4:contains(Home Town)");
        Elements locationElements = doc.select("h4:contains(Current City)");

        String userName = userNameElements.size() > 0 ? userNameElements.first().text().trim() : "";
        String gender = genderElements.size() > 0 ? genderElements.first().text().trim() : "";
        String hometown = hometownElements.size() > 0 ? hometownElements.first().firstElementSibling().text().trim() : "";
        String location = locationElements.size() > 0 ? locationElements.first().firstElementSibling().text().trim() : "";

        FbUserInfo userInfo = new FbUserInfo();
        userInfo.setId(userId);
        userInfo.setUserName(userName);
        userInfo.setGender(gender);
        userInfo.setHometown(hometown);
        userInfo.setLocation(location);
        return userInfo;
    }

4. Окончательный эффект

主界面及导出数据