2013-10-20 9 views
7

Próbuję zeskrobać dużą liczbę stron internetowych, aby później je przeanalizować. Ponieważ liczba adresów URL jest ogromna, zdecydowałem się użyć pakietu parallel wraz z XML.Używanie równoległości do skrobania stron internetowych za pomocą R

Specjalnie używam funkcji htmlParse() z XML, który działa dobrze, gdy używana z sapply, ale generuje pustych obiektów klasy HTMLInternalDocument gdy używana z parSapply.

url1<- "http://forums.philosophyforums.com/threads/senses-of-truth-63636.html" 
url2<- "http://forums.philosophyforums.com/threads/the-limits-of-my-language-impossibly-mean-the-limits-of-my-world-62183.html" 
url3<- "http://forums.philosophyforums.com/threads/how-language-models-reality-63487.html" 

myFunction<- function(x){ 
cl<- makeCluster(getOption("cl.cores",detectCores())) 
ok<- parSapply(cl=cl,X=x,FUN=htmlParse) 
return(ok) 
} 

urls<- c(url1,url2,url3) 

#Works 
output1<- sapply(urls,function(x)htmlParse(x)) 
str(output1[[1]]) 
> Classes 'HTMLInternalDocument', 'HTMLInternalDocument', 'XMLInternalDocument', 'XMLAbstractDocument', 'oldClass' <externalptr> 
output1[[1]] 


#Doesn't work 
myFunction<- function(x){ 
cl<- makeCluster(getOption("cl.cores",detectCores())) 
ok<- parSapply(cl=cl,X=x,FUN=htmlParse) 
stopCluster(cl) 
return(ok) 
} 

output2<- myFunction(urls) 
str(output2[[1]]) 
> Classes 'HTMLInternalDocument', 'HTMLInternalDocument', 'XMLInternalDocument', 'XMLAbstractDocument', 'oldClass' <externalptr> 
output2[[1]] 
#empty 

Dzięki.

+3

ktoś bardziej kompetentny, miejmy nadzieję, kurant, ale moja intuicja jest parallelizing to (jak obecnie zaprojektowany) może nie być tak skuteczny, ponieważ wywołujesz strony bezpośrednio w 'htmlParse' i wszystkie twoje rdzenie prawdopodobnie dzielą jedno połączenie z internetem. Możesz zajrzeć do 'RCurl' dla [asynchroniczne pliki do pobrania, które są rzekomo bardziej wydajne] (http://www.inside-r.org/packages/cran/RCurl/docs/getURIAsynchronous). – Thomas

+0

@Thomas Dzięki. Tak jak w poprzednich pytaniach, które mi pomogłeś, przyjmuję Twoje sugestie/komentarze. Zajrzę też do RCurla. –

+2

Należy również pamiętać, że jeśli twoje własne taśmy nie zajmą tak dużo czasu (zamówienie ms), obciążenie związane z równoległością spowoduje, że potrwa to dłużej niż zwykłe przetwarzanie w serii. –

Odpowiedz

9

Możesz użyć getURIAsynchronous z pakietu Rcurl, który pozwala dzwoniącemu określić wiele identyfikatorów URI do pobrania w tym samym czasie.

library(RCurl) 
library(XML) 
get.asynch <- function(urls){ 
    txt <- getURIAsynchronous(urls) 
    ## this part can be easily parallelized 
    ## I am juste using lapply here as first attempt 
    res <- lapply(txt,function(x){ 
    doc <- htmlParse(x,asText=TRUE) 
    xpathSApply(doc,"/html/body/h2[2]",xmlValue) 
    }) 
} 

get.synch <- function(urls){ 
    lapply(urls,function(x){ 
    doc <- htmlParse(x) 
    res2 <- xpathSApply(doc,"/html/body/h2[2]",xmlValue) 
    res2 
    })} 

Oto niektóre benchmarking do 100 adresów URL, które dzielą czas parsowania o czynnik 2.

library(microbenchmark) 
uris = c("http://www.omegahat.org/RCurl/index.html") 
urls <- replicate(100,uris) 
microbenchmark(get.asynch(urls),get.synch(urls),times=1) 

Unit: seconds 
      expr  min  lq median  uq  max neval 
get.asynch(urls) 22.53783 22.53783 22.53783 22.53783 22.53783  1 
    get.synch(urls) 39.50615 39.50615 39.50615 39.50615 39.50615  1 
+0

+1 dziękuję za wprowadzenie do tej funkcji! –