2014-08-28 10 views
6

Sto eseguendo una raschiatura del sito web di questo corso e mi chiedo se c'è un modo più veloce per raschiare la pagina una volta che l'ho messa in beautifulsoup. Ci vuole molto più tempo di quanto mi sarei aspettato.Accelerazione di beautifulsoup

Suggerimenti?

from selenium import webdriver 
from selenium.webdriver.common.keys import Keys 
from selenium.webdriver.support.ui import Select 
from selenium.webdriver.support import expected_conditions as EC 

from bs4 import BeautifulSoup 

driver = webdriver.PhantomJS() 
driver.implicitly_wait(10) # seconds 
driver.get("https://acadinfo.wustl.edu/Courselistings/Semester/Search.aspx") 
select = Select(driver.find_element_by_name("ctl00$Body$ddlSchool")) 

parsedClasses = {} 

for i in range(len(select.options)): 
    print i 
    select = Select(driver.find_element_by_name("ctl00$Body$ddlSchool")) 
    select.options[i].click() 
    upperLevelClassButton = driver.find_element_by_id("Body_Level500") 
    upperLevelClassButton.click() 
    driver.find_element_by_name("ctl00$Body$ctl15").click() 

    soup = BeautifulSoup(driver.page_source, "lxml") 

    courses = soup.select(".CrsOpen") 
    for course in courses: 
     courseName = course.find_next(class_="ResultTable")["id"][13:] 
     parsedClasses[courseName] = [] 
     print courseName 
     for section in course.select(".SecOpen"): 
      classInfo = section.find_all_next(class_="ItemRowCenter") 
      parsedClasses[courseName].append((int(classInfo[0].string), int(classInfo[1].string), int(classInfo[2].string))) 

print parsedClasses 
print parsedClasses['FL2014' + 'A46' + '3284'] 

driver.quit() 
+0

uso 'lxml' direttamente (con le espressioni XPath) se vi preoccupate per la velocità. – roippi

+0

Si tratta essenzialmente di una richiesta di revisione del codice e quindi non adatta per SO. C'è un sito nella rete SE dedicato alle revisioni del codice: http://codereview.stackexchange.com/ – Louis

+1

@Louis Non sono d'accordo, si tratta anche di modi alternativi per ottenere i dati dai risultati della ricerca. Non si tratta solo del codice e della sua qualità. – alecxe

risposta

10

Va bene, si può veramente accelerare questo da:

Dal questo è il modulo generato da ASP.NET e, grazie alle sue funzioni di sicurezza, le cose si complicano leggermente. Ecco il codice completo, non abbiate paura di esso - non ho commenti aggiunto e aperto alle domande:

import re 
from bs4 import BeautifulSoup, SoupStrainer 
import requests 

# start session and get the search page 
session = requests.Session() 
response = session.get('https://acadinfo.wustl.edu/Courselistings/Semester/Search.aspx') 

# parse the search page using SoupStrainer and lxml 
strainer = SoupStrainer('form', attrs={'id': 'form1'}) 
soup = BeautifulSoup(response.content, 'lxml', parse_only=strainer) 

# get the view state, event target and validation values 
viewstate = soup.find('input', id='__VIEWSTATE').get('value') 
eventvalidation = soup.find('input', id='__EVENTVALIDATION').get('value') 
search_button = soup.find('input', value='Search') 
event_target = re.search(r"__doPostBack\('(.*?)'", search_button.get('onclick')).group(1) 

# configure post request parameters 
data = { 
    '__EVENTTARGET': event_target, 
    '__EVENTARGUMENT': '', 
    '__LASTFOCUS': '', 
    '__VIEWSTATE': viewstate, 
    '__EVENTVALIDATION': eventvalidation, 
    'ctl00$Body$ddlSemester': '201405', 
    'ctl00$Body$ddlSession': '', 
    'ctl00$Body$ddlDept': '%', 
    'ctl00$Body$ddlAttributes': '0', 
    'ctl00$Body$Days': 'rbAnyDay', 
    'ctl00$Body$Time': 'rbAnyTime', 
    'ctl00$Body$cbMorning': 'on', 
    'ctl00$Body$cbAfternoon': 'on', 
    'ctl00$Body$cbEvening': 'on', 
    'ctl00$Body$tbStart': '9:00am', 
    'ctl00$Body$tbEnds': '5:00pm', 
    'ctl00$Body$ddlUnits': '0', 
    'ctl00$Body$cbHideIStudy': 'on', 
    'ctl00$Body$courseList$hidHoverShow': 'Y', 
    'ctl00$Body$courseList$hidDeptBarCnt': '', 
    'ctl00$Body$courseList$hidSiteURL': 'https://acadinfo.wustl.edu/Courselistings', 
    'ctl00$Body$courseList$hidExpandDetail': '', 
    'ctl00$Body$hidDay': ',1,2,3,4,5,6,7', 
    'ctl00$Body$hidLevel': '1234', 
    'ctl00$Body$hidDefLevel': '' 
} 

# get the list of options 
strainer = SoupStrainer('div', attrs={'id': 'Body_courseList_tabSelect'}) 
options = soup.select('#Body_ddlSchool > option') 
for option in options: 
    print "Processing {option} ...".format(option=option.text) 

    data['ctl00$Body$ddlSchool'] = option.get('value') 

    # make the search post request for a particular option 
    response = session.post('https://acadinfo.wustl.edu/Courselistings/Semester/Search.aspx', 
          data=data) 
    result_soup = BeautifulSoup(response.content, parse_only=strainer) 
    print [item.text[:20].replace('&nbsp', ' ') + '...' for item in result_soup.select('div.CrsOpen')] 

Stampe:

Processing Architecture ... 
[u'A46 ARCH 100...', u'A46 ARCH 111...', u'A46 ARCH 209...', u'A46 ARCH 211...', u'A46 ARCH 266...', u'A46 ARCH 305...', u'A46 ARCH 311...', u'A46 ARCH 323...', u'A46 ARCH 328...', u'A46 ARCH 336...', u'A46 ARCH 343...', u'A46 ARCH 350...', u'A46 ARCH 355...', u'A46 ARCH 411...', u'A46 ARCH 422...', u'A46 ARCH 428...', u'A46 ARCH 436...', u'A46 ARCH 445...', u'A46 ARCH 447...', u'A46 ARCH 465...', u'A48 LAND 451...', u'A48 LAND 453...', u'A48 LAND 461...'] 
Processing Art ... 
[u'F10 ART 1052...', u'F10 ART 1073...', u'F10 ART 213A...', u'F10 ART 215A...', u'F10 ART 217B...', u'F10 ART 221A...', u'F10 ART 231I...', u'F10 ART 241D...', u'F10 ART 283T...', u'F10 ART 301A...', u'F10 ART 311E...', u'F10 ART 313D...', u'F10 ART 315B...', u'F10 ART 317H...', u'F10 ART 323A...', u'F10 ART 323B...', u'F10 ART 323C...', u'F10 ART 329C...', u'F10 ART 337E...', u'F10 ART 337F...', u'F10 ART 337H...', u'F10 ART 385A...', u'F10 ART 391M...', u'F10 ART 401A...', u'F10 ART 411E...', u'F10 ART 413D...', u'F10 ART 415B...', u'F10 ART 417H...', u'F10 ART 423A...', u'F10 ART 423B...', u'F10 ART 423C...', u'F10 ART 429C...', u'F10 ART 433C...', u'F10 ART 433D...', u'F10 ART 433E...', u'F10 ART 433K...', u'F10 ART 461C...', u'F10 ART 485A...', u'F20 ART 111P...', u'F20 ART 115P...', u'F20 ART 1186...', u'F20 ART 119C...', u'F20 ART 127A...', u'F20 ART 133B...', u'F20 ART 135G...', u'F20 ART 135I...', u'F20 ART 135J...', u'F20 ART 1361...', u'F20 ART 1363...', u'F20 ART 1713...', u'F20 ART 219C...', u'F20 ART 2363...', u'F20 ART 2661...', u'F20 ART 281S...', u'F20 ART 311P...', u'F20 ART 315P...', u'F20 ART 3183...', u'F20 ART 333B...', u'F20 ART 335A...', u'F20 ART 335J...', u'F20 ART 3713...', u'F20 ART 381S...', u'F20 ART 415P...', u'F20 ART 435I...'] 
... 

ci sono certamente cose da migliorare qui, come, hai hardcoded gli altri valori del modulo - dovresti probabilmente analizzare i valori possibili e impostarli in modo appropriato.

Un altro miglioramento potrebbe essere quello di collegare questo fino a grequests:

GRequests consente di utilizzare le richieste con Gevent per rendere asincroni richieste HTTP facilmente.


Come si può vedere, quando si è al livello più alto e interagire con un browser attraverso il WebDriver - non siete preoccupati per le richieste effettive prossimi al server per ottenere i dati. Ciò lo rende facile da automatizzare, ma può essere dolorosamente lento. Quando si scende all'automazione di basso livello, si hanno più opzioni per accelerare le cose, ma la complessità dell'implementazione aumenta molto velocemente. Inoltre, pensa a quanto può essere affidabile questo tipo di soluzione. Quindi si può aderire alla soluzione "black-box" e rimanere con selenium?


Ho anche provato a risolvere il problema utilizzando:

ma non è riuscito a causa di motivi diversi (grado di fornire con i messaggi di errore rilevanti). Tuttavia, tutti questi 3 strumenti dovrebbero aver contribuito a semplificare la soluzione.

vedere anche le discussioni simili:

+0

Wow! Quindi, sì, una delle ragioni per cui sono andato con Selenium è perché sentivo che potevano cambiare il sito web in piccola quantità e rompere il mio script incredibilmente facilmente ... ma riconosco anche quanto sia lento eseguire un browser fantasma. Stavo anche tagliando felicemente e pensavo che sarebbe stato più semplice saltare tutti i dati dei post perché il sito sembrava una specie di casino. Come hai capito quali campi dovevano essere popolati? – tbondwilkinson

+0

@tbondwilkinson esattamente. Ho usato gli strumenti per gli sviluppatori di browser forniti da Chrome. In caso di Firefox, puoi usare firebug. Praticamente deve avere per il web-scraping. – alecxe