Как загрузить ленивый контент на странице поиска Linkedin с помощью селена - PullRequest
0 голосов
/ 06 января 2019

Резюме

Я пытаюсь очистить все ссылки профиля первого подключения учетной записи на странице поиска LinkedIn. Но поскольку страница загружает остальное содержимое динамически (при прокрутке вниз), я не могу получить кнопку «Далее» на странице, которая находится в конце страницы.


Описание проблемы

https://linkedin.com/search/results/people/?facetGeoRegion=["tr%3A0"]&facetNetwork=["F"]&origin=FACETED_SEARCH&page=YOUR_PAGE_NUMBER

Я могу перейти на страницу поиска, используя селен и ссылку выше. Я хочу знать, сколько существует страниц для навигации по ним, просто изменив переменную page= по ссылке выше.

Для реализации этого я хотел проверить наличие кнопки Next. Пока есть следующая кнопка, я бы запросил следующую страницу для очистки. Но если вы не прокрутите вниз до нижней части страницы, где находится кнопка «Далее», вы не сможете найти кнопку Next и информацию о других профилях, потому что они еще не загружены.

Вот как это выглядит, если вы не прокрутите вниз и не сделаете скриншот всей страницы, используя инструмент для создания скриншотов в Firefox.

linkedIn


Как я реализовал

Я могу исправить это, жестко запрограммировав прокрутку вниз в моем коде и заставив драйвер ждать visibilityOfElementLocated. Но мне было интересно, есть ли другой способ лучше моего подхода. И если при подходе драйвер не может найти кнопку Next, то программа завершается с кодом выхода 1.

И когда я просматриваю запросы, когда прокручиваю страницу вниз, это просто запросы на изображения и т. Д., Как вы можете видеть ниже. Я не мог понять, как страница загружает больше информации о профилях, когда я прокручиваю страницу вниз.

networks inspection


Исходный код

Вот как я реализовал это в своем коде. Это простое приложение, которое пытается найти кнопку Next на странице.

package com.andreyuhai;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

public class App 
{
    WebDriver driver;

    public static void main( String[] args )
    {
        Bot bot = new Bot("firefox", false, false, 0, 0, null, null, null);

        int pagination = 1;

        bot.get("https://linkedin.com");
        if(bot.attemptLogin("username", "pw")){
            bot.get("https://www.linkedin.com/" +
                    "search/results/people/?facetGeoRegion=" +
                    "[\"tr%3A0\"]&origin=FACETED_SEARCH&page=" + pagination);


            JavascriptExecutor js = (JavascriptExecutor) bot.driver;

            js.executeScript("scrollBy(0, 2500)");

            WebDriverWait wait = new WebDriverWait(bot.driver, 10);
            wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//button[@class='next']/div[@class='next-text']")));

            WebElement nextButton = bot.driver.findElement(By.xpath("//button[@class='next']/div[@class='next-text']"));


            if(nextButton != null ) {
                System.out.println("Next Button found");
                nextButton.click();
            }else {
                System.out.println("Next Button not found");
            }
        }
    }
}

Еще один инструмент, который мне интересен: LinkedIn Spider

Существует это расширение Chrome, которое называется connectedIn Spider

Это также делает именно то, что я пытаюсь достичь, но с использованием JavaScript, я думаю, я не уверен. Но когда я запускаю это расширение на той же странице поиска. Это не приводит к прокрутке или загрузке других страниц одна за другой для извлечения данных.


Итак, мои вопросы:

  1. Не могли бы вы объяснить, как LinkedIn достигает этого? Я имею в виду, как он загружает информацию профиля, когда я прокручиваю вниз, если не делаю никаких запросов и т. Д. Я действительно не знаю об этом. Буду признателен за любые ссылки на источники или объяснения.

  2. У вас есть идея получше (быстрее я имею в виду) реализовать то, что я пытаюсь реализовать?

  3. Не могли бы вы объяснить, как LinkedIn Spider может работать без прокрутки вниз и т. Д.

Ответы [ 2 ]

0 голосов
/ 05 февраля 2019

Что я заметил, так это то, что контент уже загружен на страницу, и он будет отображен нам, когда мы сделаем прокрутку вниз.

Но если мы проверяем кнопку «Далее>», загружая страницу вручную, используя имя класса «следующий», например, как показано ниже,

// Кнопка [@ класс = 'следующий']

мы не можем найти его, пока не сделаем прокрутку вниз, потому что он нам не виден. Но используя приведенный ниже XPath, мы можем определить количество ссылок в профиле независимо от того, отображаются они или нет?

// h3 [содержит (@class, 'search-results__total')] / parent :: div / ul / li

Поскольку вы хотите получить все ссылки на профили со страницы, мы можем использовать приведенную выше справку XPath для этого. Мы получим количество ссылок, используя вышеупомянутый XPath, затем прокручиваем по каждому представлению элементов за раз, а затем будем получать ссылки на профили следующим образом:

// Identifying the all the profile links
List<WebElement> totalProfileLinks = driver.findElements(By.xpath("//h3[contains(@class, 'search-results__total')]/parent::div/ul/li"));
// Looping for getting the profile link
for(int i=1;i<totalProfileLinks.size();i++) {
    // Scrolling so that it will be visible
    ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", totalProfileLinks.get(i));
    // Fetching the anchor node
    final WebElement link = driver.findElement(By.xpath("(//h3[contains(@class, 'search-results__total')]/parent::div/ul/li//div[contains(@class, 'search-result__info')]//a)["+i+"]"));
    // Avoiding the StaleElementReferenceException
    new FluentWait<WebDriver>(driver).withTimeout(1, TimeUnit.MINUTES).pollingEvery(1, TimeUnit.SECONDS).ignoring(StaleElementReferenceException.class).until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver arg0) {
            return link;
        }
    });
    // Fetching and printing the link from anchor node
    System.out.println(link.getAttribute("href").trim());
}

Итак, если мы хотим сначала нажать кнопку «Далее>», нам нужно проверить, присутствует ли она или нет (так как при прокрутке ссылок профиля мы прокручивались, кнопка «далее» также выводилась). Мы можем использовать метод `driver.findElements ();`, чтобы получить совпадения количества элементов и сохранить его в некотором списке (поскольку он возвращает список веб-элементов), как показано ниже:

List<WebElement> nextButton = driver.findElements(By.className("next"));

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

Затем мы можем использовать метод size () интерфейса List, чтобы получить количество совпадений, как показано ниже:

int size = nextButton.size();

И если размер больше 0, то этот элемент присутствует, иначе нет, мы можем проверить это условие, как показано ниже:

if(size > 0) {
    nextButton.get(0).click(); // Do some operation like clicking on it
    System.out.println("=> 'Next >' button is there and clicked on it...");
} else {
    System.out.println("=> 'Next >' button is NOT there...");
}

Поскольку содержимое загружено и элемент доступен для просмотра, мы будем использовать JavaScriptExecutor, который найдет его и щелкнет по нему.

Оберните вышеуказанный код в цикл while и проверяйте наличие кнопки «Далее>» каждый раз после нажатия на предыдущую кнопку «Далее>», как показано ниже:

boolean next = true;
while(next) {
    // Checking 'Next >' button is there or not in the page
    List<WebElement> nextButton = driver.findElements(By.className("next"));
    // If the 'Next >' button is there then clicking on it otherwise stopping the execution
    if(nextButton.size() > 0) {
        doClickUsingJSE(nextButton.get(0));
        System.out.println("=> 'Next >' button is there and clicked on it...");
    } else {
        next = false;
        System.out.println("=> 'Next >' button is NOT there so stopping the execution...");
    }
    Thread.sleep(1000);
}

Цикл прервется, если условие «if» не выполнится в приведенном выше коде, поскольку «next» станет «false» И если мы будем использовать Fluent Wait, это поможет нам избежать некоторых «исключений», таких как «WebDriverException» и «StaleElementReferenceException». Поэтому я написал один отдельный метод, который будет ожидать элемент, избегая некоторых исключений и щелкая по нему, если условия будут выполнены.

Проверьте код ниже:

private static void doClickUsingJSE(final WebElement element) {
    // Using the Fluent Wait to avoid some exceptions like WebDriverException and StaleElementReferenceException
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(1, TimeUnit.MINUTES).pollingEvery(1, TimeUnit.SECONDS).ignoring(WebDriverException.class, StaleElementReferenceException.class);
    WebElement waitedElement = wait.until(new Function<WebDriver, WebElement>() {
        public WebElement apply(WebDriver driver) {
            return element;
        }
    });
    wait.until(ExpectedConditions.visibilityOf(waitedElement));
    wait.until(ExpectedConditions.elementToBeClickable(waitedElement));
    // Clicking on the particular element using the JavaScriptExcecutor
    ((JavascriptExecutor) driver).executeScript("arguments[0].click();", waitedElement);
    }

Как я уже упоминал о JavaScriptExecutor ранее, я включил использование этого также только в вышеупомянутый метод.

Попробуйте приведенный ниже сквозной рабочий код:

import java.util.List;
import java.util.concurrent.TimeUnit;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.Wait;

import com.google.common.base.Function;

public class BasePage 
{
    // Declaring WebDriver
    private static WebDriver driver;

    private static void doClickUsingJSE(final WebElement element) {
        // Using the Fluent Wait to avoid some exceptions like WebDriverException and StaleElementReferenceException
        Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(1, TimeUnit.MINUTES).pollingEvery(1, TimeUnit.SECONDS).ignoring(WebDriverException.class, StaleElementReferenceException.class);
        WebElement waitedElement = wait.until(new Function<WebDriver, WebElement>() {
            public WebElement apply(WebDriver driver) {
                return element;
            }
        });
        wait.until(ExpectedConditions.visibilityOf(waitedElement));
        wait.until(ExpectedConditions.elementToBeClickable(waitedElement));
        // Clicking on the particular element using the JavaScriptExcecutor
        ((JavascriptExecutor) driver).executeScript("arguments[0].click();", waitedElement);
    }

    public static void main( String[] args ) throws Exception
    {
        System.setProperty("webdriver.chrome.driver", "C:\\NotBackedUp\\chromedriver.exe");

        // Initializing the Chrome Driver
        driver = new ChromeDriver();

        // Launching the LinkedIn site
        driver.get("https://linkedin.com/search/results/people/?facetGeoRegion=[\"tr%3A0\"]&facetNetwork=[\"F\"]&origin=FACETED_SEARCH&page=YOUR_PAGE_NUMBER");

        // You can avoid this and it to your convience way
        // As there are no connections in my page, I have used like this
        //------------------------------------------------------------------------------------
        // Switching to the login from - iframe involved
        driver.switchTo().frame(driver.findElement(By.className("authentication-iframe")));

        // Clicking on the Sign In button
        doClickUsingJSE(driver.findElement(By.xpath("//a[text()='Sign in']")));

        // Entering the User Name
        WebElement element = driver.findElement(By.id("username"));
        doClickUsingJSE(element);
        element.sendKeys("something@gmail.com");

        // Entering the Password
        element = driver.findElement(By.id("password"));
        doClickUsingJSE(element);
        element.sendKeys("anything"+Keys.ENTER);

        // Clicking on the People drop down
        Thread.sleep(8000);
        element = driver.findElement(By.xpath("//span[text()='People']"));
        doClickUsingJSE(element);

        // Selecting the All option
        Thread.sleep(2000);
        element = driver.findElement(By.xpath("//ul[@class='list-style-none']/li[1]"));
        element.click();

        // Searching something in the LinkedIn search box
        Thread.sleep(3000);
        element = driver.findElement(By.xpath("//input[@role='combobox']"));
        doClickUsingJSE(element);
        element.sendKeys("a"+Keys.ENTER);
        Thread.sleep(8000);
        //------------------------------------------------------------------------------------

        boolean next = true;
        while(next) {
            // Identifying the all the profile links
            List<WebElement> totalProfileLinks = driver.findElements(By.xpath("//h3[contains(@class, 'search-results__total')]/parent::div/ul/li"));
            // Looping for getting the profile link
            for(int i=1;i<totalProfileLinks.size();i++) {
                // Scrolling so that it will be visible
                ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView(true);", totalProfileLinks.get(i));
                // Fetching the anchor node
                final WebElement link = driver.findElement(By.xpath("(//h3[contains(@class, 'search-results__total')]/parent::div/ul/li//div[contains(@class, 'search-result__info')]//a)["+i+"]"));
                // Avoiding the StaleElementReferenceException
                new FluentWait<WebDriver>(driver).withTimeout(1, TimeUnit.MINUTES).pollingEvery(1, TimeUnit.SECONDS).ignoring(StaleElementReferenceException.class).until(new Function<WebDriver, WebElement>() {
                    public WebElement apply(WebDriver arg0) {
                        return link;
                    }
                });
                // Fetching and printing the link from anchor node
                System.out.println(link.getAttribute("href").trim());
            }

            // Checking 'Next >' button is there or not in the page
            List<WebElement> nextButton = driver.findElements(By.className("next"));
            // If the 'Next >' button is there then clicking on it otherwise stopping the execution
            if(nextButton.size() > 0) {
                doClickUsingJSE(nextButton.get(0));
                System.out.println("=> 'Next >' button is there and clicked on it...");
            } else {
                next = false;
                System.out.println("=> 'Next >' button is NOT there so stopping the execution...");
            }
            Thread.sleep(1000);
        }

    }
}

Надеюсь, это поможет ... Счастливое кодирование ...

0 голосов
/ 04 февраля 2019

Я проверил структуру div и то, как linkedin показывает результаты. Таким образом, если вы нажмете на URL-адрес непосредственно и проверите следующую команду xpath: //li[contains(@class,'search-result')] Вы обнаружите, что все результаты уже загружены на страницу, но linkedin показывает только 5 результатов за один раз и при прокрутке, он показывает следующие 5 результатов, однако все результаты уже загружены на страницу и могут быть найдены по указанному xpath.

См. Это изображение, которое выделяет структуру div и результаты, когда вы найдете результаты при вводе xpath при нажатии на URL: https://imgur.com/Owu4NPh и
Обратитесь к этому изображению, которое выделяет структуру div и результаты после прокрутки страницы вниз и последующего поиска результатов с использованием того же xpath: https://imgur.com/7WNR830

Вы можете видеть, что набор результатов такой же, но есть дополнительная часть search-result__occlusion-hint в теге

в последних 5 результатах, и через этот код скрываются следующие 5 результатов и отображаются только первые 5 результатов на первый ход.

Теперь идет часть реализации, я проверил, что кнопка «Далее» появляется только тогда, когда вы прокручиваете все результаты на странице, поэтому вместо прокрутки до определенных координат, потому что это можно изменить для разных размеров экрана и окон, вы можете взять результаты в списке webelement и получить его размер, а затем перейдите к последнему элементу этого списка. В этом случае, если имеется всего 10 результатов, страница будет прокручена до 10-го результата, а если имеется только 4 результата, то страница будет прокручена до 4-го результата, и после прокрутки вы можете проверить, присутствует ли кнопка Далее. страница или нет. Для этого вы можете проверить размер списка списка веб-элементов кнопки «Далее», если размер списка больше 0, это означает, что следующая кнопка присутствует на странице, а если она не больше 0, это означает, что Next Кнопка отсутствует в списке, и вы можете остановить выполнение.

Итак, чтобы реализовать его, я взял логическое значение, которое имеет начальное значение как истинное, и код будет выполняться в цикле, пока это логическое значение не станет ложным, и оно станет ложным, когда размер списка кнопки «Далее» станет равным 0.

Пожалуйста, используйте код ниже:

public class App 
{    
    WebDriver driver;

  // For initialising javascript executor
  public Object executeScript(String script, Object... args) {
    JavascriptExecutor exe = (JavascriptExecutor) driver;
    return exe.executeScript(script, args);
  }

  // Method for scrolling to the element
  public void scrollToElement(WebElement element) {
    executeScript("window.scrollTo(arguments[0],arguments[1])", element.getLocation().x, element.getLocation().y);

    }

  public static void main(String[] args) {
    // You can change the driver to bot according to your usecase
    driver = new FirefoxDriver();
    // Add your direct URL here and perform the login after that, if necessary
    driver.get(url);
    // Wait for the URL to load completely
    Thread.sleep(10000);
    // Initialising the boolean
    boolean nextButtonPresent = true;
    while (nextButtonPresent) {
        // Fetching the results on the page by the xpath
        List<WebElement> results = driver.findElements(By.xpath("//li[contains(@class,'search-result')]"));
        // Scrolling to the last element in the list
        scrollToElement(results.get(results.size() - 1));
        Thread.sleep(2000);

        // Checking if next button is present on the page
        List<WebElement> nextButton = driver.findElements(By.xpath("//button[@class='next']"));
        if (nextButton.size() > 0) {
            // If yes then clicking on it
            nextButton.get(0).click();
            Thread.sleep(10000);
        } else {
            // Else setting the boolean as false
            nextButtonPresent = false;
            System.out.println("Next button is not present, so ending the script");
        }
      }
   }
}
...