12

Próbuję skrobać nazwy produktów ze strony internetowej. Dziwnie, wydaje mi się, że tylko zeskrobuję losowo 12 przedmiotów. Próbowałem zarówno HtmlAgilityPack i HTTPClient i otrzymuję te same losowe wyniki. Oto mój kod HtmlAgilityPack:HtmlAgilityPack i Selenium Webdriver zwraca losowe wyniki

using HtmlAgilityPack; 
using System.Net.Http; 

var url = @"http://www.roots.com/ca/en/men/tops/shirts-and-polos/"; 
HtmlWeb web = new HtmlWeb(); 
var doc = web.Load(url, "GET", proxy, new NetworkCredential(PROXY_UID, PROXY_PWD, PROXY_DMN)); 
var nodes = doc.DocumentNode.Descendants("div") 
      .Where(div => div.GetAttributeValue("class", string.Empty) == "product-name") 
      .Select(div => div.InnerText.Trim()) 
      ; 

[UPDATE 1] @CodingKuma zaproponował staram selen Webdriver. Oto mój kod przy użyciu Selenium Webdriver:

IWebDriver chromeDriver = new ChromeDriver(@"C:\TEMP\Projects\Chrome\chromedriver_win32"); 
chromeDriver.Url = "http://www.roots.com/ca/en/men/tops/shirts-and-polos/"; 
var items = chromeDriver.FindElements(By.ClassName("product-name")); 
items.Count().Dump(); 
chromeDriver.Quit(); 

Próbowałem tego kodu, ale nadal nie ma szczęścia. Na tej stronie znajduje się ponad 20 pozycji, ale wydaje mi się, że otrzymuję tylko losowe 12. Jak mogę zeskrobać wszystkie przedmioty na tej stronie?

+0

spróbuj innego klienta użytkownika? czy inni są załadowani ajaxem? –

+0

Ponieważ ta strona ładuje się jako przewijana, skrobak nie jest człowiekiem. –

+0

@ DanielA.White co inni agenci polecacie? –

Odpowiedz

3

W przypadku większości aplikacji lub stron pojedynczych, które ładują zawartość dynamicznie, lepiej skorzystać z rzeczywistej przeglądarki do nawigacji po stronach. Proponuję zajrzeć do selenu dla tego typu konfiguracji.

https://www.nuget.org/packages/Selenium.WebDriver

+0

To też nie działa. Oto mój kod: 'IWebDriver chromeDriver = nowy ChromeDriver (@" C: \ TEMP \ Projects \ Chrome \ chromedriver_win32 "); chromeDriver.Url = "http://www.roots.com/ca/en/men/tops/shirts-and-polos/"; var items = chromeDriver.FindElements (By.ClassName ("nazwa-produktu")); items.Count(). Dump(); chromeDriver.Quit(); ' Ciągle otrzymuję liczbę 12 zamiast 24. –

+0

Poprawiłem odpowiedź odpowiednio. – CodingKuma

+0

Myślę, że większość ludzi zgodziłaby się, że niewłaściwą praktyką jest przyjmowanie innych odpowiedzi i dodawanie ich do własnego bez znaczącego wkładu. – JeffC

3

więc istnieje kilka problemów, które uniemożliwiają poprawne liczby od bycia.

  1. Strona ma leniwego ładowacz. Trzeba przewinąć w dół, aby wywołać obciążenie elementów ponad 12.

  2. Strona wykorzystuje AJAX wzywa do załadowania elementów ponad 12.

Tak, trzeba, aby przejść do strony, przewiń na dole strony poczekaj na zakończenie AJAX, a następnie zeskanuj stronę. Poniższy kod jest testowany i zwraca 20 elementów.

Skrypt

String url = "http://www.roots.com/ca/en/men/tops/shirts-and-polos/"; 
driver.navigate().to(url); 
JavascriptExecutor js = ((JavascriptExecutor) driver); 
int height = 1; 
int lastHeight = 0; 
while (lastHeight != height) 
{ 
    lastHeight = height; 
    js.executeScript("window.scrollTo(0, document.body.scrollHeight);"); 
    height = (int) (long) js.executeScript("return document.body.scrollHeight;"); 
} 

waitForJSandJQueryToLoad(10); 

List<WebElement> products = driver.findElements(By.cssSelector("div.product-name")); 
System.out.println(products.size()); 
for (WebElement e : products) 
{ 
    System.out.println(e.getText()); 
} 

funkcja Pomoc

public boolean waitForJSandJQueryToLoad(int timeOut) 
{ 
    WebDriverWait wait = new WebDriverWait(driver, timeOut); 

    ExpectedCondition<Boolean> jQueryIsLoaded = new ExpectedCondition<Boolean>() 
    { 
     @Override 
     public Boolean apply(WebDriver driver) 
     { 
      return (Boolean) ((JavascriptExecutor) driver).executeScript("return (window.jQuery != null) && (jQuery.active === 0);"); 
     } 
    }; 

    ExpectedCondition<Boolean> jsIsLoaded = new ExpectedCondition<Boolean>() 
    { 
     @Override 
     public Boolean apply(WebDriver driver) 
     { 
      return (Boolean) ((JavascriptExecutor) driver).executeScript("return document.readyState == 'complete'"); 
     } 
    }; 

    return wait.until(jQueryIsLoaded) && wait.until(jsIsLoaded); 
} 

Wyjście

20 
Rideau Flannel Shirt 
Westridge Denim Shirt 
Rideau Flannel Shirt 
Riverside Plaid Shirt 
Riverside Plaid Shirt 
Heritage Peppered Polo 
Heritage Peppered Polo 
Heritage Peppered Polo 
Cedar Jersey Polo 
Cedar Jersey Polo 
Hope River Shirt 
Hawthorne Surplus Shacket 
Acadian Linen Shirt 
Camp Short Sleeve Shirt 
Foxley Short Sleeve Shirt 
Heritage Peppered Polo 
Foxley Short Sleeve Shirt 
Waterway Indigo Shirt 
Waterway Indigo Shirt 
Resolute Flannel Shirt 
3

Jak mówili inni, strona z tej witryny ładuje się dynamicznie przy użyciu niektórych javascript, więc html Agility Pakiet dostaje tylko pierwsze elementy.

Przeszukanie stron internetowych może być trudne, szczególnie w przypadku nowoczesnych witryn, które wykorzystują coraz więcej skryptów javascript, a generalnie jest to bardzo specyficzne dla witryny docelowej (nawet nie mówię o problemach prawnych). Możesz użyć różnych technik, aby określić, w jaki sposób uzyskać potrzebne informacje.

W takim przypadku, jeśli korzystasz z dowolnego analizatora sieci, szybko zobaczysz, że witryna używa parametru ciągu zapytania 'sz' (dla rozmiaru domyślnego), który pozwala określić liczbę potrzebnych elementów.

Tak, tylko zmodyfikować URL to:

var url = @"http://www.roots.com/ca/en/men/tops/shirts-and-polos/?sz=9999"; 

i uzyskać wszelkie liczby elementów, które chcesz.

+0

Chociaż jest to przydatne informacje, nie odpowiada na pytanie. Otrzymuje już 20 produktów i widzi tylko pierwsze 12. Otrzymanie 9999 produktów nie rozwiąże tego problemu. – JeffC

+0

@JeffC - ??? Bez parametrów sz nie dostajesz wszystkich produktów w jednym HTTP GET, tylko część, to jest właśnie pytanie. Definiowanie sz o dużej wartości spowoduje uzyskanie maksymalnej możliwej liczby pozycji w jednym GET (do 9999 w mojej próbce), tj. 20 dla tego zapytania. Spróbuj obu adresów URL będzie skrzypek i zrozumiesz. –

+0

Nie, pytanie brzmi: "hej ... jest 20 produktów na stronie i mam tylko 12, dlaczego tak jest?" Jeśli OP użyje twojej odpowiedzi, następnym pytaniem będzie: "hej ... na stronie jest 9999 produktów, a ja mam tylko 12, dlaczego?" Ref: 'Istnieje ponad 20 pozycji na tej stronie, ale wydaje mi się, że dostaję losowe 12.' – JeffC

3

Od wersji v1.5.0-beta92,

HtmlAgilityPack ma metodę FromBrowser, która pozwala czekać, aż wszystkie elementy będą gotowe.

Dokumentacja: http://html-agility-pack.net/from-browser

string url = "http://html-agility-pack/from-browser"; 

var web1 = new HtmlWeb(); 
var doc1 = web1.LoadFromBrowser(url, o => 
{ 
    var webBrowser = (WebBrowser) o; 

    // WAIT until the dynamic text is set 
    return !string.IsNullOrEmpty(webBrowser.Document.GetElementById("uiDynamicText").InnerText); 
}); 
var t1 = doc1.DocumentNode.SelectSingleNode("//div[@id='uiDynamicText']").InnerText 

var web2 = new HtmlWeb(); 
var doc2 = web2.LoadFromBrowser(url, html => 
{ 
    // WAIT until the dynamic text is set 
    return !html.Contains("<div id=\"uiDynamicText\"></div>"); 
}); 
var t2 = doc2.DocumentNode.SelectSingleNode("//div[@id='uiDynamicText']").InnerText 

Console.WriteLine("Text 1: " + t1); 
Console.WriteLine("Text 2: " + t2); 

Sztuką jest, aby znaleźć coś, co mówi, gdy strona jest gotowa, ponieważ jest to niemożliwe dla biblioteki wiedzieć.

+0

Czy próbowałeś to na stronie OP zamieszczono? Nie sądzę, że to zadziała, ponieważ używa leniwego programu ładującego. Strona ładuje się, musisz przewinąć w dół i WTEDY czekać na zakończenie strony ... zobacz moją odpowiedź po więcej szczegółów. – JeffC

+0

@JeffC, nie, nie próbowałem. Jednak ten sam wynik można osiągnąć, ponieważ ma on dostęp do WebBrowser i może korzystać z niektórych interfejsów API, takich jak webBrowser.Document.Window.ScrollTo (0, webBrowser.Document.Body.ScrollRectangle.Height); –