[C#] Źródło strony a jQuery


(Marcin Obala) #1

Witam

Chciałbym ściągnąć do pliku źródło strony jednak jest w nim dużo pustych linijek jednak kopiując źródło np. z Opery sekcje te są wypełnione. Szukam i szukam i wyczytałem że to ma związek z jQuery gdzie kod jest wstrzykiwany w źródło. A ja potrzebuje czegoś z tego wstrzykniętego kodu. Jest jakiś silnik jQuery (ajaxa) na C#?


(Mirek Sztramski) #2

JS jest interpretowany po stronie klienta przez silnik przeglądarki. Możesz pójść dwiema drogami:

  1. Wykorzystaj klasę WebBrowser (silnik IE): pobierz dokument, poczekaj na wykonanie skryptu, pobierz dane przemierzając DOM.

  2. Wykorzystaj klasę WebClient i Javascript .NET lub podobny (http://javascriptdotnet.codeplex.com/): pobierz dokument, wykonaj skrypt ręcznie, zmodyfikuj DOM.


(Marcin Obala) #3

Co do rozwiązania numer 1. Robię tak:

webBrowser1.Navigate(ListaLinkow[i]);

Thread.Sleep(5000);

source = wb.Document.All[0].InnerHtml.ToString();

I dostaję błąd

Jednak jeśli zrobię tak:

private void button4_Click(object sender, EventArgs e)

{

      webBrowser1.Navigate(textBox2.Text);

}


private void button5_Click(object sender, EventArgs e)

{

    richTextBox1.Text = webBrowser1.Document.All[0].InnerHtml;

}

To kod pobiera bez problemu. Dodatkowo ustawiłem duży Timeout w wątku, klikam przycisk i pojawia mi się kod w richtextBox. Wątek przechodzi TimeOut, próbuje dostać się do kodu strony i nie może mimo że przed spod przycisku to działało. Spróbowałem zrobić PerformClick z wątku ale to się nie udało. Zrobiłem więc funkcję.

public void ButtonClick(Button b)

        {

            if (InvokeRequired)

                this.Invoke(new Action(ButtonClick), new object[] { b });

            else

                b.PerformClick();


        }

I w wątku ją wywołałem

ButtonClick(button5);

I zapisuje mi do zmiennej i mogę wyciągnąć dane. Jednak to są duże kombinacje.


(Mirek Sztramski) #4

@Marcin511

:slight_smile:

Nie możesz pobierać danych w taki sposób, ponieważ nie wiesz, w którym momencie dokument zostanie pobrany przez obiekt klasy WebBrowser. Metoda Navigate dopiero rozpoczyna nawigację do wskazanego adresu (równie dobrze dokument może zostać pobrany za 30 sekund).

Zgodnie z MSDN:

Musisz więc wykorzystać zdarzenie DocumentCompleted. Będziesz miał pewność, że dokument zostanie do końca (w ogóle) wczytany. W zdarzeniu tym umieść kod ( = ...InnerHtml).

Ponieważ metoda Navigate jest metodą asynchroniczną to obsługę zdarzenia DocumentCompleted musisz zaimplementować z głową, tak aby wczytać każdy dokument z ListyLinkow.

Poniższy kod nie wystarczy:

foreach (string link in ListaLinkow) { wb.Navigate(link); }

Jeżeli nie będziesz już miał pomysłu, to daj mi znać.


(Marcin Obala) #5

Dlatego napisałem że daje opóźnienie na wczytanie. Daję Navigate, czekam minutę i nadal błąd cast is not vaild mimo że strona dawno się załadowała i przycisk z głównego wątku, nie stworzonego przeze mnie potrafi odczytać źródło.


(Mirek Sztramski) #6

Błąd "Specified cast is not valid" informuje, że nie można pewnego typu rzutować (przekształcić) w inny. Nic więcej. Jeżeli potrzebujesz tylko źródła dokumentu, to czemu nie wykorzystasz pola WebBrowser.DocumentText (oczekujesz przecież typu "string")?

Ponadto nie należy wykorzystywać metody Sleep na wątkach, ponieważ wątki te nie będą przetwarzać zdarzeń systemowych (jakichkolwiek zdarzeń). Metodą Sleep informujesz system operacyjny, aby nie przydzielał czasu procesora danemu wątkowi. Gdyby metoda WebBrowser.Navigate była metodą synchroniczną, to uniemożliwiłbyś jej wykonanie zadania. Nie tędy droga. Wykorzystaj zdarzenie DocumentCompleted - jeżeli ktoś robi to inaczej, robi to błędnie.


(Marcin Obala) #7

Ok, rozumiem, po prostu moje założenie było błędne bo chciałem załadować stronę a później wczytać od razu kod w jednym wątku. Rozumiem że mam zrobić to tak:

Przycisk start->pobranie listy adresów z pliku i wpuszczenie pierwszego linku do Navigate.

Czekać na zdarzenie document complete, pobrać dane, zrobić z nimi co chce i wpuścić kolejny link do navigate

Jak wyżej

jak wyżej

..


(Mirek Sztramski) #8

Tak, dokładnie tak.

Masz tylko mały problem: W jaki sposób poczekać w pętli wybierającej linki na zdarzenie DocumentCompleted? Metoda Navigate jest wykonywana asynchronicznie, czyli tuż po wywołaniu metody sterowanie powraca do miejsca, w którym wywołałeś Navigate i program wykonuje kolejną instrukcję bez oczekiwania na wynik/załadowanie strony.

Rozwiązanie: ManualResetEventSlim lub podobne

Utwórz obiekt klasy ManualResetEventSlim widoczny w całej klasie. W metodzie, w której przechodzisz do adresów pod linkami wykorzystaj podobne rozwiązanie:

foreach (string link in ListaLinkow)

{

    wb.Navigate(link);

    resetSlim.Wait();

}

Natomiast w zdarzeniu DocumentCompleted na samym końcu dodaj resetSlim.Set(). Dzięki temu poinformujesz wątek, który oczekuje w pętli na sygnał, aby kontynuował swoją pracę (czyli pobrał kolejny link i załadował stronę itd.).


(Marcin Obala) #9

Ja miałem trochę inną koncepcję. Lista linków będzie globalna i sama funkcja wywołana przy zdarzeniu DocumentCompleted będzie wysyłała do webbrowsera kolejny link

void StartButtonClick()

{

    wb.Navigate(listaLinkow[0]);

    nastepny_link=1;

}

void DocumentCompleted()

{

    string zrodlo = wb.DocumentText; // wb.Document.All[0].InnerHtml;

    /*cos tam przerabiam

    ...

    */

    if(nastepny_link >= lista_linkow.Count)

        return;


    wb.Navigate(lista_linkow[nastepny_link]);

    nastepny_link++;


}

-- Dodane 24.10.2012 (Śr) 12:18 --

Aktualnie testuje i jak wyciągam źródło po zdarzeniu DocumentCompleted to nie ma w nim części danych. Czy to DocumentText czy innerHtml.

Jeszcze zauważyłem że zdarzenie DocumentCompleted wykonuje się dużo razy więcej niż jest linków.

Pierwsza liczba to numer kolejny zdarzenia DocumentCompleted, obok e.Url


(Mirek Sztramski) #10

Może w adresie są niedozwolone znaki. Aby znaleźć przyczynę błędu należy poza danymi wyjściowymi posiadać także te wejściowe.

Wszelkie dostępne informacje opisujące zachowanie się klasy WebBrowser, jej metod, zdarzeń i pól opisuje MSDN. Czy kod JavaScript został wykonany przed zdarzeniem DocumentCompleted, jest wykonywany w trakcie obsługi tego zdarzenia, czy też zostanie wykonany trochę później (po zdarzeniu) -> to musisz koniecznie sprawdzić.

Prędzej czy później Twój adres IP zostanie (może zostać) zablokowany przez Amazon za próbę dostępu do treści niedozwolonymi metodami, więc nie wykonuj swojego kodu zbyt często.


(Marcin Obala) #11

Z tego co widzę testując na żywo to zdarzenie DocumentCompleted występuje kilka razy dla jednej strony. JS + jakieś dodatkowe skrypty reklamowe czy statystyczne.