public string getURL(string URL, page p, bool followRedirects) { errorString = String.Empty; contentType = String.Empty; statusCode = 0; HTML = String.Empty; try { HttpWebRequest request = HttpWebRequest.Create(URL) as HttpWebRequest; // imposta l'user-agent request.UserAgent = GlobalVars.OpenWebSpider.USERAGENT; // imposta il timeout (default: un minuto 60.000 ms) request.Timeout = GlobalVars.args.reqTimeout * 1000; // segue i redirect if (followRedirects) { request.AllowAutoRedirect = true; request.MaximumAutomaticRedirections = 5; } else { request.AllowAutoRedirect = false; } HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if (response.CharacterSet == null) { return(HTML); } // Support to Encodings Encoding responseEncoding; responseEncoding = Encoding.UTF8; //default UTF-8 if (response.CharacterSet.Trim() != "") { responseEncoding = Encoding.GetEncoding(response.CharacterSet); } StreamReader sr = new StreamReader(response.GetResponseStream(), responseEncoding); BinaryReader binaryStream = new BinaryReader(response.GetResponseStream()); statusCode = (int)response.StatusCode; contentType = response.Headers["Content-Type"]; // il content-type di questa pagina è testo? if (contentType.StartsWith("text", StringComparison.CurrentCultureIgnoreCase)) { HTML = sr.ReadToEnd(); } else if (GlobalVars.args.indexMP3 && contentType.ToLower() == "audio/mpeg") { // se sappiamo chi è il padre e ha un hostID valido allora indicizza l'MP3 if (p != null) { if (p.isValidPage && p._hostID > 0) { string fullPath = binaryStream2MD5File(p, binaryStream, response.Headers["Content-Type"], response.Headers["Content-Length"]); mp3 MP3 = new mp3(fullPath); deleteFile(fullPath); MP3.mp3Size = int.Parse(response.Headers["Content-Length"]); db __db = new db(); __db.indexMP3(p, MP3); } } } else if (GlobalVars.args.indexPDF && contentType.ToLower() == "application/pdf") { // se sappiamo chi è il padre e ha un hostID valido allora indicizza il PDF if (p != null) { if (p.isValidPage && p._hostID > 0) { string fullPath = binaryStream2MD5File(p, binaryStream, response.Headers["Content-Type"], response.Headers["Content-Length"]); pdf PDF = new pdf(fullPath); deleteFile(fullPath); db __db = new db(); __db.indexPDF(p, int.Parse(response.Headers["Content-Length"]), PDF.pdfText); } } } else { HTML = string.Empty; } // forza l'encoding corrente a UTF-8 Encoding utf8 = Encoding.Unicode; byte[] responseEncodingBytes = responseEncoding.GetBytes(HTML); byte[] utf8Bytes = Encoding.Convert(responseEncoding, utf8, responseEncodingBytes); HTML = utf8.GetString(utf8Bytes); sr.Close(); } catch (WebException e) { // TODO: in caso di 404 leggere ugualmente lo stream e ritornare l'HTML HttpWebResponse response = (HttpWebResponse)e.Response; if (response != null) { // in caso di eccezione: prova a recuperare da qui lo status code statusCode = (int)response.StatusCode; if (response.StatusCode == HttpStatusCode.Unauthorized) { string challenge = null; challenge = response.GetResponseHeader("WWW-Authenticate"); if (challenge != null) { errorString = "The following challenge was raised by the server:" + challenge; } } else { errorString = "The following WebException was raised : " + e.Message; } } else { errorString = "Response Received from server was null"; } } catch (Exception e) { errorString = "The following Exception was raised :" + e.Message; } return(HTML); }
// indexer vero e proprio public void DoWork(object threadID) { page pageToIndex; string outStr; // funzioni per l'accesso al DB db __db = new db(); while (!_shouldStop) { System.Threading.Thread.Sleep(50); System.Threading.Thread.Sleep(0); if (GlobalVars.OpenWebSpider.crawlerActPAUSE) { // aspetta ulteriore tempo System.Threading.Thread.Sleep(300); continue; } outStr = string.Empty; // nsGlobalOutput.output.write("Thread["+ (int)threadID +"]: working."); // blocca l'accesso alla lista degli URL da indicizzare e ne estrae uno ( se disponibile ) GlobalVars.threadsVars.mutexAccessURLList.WaitOne(); // cerca una pagina da indicizzare // se siamo in stress test mode: usa sempre la prima pagina if (GlobalVars.args.stressTest == true) { pageToIndex = GlobalVars.threadsVars.currentDomain; } else { pageToIndex = GlobalVars.urlsLists.getPageByStatus(0); } if (pageToIndex == null) { // rilascia il mutex GlobalVars.threadsVars.mutexAccessURLList.ReleaseMutex(); // riparte dall'inizio continue; } else { try { // imposta la pagina come "indicizzazione in corso" pageToIndex.isIndexed = 2; // rilascia il mutex GlobalVars.threadsVars.mutexAccessURLList.ReleaseMutex(); // indicizza questa pagina!!! html htmlPage = new html(); outStr = outStr + "\nT[" + (int)threadID + "] \t Downloading... [ " + pageToIndex.GenerateURL() + " ] [Depth Level: " + pageToIndex._depthLevel + "]\n"; // siamo in presenza di un Crawl-Delay ( da robots.txt o da riga di comando) if (GlobalVars.robotstxtVars.robotstxtCrawlDelay > 0 || GlobalVars.args.crawlDelay > 0) { // locka il mutex GlobalVars.threadsVars.mutexCrawlDelay.WaitOne(); // aspetta il tempo del Crawl-Delay // se c'è un crawl-delay da robots.txt usa quello; altrimenti quello da riga di comando int crawlDelay2Use = (GlobalVars.robotstxtVars.robotstxtCrawlDelay > 0) ? GlobalVars.robotstxtVars.robotstxtCrawlDelay * 1000 : GlobalVars.args.crawlDelay * 1000; System.Threading.Thread.Sleep(crawlDelay2Use); } long curTick = DateTime.Now.Ticks; // scarica la pagina htmlPage.getURL(pageToIndex, true); // siamo in presenza di un Crawl-Delay ( da robots.txt o da riga di comando) if (GlobalVars.robotstxtVars.robotstxtCrawlDelay > 0 || GlobalVars.args.crawlDelay > 0) { // un-locka il mutex GlobalVars.threadsVars.mutexCrawlDelay.ReleaseMutex(); } outStr = outStr + "T[" + (int)threadID + "] \t Downloaded "; if (htmlPage.HTML.Length > 0) { outStr = outStr + htmlPage.HTML.Length / 1024 + " Kb (" + htmlPage.HTML.Length + " bytes)"; } outStr = outStr + " in " + ((DateTime.Now.Ticks - curTick) / 10000) + " ms\n"; outStr = outStr + "T[" + (int)threadID + "] \t HTTP Status Code: " + htmlPage.statusCode + " -][- Content-Type: " + htmlPage.contentType + "\n"; if (htmlPage.errorString != "") { outStr = outStr + "T[" + (int)threadID + "] \t Error: " + htmlPage.errorString + "\n"; } // se non stiamo in stress test mode: allora parsa la pagina e indicizza if (GlobalVars.args.stressTest == false) { // se lo status code (HTTP) è 200 (OK) e il content-type è "text/html" :: indicizza! if (htmlPage.statusCode == 200) { if (htmlPage.contentType.StartsWith("text/html")) { // siamo in una pagina HTML: controlla i META TAG htmlPage.checkMETA(); // siamo in una pagina HTML: estrai il titolo <title>...</title> string __title = htmlPage.getTitle(); if (__title != string.Empty) { pageToIndex._title = __title; // controlla che il titolo rientri nel limite del campo sul DB if (pageToIndex._title.Length > GlobalVars.dbLimits.maxTitleLength) { pageToIndex._title = pageToIndex._title.Substring(0, 255); } outStr = outStr + "T[" + (int)threadID + "] \t Page Title: " + __title + "\n"; } curTick = DateTime.Now.Ticks; outStr = outStr + "T[" + (int)threadID + "] \t Getting URLs..."; if (htmlPage.META_ROBOTS_FOLLOW) { int nValidURLs = htmlPage.GetURLs(htmlPage.HTML, pageToIndex); outStr = outStr + "OK [" + ((DateTime.Now.Ticks - curTick) / 10000) + " ms] ( " + nValidURLs + " valid URLs found )\n"; } else { // NOFOLLOW outStr = outStr + "NOFOLLOW (META ROBOTS)\n"; } } curTick = DateTime.Now.Ticks; outStr = outStr + "T[" + (int)threadID + "] \t Indexing..."; if (htmlPage.META_ROBOTS_INDEX) { if (__db.indexThisPage(pageToIndex, htmlPage) == true) { outStr = outStr + "OK [" + ((DateTime.Now.Ticks - curTick) / 10000) + " ms]\n"; } else { outStr = outStr + "NOT INDEXED [" + ((DateTime.Now.Ticks - curTick) / 10000) + " ms ]\n"; } } else { // NOINDEX outStr = outStr + "NOINDEX (META ROBOTS)\n"; } } else { GlobalVars.limits.curErrorCodes++; // TODO: da testare gli error code che arrivano qui Console.WriteLine(" ## Error code: " + htmlPage.statusCode + " ## "); } } // incrementa il numero di pagine indicizzate GlobalVars.limits.curPages++; // incrementa i bytes indicizzati GlobalVars.limits.curBytes += htmlPage.HTML.Length; } catch (Exception e) { outStr += "Error: " + e.Message + "\n"; } finally { // imposta la pagina come "indicizzata" if (GlobalVars.args.stressTest == false) { pageToIndex.isIndexed = 1; } else { // se siamo in stress-test: la mette come da indicizzare pageToIndex.isIndexed = 0; } nsGlobalOutput.output.write(outStr); } } } nsGlobalOutput.output.write("T[" + (int)threadID + "] \t Worker thread: terminating gracefully."); }
// inizia l'indicizzazione public bool startCrawling() { // inizializza le liste degli URL da indicizzare e esterni(cache) + la lista delle relazioni GlobalVars.urlsLists.init(); GlobalVars.externUrlsLists.init(); GlobalVars.relsList.init(); GlobalVars.imagesLists.init(); // inizializza i thread threads = new threading(); bool haveToIndexSomething = true; // funzioni per l'accesso al DBs db __db = new db(); // attiva l'handler per la pressione di CTRL+C nsGlobalOutput.output.handleCTRLC(); while (haveToIndexSomething) { // svuota le liste nsGlobalOutput.output.write(" + Clearing structures..."); GlobalVars.urlsLists.clear(); GlobalVars.externUrlsLists.l.Clear(); GlobalVars.relsList.rels.Clear(); GlobalVars.imagesLists.clear(); nsGlobalOutput.output.write(""); // init mutexes threads.initMutexes(); if (isMysqlNeeded()) { // ******************************************************************* nsGlobalOutput.output.write(" + Checking connection to MySQL servers..."); if (GlobalVars.mysqlConn.connHostList.ping() == false || GlobalVars.mysqlConn.connPageList.ping() == false) { nsGlobalOutput.output.write(" - One or both MySQL server disconnected, trying to reconnect!"); if (mysqlConnect() == false) { nsGlobalOutput.output.write(" - Unable to connect to one or both MySQL server!"); break; } } // ******************************************************************* // inserisce/aggiorna nel DB il sito corrente e ritorna l'hostID GlobalVars.threadsVars.currentDomain._hostID = __db.startIndexThisSite(GlobalVars.threadsVars.currentDomain); } // a questo punto ha aggiunto l'HOST al DB: possiamo uscire if (GlobalVars.args.add2Hostlist == true) { nsGlobalOutput.output.write("\n\n"); nsGlobalOutput.output.write(" + Host added to the table of the hosts!"); nsGlobalOutput.output.write(" - Hostname : " + GlobalVars.threadsVars.currentDomain._hostname); nsGlobalOutput.output.write(" - Host ID : " + GlobalVars.threadsVars.currentDomain._hostID); nsGlobalOutput.output.write("\n\n"); break; } if (isMysqlNeeded()) { // carica i limiti dal DB [ hostlist_extras ] se presenti GlobalVars.limits.loadHostlistExtraLimits(GlobalVars.threadsVars.currentDomain._hostID); } GlobalVars.limits.showLimits(); // setta il tempo esatto di inizio dell'indicizzazione del dominio corrente GlobalVars.limits.startTime = DateTime.Now.Ticks; // azzera le pagine, i bytes indicizzati e gli errori HTTP GlobalVars.limits.curPages = 0; GlobalVars.limits.curBytes = 0; GlobalVars.limits.curErrorCodes = 0; // se siamo in stress test non controllare il file robots.txt if (GlobalVars.args.stressTest == false) { // recupera e analizza il file: robots.txt { robots prb = new robots(); http tmpHttpRobotsTxt = new http(); string tmpsRobotsTxt; tmpsRobotsTxt = tmpHttpRobotsTxt.getURL("http://" + GlobalVars.threadsVars.currentDomain._hostname + ":" + GlobalVars.threadsVars.currentDomain._port + "/robots.txt", null, false); if (tmpHttpRobotsTxt.statusCode == 200) { prb.parseRobotsTxt(tmpsRobotsTxt); } else { nsGlobalOutput.output.write(" - robots.txt not found"); } } } nsGlobalOutput.output.write(""); // aggiunge al primo posto della lista la prima pagina da indicizzare GlobalVars.urlsLists.addURL(GlobalVars.threadsVars.currentDomain); // crea i threads che indicizzeranno le pagine try { threads.createThreads(); } catch (Exception e) { nsGlobalOutput.output.write(" - Error while creating threads: " + e.Message); threads.killThreads(); return(false); } try { bool waitThreads = true; while (waitThreads) { System.Threading.Thread.Sleep(250); __db.checkCrawlerAct(); // se abbiamo premuto CTRL+C : esci dal ciclo di indicizzazione if (GlobalVars.OpenWebSpider.stopItGracefully == true) { waitThreads = false; } // controlla ogni tot millisecondi se ci sono altre pagine da indicizzare // se no: passa al prossimo dominio GlobalVars.threadsVars.mutexAccessURLList.WaitOne(); try { if (GlobalVars.urlsLists.getPageByStatus(0) == null && GlobalVars.urlsLists.getPageByStatus(2) == null) { waitThreads = false; } } catch (Exception e) { nsGlobalOutput.output.write(" - Error 1 [Wait Threads]: " + e.Message); } finally { GlobalVars.threadsVars.mutexAccessURLList.ReleaseMutex(); } // controlla i limiti if (GlobalVars.limits.checkLimits() == false) { waitThreads = false; } } } catch (Exception e) { nsGlobalOutput.output.write(" - Error 2 [Wait Threads]: " + e.Message); } // killa i threads threads.killThreads(); // ******************************************************************* // ri-controlla la connessione al mysql e se down prova a ristabilirla if (isMysqlNeeded()) { nsGlobalOutput.output.write(" + Checking connection to MySQL servers..."); if (GlobalVars.mysqlConn.connHostList.ping() == false || GlobalVars.mysqlConn.connPageList.ping() == false) { nsGlobalOutput.output.write(" - One or both MySQL server disconnected, trying to reconnect!"); if (mysqlConnect() == false) { nsGlobalOutput.output.write(" - Unable to connect to one or both MySQL server!"); break; } } } // ******************************************************************* // aggiorna nel DB il sito corrente ("indicizzato"; status = 1) __db.stopIndexThisSite(GlobalVars.threadsVars.currentDomain); // stampa le statistiche di indicizzazione printCurDomainStats(); if (isMysqlNeeded()) { // lascia intatti i duplicati? if (!GlobalVars.args.keepDup) { // elimina le pagine con MD5 duplicato lasciando solo quella con id minore __db.deleteDuplicatedPages(GlobalVars.threadsVars.currentDomain._hostID); } // riversa gli URL esterni trovati nel DB __db.swapExternURLs2DB(); // salva le relazioni nel DB __db.saveRels(); //salva le immagini __db.saveImages(); // calcolo del rank delle pagine new rank(GlobalVars.threadsVars.currentDomain._hostID); } // c'è stata una pressione di CTRL+C : esci if (GlobalVars.OpenWebSpider.stopItGracefully == true) { break; } // abbiamo indicizzato il primo sito! dobbiamo continuare? if (GlobalVars.args.singleHostMode == true) { haveToIndexSomething = false; // siamo in single host mode; non indicizzare nient'altro } else { // recupera il primo host libero dal DB page p = __db.getFirstAvailableURL(); if (p == null) { // impossibile trovare un URL: esci!!! haveToIndexSomething = false; nsGlobalOutput.output.write(" + Nothing to do: no available website to index!!! (Try to add: --add-external to the command line arguments) "); } else { // imposta il dominio corrente da indicizzare GlobalVars.threadsVars.currentDomain = p; haveToIndexSomething = true; } } } return(true); }