Inserito analisi del link per l'estrazione dei link agli indici e ai racconti

- Testata estrazione die link alle pagine di indice
- Testata estrazione dei link ai racconti
- iniziato ad inserire un service per la gestione parallela.

ToDo:
- Aggiungere gestione parallela
- Aggiungere analisi di quanto downloadato per estrarre altri link e proseguire con lo skraping
- Aumentare i log in modo da tenere traccia di quanto succede
- Animare la UI in modo da far capire cosa succede
This commit is contained in:
CSiCoAlm committed 2025-12-26 18:28:08 +01:00
1 parent 5567b141f8
commit 3033880fd9
6 files changed
+325 -36

No files matched your search

+1
View File
@@ -62,6 +62,7 @@
<Compile Include="Data\DBTools.cs" />
<Compile Include="Net\eLinkState.cs" />
<Compile Include="Net\cLink_A69.cs" />
<Compile Include="Service\Service_Crawler.cs" />
<Compile Include="Site\cSite_A69.cs" />
<Compile Include="UI\frmDownloader.cs">
<SubType>Form</SubType>
+30
View File
@@ -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<iLink> GetStartLink() {
List<iLink> links = new List<iLink>();
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<iLink> _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
+143
View File
@@ -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<string> _urlQueue = new BlockingCollection<string>(new ConcurrentQueue<string>());
// 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;
/// <summary>
/// Avvia il thread worker che consuma la coda di URL.
/// </summary>
public void StartWorker(HttpClient client) {
_client = client;
_workerThread = new Thread(ProcessQueue) { IsBackground = true };
_workerThread.Start();
}
/// <summary>
/// Ferma il worker completando la coda.
/// </summary>
public void StopWorker() {
_urlQueue.CompleteAdding();
}
/// <summary>
/// Aggiunge un URL alla coda FIFO.
/// </summary>
public void EnqueueUrl(string url) {
_urlQueue.Add(url);
UpdateCounters();
}
/// <summary>
/// Thread worker: consuma la coda ed avvia task di download.
/// </summary>
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();
}
});
}
}
/// <summary>
/// Aggiorna i contatori e la progress bar sulla UI.
/// </summary>
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;
}
}
}
+103 -23
View File
@@ -87,8 +87,18 @@ namespace A69WD.Site {
return logged;
}
public async Task<IEnumerable<string>> CrawlPageAsync(iLink lk) {
var links = new List<string>();
/// <summary>
/// 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
/// </summary>
/// <param name="lk"></param>
/// <returns></returns>
public async Task<List<iLink>> CrawlPageAsync(iLink lk) {
var newlinks = new List<iLink>();
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<string> tmpLk = new List<string>();
#region Estraggo gli url ad indici:
{
/*
* Gli url delle pagine di indice sono del tipo:
*
* <div id="pagination">
* <ul>
* <li class="prev"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=2">&lsaquo;</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=1">1</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=2">2</a></li>
* <li class="currentpage">3</li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=4">4</a></li>
* <li class="page"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=5">5</a></li>
* <li class="next"><a href="/racconti-erotici/racconti_lista.php?genere=altro&pagina=4">&rsaquo;</a></li>
* </ul>
* </div>
*
*/
// 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:
* <div class="continua">
* <a href="/racconti-erotici/altro/Tutto-e-iniziato-per-gioco-pazza-famiglia_166770.html">Continua a leggere » </a>
* </div>
*/
// Estraggo i link ai racconti:
tmpLk = new List<string>();
// 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;
}
+1 -1
View File
@@ -47,7 +47,7 @@ namespace A69WD.Site {
/// </summary>
/// <param name="lk"></param>
/// <returns></returns>
Task<IEnumerable<string>> CrawlPageAsync(iLink lk);
Task<List<iLink>> CrawlPageAsync(iLink lk);
}//interface iSiteType
+47 -12
View File
@@ -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<iLink> _urlQueue = new BlockingCollection<iLink>(new ConcurrentQueue<iLink>());
// 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<iLink> 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<iLink> 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);