2017-08-29 4 views
1

J'ai écrit le code suivant pour supprimer les informations d'appel d'un portail sur une base quotidienne.Appliquer vs boucle For dans R

packages <- c('rvest', 'stringi', 'tidyverse','lubridate','dplyr') 
purrr::walk(packages, library, character.only = TRUE, warn.conflicts = FALSE) 
start_time <- proc.time() 

Page principale à scrapper et obtenir le nombre total d'enregistrements.

data <- read_html('https://eprocure.gov.in/mmp/latestactivetenders') 
total_tenders_raw <- html_nodes(data,xpath = '//*[(@id = "table")]') 
All_tenders <- data.frame(html_table(total_tenders_raw, header = TRUE)) 
links <- html_nodes(data, xpath='//*[(@id = "table")] | //td | //a') 
links_fair <- html_attr(links,'href') 
links_fair <- links_fair[grep("tendersfullview",links_fair)] 
All_tenders <- cbind(All_tenders,links_fair) 

La lecture du nombre total d'enregistrements à récupérer

Count_of_Recs_raw <- html_nodes(data, xpath = '//*[(@id = "edit-l-active-teners")]//div') 
Count_of_Recs <- as.numeric(gsub("Total Tenders : ","",html_text(Count_of_Recs_raw[1]))) 

Fonctions pour les champs de données nettoyage et de traitement comme les dates et les facteurs.

process_dates <- function(data){ 
    cols2date <- c('Bid.Submission.Closing.Date','epublished_date','document_download_start_date','bid_submission_start_date','bid_opening_date','document_download_end_date','bid_submission_end_date') 
    date_processed_data <- data 
    date_processed_data[cols2date] <- lapply(data[cols2date] , dmy_hm) 
    return(date_processed_data) 
} 

clean_process_data <- function(data){ 
    cols2factor <- c('State.Name','product_category','pre_qualification','organisation_name','organisation_type','tender_type') 
    clean_processed_data <- data 
    clean_processed_data[cols2factor] <- lapply(data[cols2factor] , factor) 
    #clean_processed_data <- process_dates(clean_processed_data) 
    return(clean_processed_data) 

} 

Le code ci-dessous est là précisément ma question est ...

Table déchirage commence ici. La première page a déjà été supprimée pour obtenir la structure de la trame de données.

for (page_no in 2:round(Count_of_Recs/10)){ 
    closeAllConnections() 
    on.exit(closeAllConnections()) 
    url_bit1 <- 'https://eprocure.gov.in/mmp/latestactivetenders/page=' 
    url <- paste(url_bit1, page_no, sep="") 
    cat(page_no,"\t",proc.time() - start_time,"\n") 
    data <- read_html(url) 
    total_tenders_raw <- html_nodes(data,xpath = '//*[(@id = "table")]') 
    Page_tenders <- data.frame(html_table(total_tenders_raw, header = TRUE)) 
    links <- html_nodes(data, xpath='//*[(@id = "table")] | //td | //a') 
    links_fair <- html_attr(links,'href') 
    links_fair <- links_fair[grep("tendersfullview",links_fair)] 
    Page_tenders <- cbind(Page_tenders,links_fair) 
    All_tenders <- rbind(All_tenders,Page_tenders) 
} 

Cette boucle for termine généralement des heures à compléter. Je cherche à utiliser la famille d'application à bon escient afin de gagner du temps. Ce programme a également la responsabilité de récupérer et de traiter tous les enregistrements, puis de réécrire à chaque fois une page entièrement nouvelle (code non répertorié ici) ....

J'ai essayé le code suivant mais il ne le fait pas ne me donnez pas ce que je veux:

url_bit1 <- 'https://eprocure.gov.in/mmp/latestactivetenders/page=' 
read_page <- function(datain){ 
    closeAllConnections() 
    on.exit(closeAllConnections()) 
    url <- paste(url_bit1, datain$S.No., sep="") 
    cat(S.No.,"\t",proc.time() - start_time,"\n") 
    data <- read_html(url) 
    total_tenders_raw <- html_nodes(data,xpath = '//*[(@id = "table")]') 
    Page_tenders <- data.frame(html_table(total_tenders_raw, header = TRUE)) 
    links <- html_nodes(data, xpath='//*[(@id = "table")] | //td | //a') 
    links_fair <- html_attr(links,'href') 
    links_fair <- links_fair[grep("tendersfullview",links_fair)] 
    Page_tenders <- cbind(Page_tenders,links_fair) 
    All_tenders <- rbind(All_tenders,Page_tenders) 
} 

All_tenders <- sapply(All_tenders, FUN=read_page(All_tenders$S.No.)) 

Tout conseil, des conseils, des suggestions, des contributions ou de l'aide est la bienvenue. J'utilise R depuis 3-4 mois seulement. Je suis également conscient des forces de Python dans ce domaine par rapport à R, mais je suis porté vers R pour la solution à ce problème.

Répondre

1

Votre fonction Sapply est incorrecte. J'ai fait quelques modifications sur votre code et l'ai testé sur la taille de l'échantillon N = 50. Nous pouvons utiliser system.time() pour savoir combien de temps il faut pour terminer la tâche.

Le "pour" approche:

system.time(
    for (page_no in 1:50){ 
    closeAllConnections() 
    on.exit(closeAllConnections()) 
    url_bit1 <- 'https://eprocure.gov.in/mmp/latestactivetenders/page=' 
    url <- paste(url_bit1, page_no, sep="") 
    cat(page_no,"\t",proc.time() - start_time,"\n") 
    data <- read_html(url) 
    total_tenders_raw <- html_nodes(data,xpath = '//*[(@id = "table")]') 
    Page_tenders <- data.frame(html_table(total_tenders_raw, header = TRUE)) 
    links <- html_nodes(data, xpath='//*[(@id = "table")] | //td | //a') 
    links_fair <- html_attr(links,'href') 
    links_fair <- links_fair[grep("tendersfullview",links_fair)] 
    Page_tenders <- cbind(Page_tenders,links_fair) 
    All_tenders <- rbind(All_tenders,Page_tenders) 
    } 
) 

#user system elapsed 
# 50.15 81.26 132.73 

L'approche "lapply":

All_tenders = NULL 
url_bit1 <- 'https://eprocure.gov.in/mmp/latestactivetenders/page=' 
read_page <- function(datain){ 
    closeAllConnections() 
    on.exit(closeAllConnections()) 
    url <- paste(url_bit1, datain, sep="") 
    cat(datain,"\t",proc.time() - start_time,"\n") 
    data <- read_html(url) 
    total_tenders_raw <- html_nodes(data,xpath = '//*[(@id = "table")]') 
    Page_tenders <- data.frame(html_table(total_tenders_raw, header = TRUE)) 
    links <- html_nodes(data, xpath='//*[(@id = "table")] | //td | //a') 
    links_fair <- html_attr(links,'href') 
    links_fair <- links_fair[grep("tendersfullview",links_fair)] 
    Page_tenders <- cbind(Page_tenders,links_fair) 
    All_tenders <- rbind(All_tenders,Page_tenders) 
} 

system.time(
    All_tenders <- lapply(1:50, function(x) read_page(x)) 
) 
# user system elapsed 
# 49.84 78.97 131.16 

Si nous voulons mettre nos résultats dans une trame de données, puis de les transformer All_tenders liste à un dataframe comme suit :

All_tenders = do.call(rbind, lapply(All_tenders, data.frame, stringsAsFactors=FALSE) 

S'éteint lapply est légèrement plus rapide.

+0

Je pense que changer 'All_tenders' dans votre fonction le rend vraiment lent ... –

+0

Eh bien, vous devez faire avec. Le scrappage Web n'est pas rapide, sinon vous risquez d'être banni par les administrateurs de serveur. L'approche alternative (beaucoup plus rapide) consisterait à utiliser des TOR et des requêtes multiples provenant de différentes IP via python mais c'est une autre histoire. –

+0

C'était stupide de ma part ..... comme je l'ai mentionné, je suis seulement novice dans R .... Pouvez-vous s'il vous plaît élaborer la partie suivante s'il vous plaît system.time ( All_tenders <- lapply (1:50, fonction (x) read_page (x)) All_tenders = do.call (rbind, lapply (All_tenders, data.frame, stringsAsFactors = FALSE) –

1

for boucles et sapply fonctionne différemment: - for boucles font des choses itérativement: ils font le calcul sur le premier élément, puis sur le deuxième ... - sapply faire des choses sur la liste des éléments indépendamment (et dans l'ordre). Ainsi, les résul- tats sont construits de manière indépendante.

Ainsi, au et de votre boucle, lorsque vous faites:

All_tenders <- rbind(All_tenders,Page_tenders) 

All_tenders Augmentation variable itertively.

Dans votre fonction sapply, cela ne fonctionnera pas (car il ne connaît pas les résultats pour les autres éléments).

Alors vous devez faire quelque chose comme ça:

url_bit1 <- 'https://eprocure.gov.in/mmp/latestactivetenders/page=' 
read_page <- function(datain){ 
    closeAllConnections() 
    on.exit(closeAllConnections()) 
    url <- paste(url_bit1, datain, sep="") 
    cat(S.No.,"\t",proc.time() - start_time,"\n") 
    data <- read_html(url) 
    total_tenders_raw <- html_nodes(data,xpath = '//*[(@id = "table")]') 
    Page_tenders <- data.frame(html_table(total_tenders_raw, header = TRUE)) 
    links <- html_nodes(data, xpath='//*[(@id = "table")] | //td | //a') 
    links_fair <- html_attr(links,'href') 
    links_fair <- links_fair[grep("tendersfullview",links_fair)] 
    Page_tenders <- cbind(Page_tenders,links_fair) 
    return(Page_tenders) 
} 

Pour retourner un résultat pour chaque page et l'appliquer de la manière suivante:

All_tenders_tmp <- sapply(2:round(Count_of_Recs/10), FUN=read_page) 

Ensuite, votre résultat sera la liste de tous les résultats et vous pouvez le fusionner avec data.table::rbindlist par exemple.

J'espère que j'étais clair.

+0

Btw, cette fonction va lancer une erreur comme "page_no", "S.No." les variables ne sont pas définies. –

+0

les deux variables sont des cols de la dataframe datain –

+0

Celui-ci ne fonctionne toujours pas pour moi .. Pour cette raison, je suis enclin à choisir la réponse précédente comme la bonne à partir de maintenant .. –