我正尝试在LinkedIn搜索页面上抓取帐户的所有首次连接的个人资料链接。但是由于该页面会动态加载其余内容(向下滚动),所以我无法获得位于页面末尾的“下一页”按钮。
https://linkedin.com/search/results/people/?facetGeoRegion=["tr%3A0"]&facetNetwork=["F"]&origin=FACETED_SEARCH&page=YOUR_PAGE_NUMBER
我可以使用硒和上面的链接导航到搜索页面。我想知道有多少页面可以浏览它们,只需更改上面链接的page=
变量即可。
要实现我想检查Next
按钮是否存在的功能。只要有下一个按钮,我就会请求下一页进行抓取。但是,如果您没有向下滚动到页面底部(即“下一步”按钮所在的位置),您将找不到Next
按钮,也找不到其他配置文件的信息,因为它们尚未加载
这是当您不向下滚动并使用firefox截屏工具截取整个页面的截屏时的外观。
我可以通过将向下滚动动作硬编码到我的代码中并使驱动程序等待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
这也确实可以达到我想要达到的目的,但是我不确定使用JavaScript。但是,当我在同一搜索页面上运行此扩展程序时。这样做不会向下滚动或加载其他页面,也不会一一提取数据。
所以我的问题是:
您能否解释一下LinkedIn是如何实现这一目标的?我的意思是,如果不发出任何请求等,当我向下滚动时它将如何加载配置文件信息。我真的不知道这一点。我将不胜感激任何源链接或解释。
您有什么更好的想法(我的意思是更快)来实现我要实现的目标?
能否请您解释一下LinkedIn Spider
在不向下滚动等情况下如何工作。
答案 0 :(得分:6)
我已经检查了div结构以及linkedin显示结果的方式。因此,如果直接点击URL并通过以下xpath进行检查://li[contains(@class,'search-result')]
,您会发现所有结果均已加载到页面上,但是linkedin一次只能显示5个结果,并在滚动时显示显示下5个结果,但是所有结果均已加载到页面上,并可以通过提及的xpath找到。
请参阅此图片,该图片突出显示了div结构和结果,当您找到在到达url时输入xpath的结果时:https://imgur.com/Owu4NPh和
将页面滚动到底部然后使用相同的xpath查找结果后,请参考这张突出显示div结构和结果的图像:https://imgur.com/7WNR830
您可能会看到结果集相同,但是最后5个结果中
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");
}
}
}
}
答案 1 :(得分:1)
我观察到的是,内容已经加载到页面中,当我们向下滚动时,它将显示给我们。
但是,如果我们通过使用类名“ next”手动加载页面来检查“ Next>”按钮,如下所示,
// button [@ class ='next']
在向下滚动之前我们无法找到它,因为它对我们不可见。但是,通过使用下面的XPath,我们可以确定所有配置文件链接的计数,无论它们是否显示?
// h3 [包含(@class,'搜索结果__总数')] / 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中(因为它返回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,那么它将有助于我们避免某些“异常”,例如“ 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);
}
}
}
我希望它对您有帮助...编码愉快...