Prehľadávanie HTML dokumentov a Java

Xgamer8

Prehľadávanie HTML dokumentov a Java
« kdy: 06. 01. 2012, 17:10:44 »
Dobrý deň,

učim sa pracovať v jave s viacerymi vlaknami, a tak som si vymyslel take zadanie, že pomocou knihovne Jsoup zparsujem html stranku a vytiahnem z nej všetky odkazy. Tieto odkazy uložim do fronty. Podla toho či bol už dosiahnuty počet odkazov tak sa odkazy pridavaju. Pre lepšiu efektivitu chcem načitavať stranky vo viacerych vlaknach. Na to použivam Thread Pool.

Ako frontu použivam ArrayBlockingQueue. Problem je v tom, že mi to nefunguje tak ako by malo. ExecutorService sice spustí vlakno ale neprida do fronty žiadne nové odkazy ale pokial vo vnutornej triede CollectThread zavolam v konštruktore metodu run() tak to funguje ale vytvoria sa duplicitne zaznamy( a volat v konštruktore run() je aj tak asi blbosť nie?:) ) Keď ju tam nevolam tak to funguje tak že tam prida niektore odkazy, inak je tam proste null.

Snažim sa prist na to prečo to má take správanie, napadá ma iba to že je to nejako blbo sychronizovane ale nejako si v tom neviem najst chybu a opraviť ju.  Preto by som chcel poprosiť niekoho kto sa rozumie problematike aby sa na to pozrel a poradil mi kde robim chybu a hlavne prečo mi to nefunguje tak ako si to prestavujem:). Ďakujem 

Tu je kod:
Kód: [Vybrat]
package collector;

import java.io.IOException;
import java.util.concurrent.*;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Collector {
private String url;
private int links; //predanie parametru konštruktora
private LinksCounter clinks; //count links
private ArrayBlockingQueue<String> LinksList;

public Collector(String baseURL, int Nlinks){
this.url = baseURL;
this.links = Nlinks;
this.clinks = new LinksCounter(0);

this.LinksList = new ArrayBlockingQueue<String>(links);
try {
LinksList.add(url);
CollectFromWeb();

} catch (InterruptedException e) {
e.printStackTrace();
}

}
private synchronized void GetLinksList(Document html) throws InterruptedException{
Elements collectedLinks = html.select("a[href]");
for(Element link:collectedLinks){
if(clinks.get() == links) break;
else{
String current = link.attr("abs:href");
if(!current.equals(url) && current.startsWith(url) && !current.contains("#")){
LinksList.put(current);
clinks.up();
}
}
}
}
private void CollectFromWeb() throws InterruptedException{
ExecutorService executor = Executors.newFixedThreadPool(4);
for(int i = 0; i < links; i++){

CollectThread worker = new CollectThread(LinksList.take());
executor.execute(worker);

System.out.println("Collecting from "+LinksList.peek());
}
executor.shutdown();
System.out.println(clinks.get());
}
public void WriteOutQueue(){
while(!LinksList.isEmpty()){
System.out.println(LinksList.poll());
}
System.out.println(clinks.get());
}

public void DuplicityTester(){}
class CollectThread implements Runnable{
private String url;
public CollectThread(String url) {
this.url = url;
}
@Override
public void run() {
//ak je potrebova zbierame linky
if(clinks.get() != links){
try {
Document html = Jsoup.connect(url).get();
GetLinksList(html);
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName());
}

}

class  LinksCounter{
private int count;

public LinksCounter(int num){
this.count = num;
}
public synchronized int get(){
return this.count;
}

public synchronized void up(){
this.count++;
}
}
}
« Poslední změna: 08. 01. 2012, 16:27:04 od Petr Krčmář »


jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #1 kdy: 06. 01. 2012, 19:24:10 »
Nechybi ti tam nejaky cyklus v run() ? Takhle vytvoris links vlaken, v kazdem zpracujes jeden odkaz a koncis.

Xgamer8

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #2 kdy: 06. 01. 2012, 19:33:47 »
To rieši metoda GetLinksList() a tam je foreach cyklus ktory prechádza jednotlive Elements(datový typ Jsoup knihovne).

jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #3 kdy: 06. 01. 2012, 20:06:07 »
No to neresi. Ta metoda nevytvari nova vlakna, jen pridava linky. Kazde vlakno zpracuje jeden link a konec.

jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #4 kdy: 06. 01. 2012, 20:09:57 »
A jestli to dobre chapu, tak je tam chyba v navrhu v tom, ze kod co se ma vykonavat vsemi vlakny(private synchronized void GetLinksList) je synchronizovan. Takze ho bykonava vzdy jen jedno vlakno, protoze mas jen jednu instanci Collectoru


Xgamer8

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #5 kdy: 06. 01. 2012, 20:20:08 »
Ako chyba v navrhu je dost možna... kedže sa s threadmi len zoznamujem.. .ale ked je to synchronizovat to znamena že v jeden okamih tam má pristup len jedno vlakno nie? takže ked si vlakno dokonči svoju robotu zase do toho može pristupit nove vlakno. No ale skušal som to aj tak že to tam nemal metodu, že som mal obsah metody priamo v rune ale aj tak to nerobilo to čo malo ...:(

jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #6 kdy: 06. 01. 2012, 20:30:43 »
Me ten kod neni moc jasny. Promenna links je cilovy pocet odkazu, ktere chces najit? Z kodu to vypada, ze to je vlastne pocet vlaken, ktere spustis. No a protoze v tom run() nemas zadny cyklus, tak je to taky pocet linku, ktere projdes


Xgamer8

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #7 kdy: 06. 01. 2012, 20:54:42 »
links je počet dokumentov ktore sa maju spracovať. Moja povodna idea bola uplne jednoducha. Použijem 4 vlakna ktoré sa budu znovu používať až dovtedy pokial nebude dosiahnuty počet požadovaných dokumentov. Každý thread sa bude starat o jeden dokument. Pri realizácii som sa však v tom asi už stratil. K tomu cyklu v rune to tam mam ako dat cyklus while( pocet linkov != pocet pozadovanych linkov)? Ved keby je to tak tak to bude stále len v jednom tom threade nie? Čiže všetko vykoná jeden thread sam?. 

jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #8 kdy: 06. 01. 2012, 21:08:44 »
Ty ty vlakna ale nevytvoris ctyri, ale vytvoris jich tolik, kolik je hodnota links. Ten Executor pak akorat zaridi, ze z te kupy vlaken najednou pobezi jen ctyri. Fatalni chyba je tady ale predevsim v nepochopeni Runnable a run(). Vykonavani vlakna skonci tehdy, kdyz skonci metoda run().

jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #9 kdy: 06. 01. 2012, 22:05:40 »
Trochu jsem prepsal ten tvuj zamer do funkcniho programu. Staci spustit
Kód: [Vybrat]
package collector;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;

public class Collector {
    static final ArrayBlockingQueue<String> unprocessedLinks = new ArrayBlockingQueue<String>(100);
    static final Set<String> processedLinks = new HashSet<String>();   
    static int runningWorkers;
    static int lastWorkerHit;
    final int threadCount=4;
    final int targetCount;


    public Collector(String baseURL, int targetCount) throws InterruptedException {
        unprocessedLinks.add(baseURL);
        this.targetCount=targetCount;
    }
   
    public void start(){
        for (int i = 0; i < threadCount; i++)
            new Thread(new Worker(i)).start();
        new Thread(new StatusWorker()).start();
    }

    private class Worker implements Runnable {
        private int workerNumber;

        public Worker(int workerNumber) {
            runningWorkers += 1;
            this.workerNumber = workerNumber+1;
        }

        @Override
        public void run() {
            String currentLink;
            System.out.println("Worker #"+workerNumber+" started");
            while (processedLinks.size()<targetCount) {
                try {
                    currentLink=unprocessedLinks.take();
                    processedLinks.add(currentLink);
                    Document html = Jsoup.connect(currentLink).get();
                    Elements collectedLinks = html.select("a[href]");
                    for (Element element : collectedLinks) {
                        String newLink = element.attr("abs:href");
                        if (!newLink.contains("#") && !processedLinks.contains(newLink))
                            unprocessedLinks.add(newLink);
                    }
                    lastWorkerHit=workerNumber;
                } catch (Exception e) {
                    //who cares? We've got plenty of links
                }
            }
            runningWorkers--;
            System.out.println("Worker #"+workerNumber+" stopped");
        }
    }

    private static class StatusWorker implements Runnable {
        @Override
        public void run() {
            while (runningWorkers > 0) {
                System.out.println("Processed:" + processedLinks.size() + " Unprocessed:" + unprocessedLinks.size()+" Last link processed by worker #"+lastWorkerHit);
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                }
            }
            System.out.println("StatusWorker stopped");
            System.out.println("=====PROCESSED LINKS=====");
            for (String link: processedLinks)
                System.out.println(link);
        }
    }
   
    public static void main(String[] args)throws Exception{
        Collector c = new Collector("http://www.root.cz", 100);
        c.start();
    }
}

P.S. Doufam, ze to neni domaci ukol a ze nejsi ten bezradnej moravskej zabijak Googlu

Xgamer8

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #10 kdy: 06. 01. 2012, 22:23:21 »
Neni to ukol :) Snažim sa to len pochopiť:) Má to služit ako sample ku lušteniu monoalfabeticky a polyalfabetickych šifier. Tento zberač sa má starat o ziskanie frekvenčnej analyzy znakov z webu. Ďakujem:)

PS: zabijam s tým cely deň:D a vy ste to za hodinu spravili, klobuk dolu :)

jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #11 kdy: 06. 01. 2012, 22:30:00 »
No popravde jsem se spis zasek na tom, kolik zabavy si s tim uziju..
Jeste to podstatne vylepsuju(uz davam cca 500 linku za vterinu) a zkousim ruzne blbiny. Treba za jak dlouho najdu link obsahujici "porn", kdyz zacnu na ruznych strankach :D
P.S. Root si stoji docela dobre:)

Xgamer8

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #12 kdy: 06. 01. 2012, 22:32:33 »
:D Hej ma to všeliake využitie:)

jehovista

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #13 kdy: 06. 01. 2012, 22:37:02 »
Nevis, co mam dat misto tohohle
Kód: [Vybrat]
html.select("a[href]"); abych dostal emailovy adresy?  ;D

Xgamer8

Re:Prehľadávanie html dokumentov, ziskavanie odkazov, java
« Odpověď #14 kdy: 06. 01. 2012, 23:08:54 »
Tak to veru neviem :D