簡體   English   中英

如何使用selenium在Linkedin搜索頁面上加載延遲內容

[英]How to load lazy content on Linkedin search page using selenium

摘要

我想在LinkedIn搜索頁面上搜索帳戶的所有第一個連接的個人資料鏈接。 但由於頁面動態加載其余內容(當您向下滾動時),我無法獲得位於頁面末尾的“下一頁”頁面按鈕。


問題描述

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

我可以使用selenium和上面的鏈接導航到搜索頁面。 我想知道有多少頁面可以導航它們只需更改上面鏈接的page=變量。

為了實現我想要檢查是否存在Next按鈕。 只要有下一個按鈕,我就會請求下一頁進行抓取。 但是如果你不向下滾動直到頁面底部 - 這是'下一步'按鈕的位置 - 你找不到Next按鈕,也找不到有關其他配置文件的信息,因為它們尚未加載。

以下是當您不使用firefox截屏工具向下滾動並截取整個頁面的截圖時的外觀。

LinkedIn


我是如何實施的

我可以通過將向下滾動操作硬編碼到我的代碼中並使驅動程序等待visibilityOfElementLocated來解決這個問題。 但我想知道是否還有其他方法比我的做法更好。 如果通過該方法,驅動程序無法以某種方式找到“ Next按鈕,程序將以退出代碼1退出。

當我向下滾動頁面時檢查請求時,它只是對圖像等的請求,如下所示。 當我向下滾動頁面時,我無法弄清楚頁面如何加載有關配置文件的更多信息。

網絡檢查


源代碼

以下是我在代碼中實現它的方法。 這個應用程序只是一個簡單的實現,它試圖找到頁面上的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擴展名為linkedIn Spider

這也正是我想要實現的,但我想使用JavaScript,我不確定。 但是當我在同一個搜索頁面上運行此擴展時。 這不會向下滾動或逐個加載其他頁面提取數據。


所以我的問題是:

  1. 能否請您解釋一下LinkedIn如何實現這一目標? 我的意思是它如何加載配置文件信息,因為我向下滾動,如果不做任何請求等等。我真的不知道這個。 我將不勝感激任何來源鏈接或解釋。

  2. 你有更好的(我的意思更快)想法來實現我想要實現的東西嗎?

  3. 能否請您解釋一下LinkedIn Spider如何在不向下滾動的情況下工作等等。

我檢查了div結構和linkedin顯示結果的方式。 所以,如果你直接點擊url並按照xpath: //li[contains(@class,'search-result')]你會發現所有結果都已經加載到頁面上,但是會顯示滾動時只有5個結果,它顯示接下來的5個結果,但是所有結果都已加載到頁面上,並且可以通過上述xpath找到。

當您在點擊網址時輸入xpath時找到結果時,請參閱此圖片,其中突出顯示了div結構和結果: https//imgur.com/Owu4NPh
參考此圖像,在將頁面滾動到底部然后使用相同的xpath查找結果后突出顯示div結構和結果: https//imgur.com/7WNR830

您可以看到結果集相同但是在最后5個結果中<li>標記中有一個額外的search-result__occlusion-hint部分,並且通過此linkedin隱藏了接下來的5個結果並且僅顯示了第一個結果中的前5個結果走。

現在來了實現部分,我已經選中了“下一步”按鈕,只有當你在頁面上滾動整個結果時才會出現,所以不是滾動到一個明確的坐標,因為可以針對不同的屏幕尺寸和窗口進行更改,你可以將結果輸入一個webelement列表並獲取它的大小,然后滾動到該列表的最后一個元素。 在這種情況下,如果總共有10個結果,則頁面將滾動到第10個結果,如果只有4個結果,則頁面將滾動到第4個結果,滾動后可以檢查下一個按鈕是否存在頁面與否。 為此,您可以檢查“下一步”按鈕Web元素列表的列表大小,如果列表大小大於0,則表示頁面上存在下一個按鈕,如果不大於0,則表示下一個按鈕列表中沒有按鈕,您可以在那里停止執行。

所以為了實現它,我采用了一個布爾值,其初始值為true,代碼將在循環中運行,直到布爾值變為false,並且當Next按鈕列表大小等於0時,它將變為false。

請參考以下代碼:

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");
        }
      }
   }
}

我觀察到的是,內容已經加載到頁面中,當我們向下滾動時它將顯示給我們。

但是如果我們通過使用類名“next”手動加載頁面來檢查“下一步>”按鈕,例如如下所示,

//按鈕[@類= '下一個']

我們無法找到它,直到我們向下滾動,因為它對我們來說是不可見的。 但是通過使用下面的XPath,我們可以識別所有配置文件鏈接的數量,無論它們是否顯示?

// h3 [contains(@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());
}

所以,如果我們想首先點擊'Next>'按鈕,我們需要檢查它是否存在(因為我們在獲取配置文件鏈接時滾動,'下一個'按鈕也被顯示)。 我們可以使用`driver.findElements();`方法的幫助來獲取該元素的匹配數,並將它存儲在某個List中(因為它返回WebElements列表),如下所示:

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

使用上述技術的好處是,如果沒有元素匹配,腳本也不會失敗,如果沒有匹配,我們將有一個空列表。

然后我們可以使用List接口的size()方法來獲得匹配計數,如下所示:

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循環中包含上面的代碼,並在每次單擊上一個'Next>'按鈕后檢查是否存在'Next>'按鈕,如下所示:

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,它將幫助我們避免一些'Exceptions',如'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);
        }

    }
}

我希望它有所幫助...快樂編碼......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM