2009-09-26 19 views

risposta

25

Come una visione d'insieme, è necessario svolgere quattro compiti principali:

  • inviare la richiesta (s) al sito web,
  • per recuperare la risposta (s) dal sito
  • per analizzare queste risposte
  • avere un po 'di logica per scorrere nei compiti di cui sopra, con i parametri associati con la navigazione (alle pagine "Avanti" nella lista dei risultati)

La gestione della richiesta e della risposta http viene eseguita con metodi e classi dalla libreria standard di Python urllib e urllib2. L'analisi delle pagine html può essere fatto con HTMLParser o con altri moduli, come Beautiful Soup

Il seguente frammento della libreria standard di Python dimostra la richiesta e la ricezione di una ricerca presso il sito indicato nella domanda.Questo sito è gestito da ASP e, di conseguenza, è necessario assicurarsi di inviare diversi campi modulo, alcuni dei quali con valori "orribili", in quanto vengono utilizzati dalla logica ASP per mantenere lo stato e per autenticare la richiesta in una certa misura. Effettivamente presentando. Le richieste devono essere inviate con il metodo http POST poiché questo è quello che ci si aspetta da questa applicazione ASP. La difficoltà principale consiste nell'identificare il campo modulo e i valori associati che ASP si aspetta (ottenere le pagine con Python è la parte facile).

Questo codice è funzionale, o più precisamente, era funzionale, fino a quando non ho rimosso la maggior parte del valore VSTATE e possibilmente introdotto un errore di battitura o due aggiungendo commenti.

import urllib 
import urllib2 

uri = 'http://legistar.council.nyc.gov/Legislation.aspx' 

#the http headers are useful to simulate a particular browser (some sites deny 
#access to non-browsers (bots, etc.) 
#also needed to pass the content type. 
headers = { 
    'HTTP_USER_AGENT': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.13) Gecko/2009073022 Firefox/3.0.13', 
    'HTTP_ACCEPT': 'text/html,application/xhtml+xml,application/xml; q=0.9,*/*; q=0.8', 
    'Content-Type': 'application/x-www-form-urlencoded' 
} 

# we group the form fields and their values in a list (any 
# iterable, actually) of name-value tuples. This helps 
# with clarity and also makes it easy to later encoding of them. 

formFields = (
    # the viewstate is actualy 800+ characters in length! I truncated it 
    # for this sample code. It can be lifted from the first page 
    # obtained from the site. It may be ok to hardcode this value, or 
    # it may have to be refreshed each time/each day, by essentially 
    # running an extra page request and parse, for this specific value. 
    (r'__VSTATE', r'7TzretNIlrZiKb7EOB3AQE ... ...2qd6g5xD8CGXm5EftXtNPt+H8B'), 

    # following are more of these ASP form fields 
    (r'__VIEWSTATE', r''), 
    (r'__EVENTVALIDATION', r'/wEWDwL+raDpAgKnpt8nAs3q+pQOAs3q/pQOAs3qgpUOAs3qhpUOAoPE36ANAve684YCAoOs79EIAoOs89EIAoOs99EIAoOs39EIAoOs49EIAoOs09EIAoSs99EI6IQ74SEV9n4XbtWm1rEbB6Ic3/M='), 
    (r'ctl00_RadScriptManager1_HiddenField', ''), 
    (r'ctl00_tabTop_ClientState', ''), 
    (r'ctl00_ContentPlaceHolder1_menuMain_ClientState', ''), 
    (r'ctl00_ContentPlaceHolder1_gridMain_ClientState', ''), 

    #but then we come to fields of interest: the search 
    #criteria the collections to search from etc. 
                 # Check boxes 
    (r'ctl00$ContentPlaceHolder1$chkOptions$0', 'on'), # file number 
    (r'ctl00$ContentPlaceHolder1$chkOptions$1', 'on'), # Legislative text 
    (r'ctl00$ContentPlaceHolder1$chkOptions$2', 'on'), # attachement 
                 # etc. (not all listed) 
    (r'ctl00$ContentPlaceHolder1$txtSearch', 'york'), # Search text 
    (r'ctl00$ContentPlaceHolder1$lstYears', 'All Years'), # Years to include 
    (r'ctl00$ContentPlaceHolder1$lstTypeBasic', 'All Types'), #types to include 
    (r'ctl00$ContentPlaceHolder1$btnSearch', 'Search Legislation') # Search button itself 
) 

# these have to be encoded  
encodedFields = urllib.urlencode(formFields) 

req = urllib2.Request(uri, encodedFields, headers) 
f= urllib2.urlopen(req)  #that's the actual call to the http site. 

# *** here would normally be the in-memory parsing of f 
#  contents, but instead I store this to file 
#  this is useful during design, allowing to have a 
#  sample of what is to be parsed in a text editor, for analysis. 

try: 
    fout = open('tmp.htm', 'w') 
except: 
    print('Could not open output file\n') 

fout.writelines(f.readlines()) 
fout.close() 

Questo è tutto per ottenere la pagina iniziale. Come detto sopra, allora bisognerebbe analizzare la pagina, cioè trovare le parti di interesse e riunirle come appropriato, e memorizzarle in un file/database/ovunque. Questo lavoro può essere fatto in molti modi: usando i parser html, o il tipo di tecnologia XSLT (anzi dopo aver analizzato l'html in xml), o anche per i lavori grezzi, semplice espressione regolare. Inoltre, uno degli elementi in genere estratti è la "prossima informazione", cioè un collegamento di ordinamenti, che può essere utilizzato in una nuova richiesta al server per ottenere pagine successive.

Questo dovrebbe darvi un'idea approssimativa di cosa sia lo scraping html "long hand". Ci sono molti altri approcci a questo, come ad esempio gli script dedicati, plug-in GreaseMonkey di Mozilla, XSLT ...

+0

Se utilizzo Google Chrome, come dovrei sostituire il valore di "HTTP_USER_AGENT"? Mi dispiace se questa domanda è stupida dato che non ho fatto molte cose sul web. Grazie! – taocp

+0

@taocp, un modo semplice per sapere quale stringa 'HTTP_USER_AGENT' da usare per un determinato browser è visitare http://www.all-nettools.com/toolbox/environmental-variables-test.php questa pagina ti mostrerà la i valori di intestazione inviati dal browser, cercare "HTTP_USER_AGENT". La stringa effettiva dipende dal sistema operativo, dalla versione specifica e dalla build di Chrome, ma dovrebbe apparire come "Mozilla/5.0" (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, come Gecko) Chrome/29.0.1547.66 Safari/537.36' – mjv

+0

grazie mille per la risposta. Ho provato il tuo codice con i valori corretti impostati sul mio browser Chrome. Il file tmp.htm risultato dice "nessun risultato trovato", mentre quando metto "york" sul sito stesso, ritorna molto. Sai perché? – taocp

0

"Supponiamo di dover selezionare" tutti gli anni "e" tutti i tipi "dai rispettivi menu a discesa."

Cosa fanno queste opzioni all'URL che viene infine inviato.

Dopotutto, si tratta di una richiesta HTTP inviata tramite urllib2.

Non so come fare "" tutti gli anni "e" tutti i tipi "dai rispettivi menu a discesa" fai quanto segue.

  1. Seleziona ' 'tutti gli anni' e 'tutti i tipi' dai rispettivi menu a discesa'

  2. Nota l'URL che in realtà è presentato.

  3. Utilizzare questo URL in urllib2.

+0

A quanto pare la pagina è un modulo che richiede il POST, ma l'idea è la stessa: prendere atto del nome del campo modulo e del valore associato a "Tutti gli anni" e witn "tutti i tipi" e utilizzare urlib2.Request per ottenere i dati. – mjv

+0

Sto utilizzando il proxy di debug web Charles per controllare tutto il traffico http quando navigo su questo sito e invio query, e l'url è completamente statico. Non contiene alcun parametro. Ci sono dati dei moduli da trasmettere in qualche modo - ajax, credo - ma non so come inviare i dati di questo modulo al server.Sembra tutto incomprensibile per me. Il fatto che non riesca a inviare una query manipolando l'url è ciò che mi confonde. – twneale

+0

Una volta ottenuti i risultati da questa pagina, se si desidera eseguirla, è possibile utilizzare il modulo python HTMLParser o Beautifulsoup per analizzare la pagina html. Anche lo scraping probabilmente coinvolgerà più chiamate urlib2 per navigare verso le prossime pagine di risultati. – mjv

4

maggior parte dei siti ASP.NET (quello si fa riferimento inclusa) saranno effettivamente inviare le loro richieste di nuovo a se stessi utilizzando il verbo HTTP POST, non il verbo GET. Questo è il motivo per cui l'URL non cambia come hai notato.

Quello che dovrete fare è guardare l'HTML generato e catturare tutti i loro valori di forma. Assicurati di acquisire tutti i valori del modulo, poiché alcuni di essi sono utilizzati per la convalida della pagina e senza di essi la tua richiesta POST verrà negata.

Oltre alla convalida, una pagina ASPX per quanto riguarda lo scraping e l'invio non è diversa dalle altre tecnologie web.

5

Selenium è un ottimo strumento da utilizzare per questo tipo di attività. È possibile specificare i valori del modulo che si desidera immettere e recuperare l'html della pagina di risposta come una stringa in un paio di righe di codice Python. Usando il selenio potresti non dover fare il lavoro manuale di simulare una richiesta post valida e tutte le sue variabili nascoste, come ho scoperto dopo molte prove ed errori.

+1

Puoi fornire qualche frammento di codice? – taocp

+0

Ho avuto successo nel collegare, accedere e fare clic sui collegamenti utilizzando il selenio. Sono bloccato nella parte in cui si desidera acquisire i dati da una pagina. Poiché l'URI rimane lo stesso anche dopo aver cliccato, questo pone un problema. –

4

Il codice nelle altre risposte era utile; Non avrei mai potuto scrivere il mio crawler senza di esso.

Un problema che ho riscontrato è stato i cookie. Il sito che stava strisciando stava usando i cookie per accedere id di sessione/roba di sicurezza, quindi ho dovuto aggiungere il codice per ottenere il mio crawler al lavoro:

Aggiungi questa importazione:

import cookielib    

Init la roba biscotto:

COOKIEFILE = 'cookies.lwp'   # the path and filename that you want to use to save your cookies in 
    cj = cookielib.LWPCookieJar()  # This is a subclass of FileCookieJar that has useful load and save methods 

Installare CookieJar in modo che venga usato come predefinito CookieProcessor nel gestore di apertura di default:

cj.load(COOKIEFILE) 
    opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj)) 
    urllib2.install_opener(opener) 

Per vedere ciò che i cookie il sito sta usando:

print 'These are the cookies we have received so far :' 

    for index, cookie in enumerate(cj): 
     print index, ' : ', cookie   

questo consente di risparmiare i cookie:

cj.save(COOKIEFILE)      # save the cookies 
Problemi correlati