diff --git a/Src/A69WD/A69WD.csproj b/Src/A69WD/A69WD.csproj
index 9a72457..7aad2c7 100644
--- a/Src/A69WD/A69WD.csproj
+++ b/Src/A69WD/A69WD.csproj
@@ -62,6 +62,7 @@
+
Form
diff --git a/Src/A69WD/Net/cLink_A69.cs b/Src/A69WD/Net/cLink_A69.cs
index 8b24934..3cfa612 100644
--- a/Src/A69WD/Net/cLink_A69.cs
+++ b/Src/A69WD/Net/cLink_A69.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
@@ -42,6 +43,35 @@ namespace A69WD.Net {
}
+
+ public static List GetStartLink() {
+ List links = new List();
+ for (int i = 0; i < 10; i++) {
+ foreach (string cat in categorie) {
+ string url = LinkIdxBase.Replace("{Categoria}", cat).Replace("{ID}", i.ToString());
+ string name = string.Format("_A69_{0}_{1}.html", cat, i.ToString());
+ links.Add(new cLink_A69(url, name));
+ }
+ }
+ return links;
+ }
+
+ public static bool InsertStartLink(BlockingCollection _urlQueue) {
+ int i = 1;
+ try {
+ foreach (string cat in categorie) {
+ string url = LinkIdxBase.Replace("{Categoria}", cat).Replace("{ID}", i.ToString());
+ string name = string.Format("_A69_{0}_{1}.html", cat, i.ToString("0000"));
+ _urlQueue.Add(new cLink_A69(url, name));
+ }
+ return true;
+ } catch (Exception ex) {
+ Logger.Error("Errore inserendo A69 Start Link - " + ex.Message);
+ return false;
+ }
+ }
+
+
}//class cLink_A69
}//namespace A69WD.Net
diff --git a/Src/A69WD/Service/Service_Crawler.cs b/Src/A69WD/Service/Service_Crawler.cs
new file mode 100644
index 0000000..103aad3
--- /dev/null
+++ b/Src/A69WD/Service/Service_Crawler.cs
@@ -0,0 +1,143 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+
+
+namespace A69WD.Service {
+ public class Service_Crawler {
+
+ // Thread worker che consuma la coda
+ private Thread _workerThread;
+
+ // Coda thread-safe di URL da scaricare
+ private readonly BlockingCollection _urlQueue = new BlockingCollection(new ConcurrentQueue());
+
+ // Semaforo per limitare il numero massimo di download simultanei
+ private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
+
+ // Contatori
+ private int _inCorso = 0;
+ private int _completati = 0;
+ private int _errori = 0;
+
+ // HttpClient autenticato (fornito da AuthService)
+ private HttpClient _client;
+
+
+ public Service_Crawler(Label lblInCorso) {
+ _lblInCorso = lblInCorso;
+ }
+
+
+
+
+
+
+ // Riferimenti ai controlli UI per aggiornare i contatori e la progress bar
+ private readonly Label _lblInCorso;
+ private readonly Label _lblCompletati;
+ private readonly Label _lblDaFare;
+ private readonly Label _lblErrori;
+ private readonly ProgressBar _progressBar;
+
+
+
+
+
+ ///
+ /// Avvia il thread worker che consuma la coda di URL.
+ ///
+ public void StartWorker(HttpClient client) {
+ _client = client;
+ _workerThread = new Thread(ProcessQueue) { IsBackground = true };
+ _workerThread.Start();
+ }
+
+ ///
+ /// Ferma il worker completando la coda.
+ ///
+ public void StopWorker() {
+ _urlQueue.CompleteAdding();
+ }
+
+ ///
+ /// Aggiunge un URL alla coda FIFO.
+ ///
+ public void EnqueueUrl(string url) {
+ _urlQueue.Add(url);
+ UpdateCounters();
+ }
+
+ ///
+ /// Thread worker: consuma la coda ed avvia task di download.
+ ///
+ private void ProcessQueue() {
+ foreach (var url in _urlQueue.GetConsumingEnumerable()) {
+ Task.Run(async () => {
+ Interlocked.Increment(ref _inCorso);
+ UpdateCounters();
+
+ await _semaphore.WaitAsync();
+ try {
+ // Scarica contenuto della pagina
+ var html = await _client.GetStringAsync(url);
+
+ Interlocked.Increment(ref _completati);
+
+ // Salvo il file downloadato
+ System.IO.StreamWriter sw = new System.IO.StreamWriter("A69_Idx_altro_" + _completati.ToString("000") + ".html", false, Encoding.UTF8);
+ sw.Write(html);
+
+ Logger.Information($"Scaricato {url} ({html.Length} bytes)");
+ } catch (Exception ex) {
+ Interlocked.Increment(ref _errori);
+ Logger.Error($"Errore su {url}: {ex.Message}");
+ } finally {
+ Interlocked.Decrement(ref _inCorso);
+ _semaphore.Release();
+ UpdateCounters();
+ }
+ });
+ }
+ }
+
+
+
+ ///
+ /// Aggiorna i contatori e la progress bar sulla UI.
+ ///
+ private void UpdateCounters() {
+ if (_lblInCorso.InvokeRequired) {
+ _lblInCorso.Invoke(new Action(UpdateCounters));
+ return;
+ }
+
+ int daFare = _urlQueue.Count;
+
+ _lblInCorso.Text = $"In corso: {_inCorso} - Completati: {_completati} - Errori: {_errori} - Da fare: {daFare}";
+
+ //int totale = _inCorso + _completati + _errori + daFare;
+ //int fatti = _completati + _errori;
+ //
+ //_progressBar.Maximum = totale > 0 ? totale : 1;
+ //_progressBar.Value = fatti;
+ }
+
+
+
+
+
+
+
+
+
+ }
+
+}
diff --git a/Src/A69WD/Site/cSite_A69.cs b/Src/A69WD/Site/cSite_A69.cs
index 2927f96..2077f8f 100644
--- a/Src/A69WD/Site/cSite_A69.cs
+++ b/Src/A69WD/Site/cSite_A69.cs
@@ -87,8 +87,18 @@ namespace A69WD.Site {
return logged;
}
- public async Task> CrawlPageAsync(iLink lk) {
- var links = new List();
+ ///
+ /// Codice da eseguire per ciascun A69 link:
+ /// - scarico l'url indicato
+ /// - se richiesto lo salvo come file di testo
+ /// - analizzo il documento html ed estraggo tutti gli url ad altre pagine di link
+ /// - analizzo il documento html ed estraggo tutti gli url a racconti
+ /// - analizzo il documento html ed estraggo il testo del racconto
+ ///
+ ///
+ ///
+ public async Task> CrawlPageAsync(iLink lk) {
+ var newlinks = new List();
try {
Console.WriteLine($"A69 - Scarico: {lk.URL}");
HtmlDocument tdoc = null;
@@ -125,7 +135,7 @@ namespace A69WD.Site {
Console.WriteLine($"A69 - Errore HTTP {(int)resp.StatusCode} {resp.ReasonPhrase} per {lk.URL}");
lk.Status = eLinkState.Error;
//App.DB.LinkLog(lk);
- return links;
+ return newlinks;
}
// Scarico la pagina
var thtml = await client.GetStringAsync(lk.URL);
@@ -141,24 +151,94 @@ namespace A69WD.Site {
#endregion
#region ToAnalize
- // if (lk.Status == eLinkState.ToAnalize) {
- // // Estraggo i link alle altre pagine:
- // var tnodes = tdoc.DocumentNode.SelectNodes("//div[@id='pagination']//li//a[@href]");
- // if (tnodes != null) {
- // foreach (var link in tnodes) {
- // var href = link.GetAttributeValue("href", "");
- // if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconti-erotici/racconti_lista.php?genere="))
- // links.Add(cLink_A69.PageBase + href);
- // }
- // }
- // // Rimuove duplicati e ordina alfabeticamente
- // links = links
- // .Distinct() // elimina duplicati
- // .OrderBy(s => s) // ordina alfabeticamente
- // .ToList();
- // }
- // lk.Status = eLinkState.End;
- // App.DB.LinkLog(lk);
+ {
+ if (lk.Status == eLinkState.ToAnalize) {
+ List tmpLk = new List();
+ #region Estraggo gli url ad indici:
+ {
+ /*
+ * Gli url delle pagine di indice sono del tipo:
+ *
+ *
+ *
+ */
+ // Estraggo i link alle altre pagine:
+ var tnodes = tdoc.DocumentNode.SelectNodes("//div[@id='pagination']//ul//li//a[@href]");
+ if (tnodes != null) {
+ foreach (var link in tnodes) {
+ var href = link.GetAttributeValue("href", "");
+ if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconti-erotici/racconti_lista.php?genere="))
+ tmpLk.Add(href); //tmpLk.Add(cLink_A69.PageBase + href);
+ }
+ }
+ // Rimuove duplicati e ordina alfabeticamente
+ tmpLk = tmpLk
+ .OrderBy(s => s) // ordina alfabeticamente
+ .Distinct() // elimina duplicati
+ .ToList();
+ // Inserisco i link trovati
+ try {
+ foreach (string ilk in tmpLk) {
+ string[] separator = new string[] { "&pagina=" };
+ string[] txt = ilk.Split(separator, StringSplitOptions.RemoveEmptyEntries);
+ string url = cLink_A69.PageBase + ilk;
+ string name = txt[0].Replace("/racconti-erotici/racconti_lista.php?genere=", "_A69_") + "_" + int.Parse(txt[1]).ToString("0000") + ".html";
+ newlinks.Add(new cLink_A69(url, name));
+ }
+ } catch (Exception ex) { Logger.Error("Errore analisi Idx link dal " + lk.FileName + " - " + ex.Message); }
+ }
+ #endregion
+
+ #region Estraggo gli url a racconti:
+ {
+ /*
+ *
+ * I link ai racconti sono dei pulsanti così formati:
+ *
+ */
+ // Estraggo i link ai racconti:
+ tmpLk = new List();
+ // Estraggo i link alle altre pagine:
+ var tnodes = tdoc.DocumentNode.SelectNodes("//div[@class='continua']//a[@href]");
+ if (tnodes != null) {
+ foreach (var link in tnodes) {
+ var href = link.GetAttributeValue("href", "");
+ if (!string.IsNullOrEmpty(href) && href.StartsWith("/racconti-erotici/") && (href.EndsWith(".html") || href.EndsWith(".htm")))
+ tmpLk.Add(href); //tmpLk.Add(cLink_A69.PageBase + href);
+ }
+ }
+ // Rimuove duplicati e ordina alfabeticamente
+ tmpLk = tmpLk
+ .OrderBy(s => s) // ordina alfabeticamente
+ .Distinct() // elimina duplicati
+ .ToList();
+ // Inserisco i link trovati
+ try {
+ foreach (string ilk in tmpLk) {
+ string url = cLink_A69.PageBase + ilk;
+ string name = ilk.Replace("/racconti-erotici/", "A69_").Replace(@"/","_");
+ newlinks.Add(new cLink_A69(url, name));
+ }
+ } catch (Exception ex) { Logger.Error("Errore analisi Idx link dal " + lk.FileName + " - " + ex.Message); }
+ }
+ #endregion
+
+ lk.Status = eLinkState.End;
+ App.DB.LinkLog(lk);
+ }
+ }
#endregion
#region End
@@ -181,14 +261,14 @@ namespace A69WD.Site {
}
#endregion
- Console.WriteLine("Link.Count=" + links.Count.ToString("00"));
+ Console.WriteLine("Link.Count=" + newlinks.Count.ToString("00"));
} catch (Exception ex) {
Console.WriteLine($"Errore con {lk.URL}: {ex.Message}");
lk.Status = eLinkState.Error;
//App.DB.LinkLog(lk);
}
- return links;
+ return newlinks;
}
diff --git a/Src/A69WD/Site/iSiteType.cs b/Src/A69WD/Site/iSiteType.cs
index 4d4eec7..737f06d 100644
--- a/Src/A69WD/Site/iSiteType.cs
+++ b/Src/A69WD/Site/iSiteType.cs
@@ -47,7 +47,7 @@ namespace A69WD.Site {
///
///
///
- Task> CrawlPageAsync(iLink lk);
+ Task> CrawlPageAsync(iLink lk);
}//interface iSiteType
diff --git a/Src/A69WD/UI/frmDownloader.cs b/Src/A69WD/UI/frmDownloader.cs
index 704402f..cb8d4a6 100644
--- a/Src/A69WD/UI/frmDownloader.cs
+++ b/Src/A69WD/UI/frmDownloader.cs
@@ -14,11 +14,21 @@ using static System.Windows.Forms.LinkLabel;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using A69WD.Site;
using A69WD.Net;
+using System.Collections.Concurrent;
+using System.Xml.Linq;
+using A69WD.Service;
namespace A69WD {
public partial class frmDownloader : Form {
+ // Coda thread-safe di URL da scaricare
+ private readonly BlockingCollection _urlQueue = new BlockingCollection(new ConcurrentQueue());
+
+ // Semaforo per limitare il numero massimo di download simultanei
+ private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1);
+
+
public frmDownloader() {
InitializeComponent();
@@ -52,20 +62,45 @@ namespace A69WD {
A69WD.Site.iSiteType siteA69 = new cSite_A69();
// - Eseguo l'autenticazione
bool res = await siteA69.Authenticate();
- lblST.Text = DateTime.Now.ToString() + " - sito loggato: " + res.ToString();
- // - Verifico directory pe ri download
- if (!System.IO.Directory.Exists(Cfg.Settings.PathDownloadWorking)) { System.IO.Directory.CreateDirectory(Cfg.Settings.PathDownloadWorking); }
- // - Test di download di un link di indice
- iLink lk = new cLink_A69("https://www.annunci69.it/racconti-erotici/altro.html", "Idx_altro_01.html");
- await siteA69.CrawlPageAsync(lk);
- // - Test di download di un link di racconto
- lk = new cLink_A69("https://www.annunci69.it/racconti-erotici/altro/Oltre-il-desiderio_166769.html", "Rac_0001.html");
- await siteA69.CrawlPageAsync(lk);
- // - Test di download di un link protetto
- lk = new cLink_A69("https://www.annunci69.it/racconti-erotici/racconti_lista.php?genere=altro&pagina=11", "Idx_altro_11.html");
- await siteA69.CrawlPageAsync(lk);
+ if (res) {
+ lblST.Text = DateTime.Now.ToString() + " - sito loggato: " + res.ToString();
+ Logger.Information("Sito correttamente loggato.");
+ } else {
+ Logger.Information("Errore di autenticazione al sito.");
+ return;
+ }
+ // - Verifico path per il download
+ if (!System.IO.Directory.Exists(Cfg.Settings.PathDownloadWorking)) System.IO.Directory.CreateDirectory(Cfg.Settings.PathDownloadWorking);
+ // // Test downloadando tutti i 10 primi link per categoria
+ // List links = cLink_A69.GetStartLink();
+ // int i = 1;
+ // foreach (iLink link in links) {
+ // lblST.Text = "Start url " + link.FileName;
+ // Logger.Information("Start url " + link.FileName);
+ // await siteA69.CrawlPageAsync(link);
+ // Logger.Information(" end url " + link.FileName);
+ // i++;
+ // }
+
+ // Inserisco i link iniziali sulla coda dei download:
+ if (!cLink_A69.InsertStartLink(_urlQueue)) {
+ lblST.Text = "Inserimento A69 start link fault.";
+ return;
+ }
+ lblST.Text = "In coda vi sono: " + _urlQueue.Count.ToString() + " url.";
+
+ // Test estrazione link dal primo indice:
+ iLink link = new cLink_A69("https://www.annunci69.it/racconti-erotici/racconti_lista.php?genere=altro&pagina=1", "Idx_altro_1.html");
+ Logger.Information("Start url " + link.FileName);
+ List urls = await siteA69.CrawlPageAsync(link);
+ Logger.Information(" end url " + link.FileName + " - URL trovati: " + urls.Count.ToString("000"));
+ foreach (iLink url in urls) { _urlQueue.Add(url); }
+ lblST.Text = "In coda vi sono: " + _urlQueue.Count.ToString() + " url.";
+
}
+ private Service_Crawler service = null;
+
private void btnShowLog_Click(object sender, EventArgs e) {
cTools.ShowOnNotepad(Cfg.Settings.PathFileLogs);