Autor |
Beitrag |
NOS1971
      
Beiträge: 193
Windows 8.1 PRO 64 Bit
Delphi XE7 Professional
|
Verfasst: Do 20.06.13 14:16
Hallo,
nachdem ich mich nun durch das Thema Threads durchgewühlt habe suche ich einen Weg wie ich aus meinem Spider, der in einer separaten Unit ist auf das MainForm zugreifen kann.
Gebe ich jedem Thread einen Pointer auf das MainForm mit und aktualisiere den Listview im Synchronizebereich oder muss ich das mit Messages machen ?
Ein kleiner Tipp was hier der richtige Weg ist wäre nett.
Grüße,
Andreas
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 20.06.13 14:24
Am einfachsten ist, wenn du ein Event OnDataAvailable oder so in deiner Threadklasse zur Verfügung stellst, das dann via Synchronize im Kontext des Hauptthreads aufgerufen wird. Die Behandlung und der Zugriff auf deine visuellen Komponenten geschieht dann außerhalb deiner Threadklasse, denn da hat die nix zu suchen.
Für diesen Beitrag haben gedankt: NOS1971, turbo
|
|
>M@steR<
      
Beiträge: 288
Erhaltene Danke: 3
|
Verfasst: Do 20.06.13 15:46
Zuletzt bearbeitet von >M@steR< am Di 17.09.13 03:09, insgesamt 1-mal bearbeitet
Für diesen Beitrag haben gedankt: NOS1971
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 20.06.13 16:26
Genau so meinte ich das, ja.
Für diesen Beitrag haben gedankt: NOS1971
|
|
NOS1971 
      
Beiträge: 193
Windows 8.1 PRO 64 Bit
Delphi XE7 Professional
|
Verfasst: Do 20.06.13 18:39
Super ... vielen Dank für Eure Tipps
ich habe schon angefangen das umzusetzen 
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: So 23.06.13 12:03
Tip:
statt Sync-funktionen zu schreiben; versuche es mit anonymen Methode, sieht a bissle better aus:
{thread code}
Delphi-Quelltext 1:
| Syncronize( procedure begin end ); |
{oder}
Delphi-Quelltext 1:
| Queue(procedure begin end); |
cheers
Msch
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
Für diesen Beitrag haben gedankt: NOS1971
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 23.06.13 13:41
Wobei anonyme Methoden nur mit Delphi 2009 und höher funktionieren. Außerdem muss man da ein wenig mit Pointertypen aufpassen.
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: So 23.06.13 15:44
jaenicke hat folgendes geschrieben : | Wobei anonyme Methoden nur mit Delphi 2009 und höher funktionieren. Außerdem muss man da ein wenig mit Pointertypen aufpassen. |
Inwiefern?
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 23.06.13 17:59
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| var TestNumber: Integer;
procedure Test; var TestVar: PInteger; begin TestVar := @TestNumber; TestVar^ := 1; TThread.Queue(nil, procedure begin ShowMessage(IntToStr(TestVar^)); end); TestVar^ := 2; end;
procedure TForm259.FormCreate(Sender: TObject); begin TThread.CreateAnonymousThread(Test).Start; end; | Das ist nun nur ein relativ konstruiertes Beispiel, aber das kann eben auch mit PChars usw. passieren.
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: So 23.06.13 18:11
Nun ja, sehr sehr sehr konstruiert.
Ich haette schon ein Problem mit globalen Variablen auf die Threads zugreifen ohne entsprechende zugriffsmechanismen. Weiterhin muss dem Entwickler doch klar sein, dass threads in der Regel ansynchron laufen (sofern man nicht vorkehrungen trifft). Das hat mit oder ohne Zeiger den gleichen effekt.
Egal, ist eher eine theoretische Disskusion und da passt das Topic nicht ganz.
Cheers Msch
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 23.06.13 18:17
Wenn dir das zu konstruiert ist, dann einfacher, ist dir auch klar, dass hier b ausgegeben wird? Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16:
| procedure Test; var TestVar: string; begin TestVar := 'a'; TThread.Queue(nil, procedure begin ShowMessage(TestVar); end); TestVar := 'b'; end;
procedure TForm259.FormCreate(Sender: TObject); begin TThread.CreateAnonymousThread(Test).Start; end; |
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: So 23.06.13 18:48
jeap. Ist ja auch irgentwie logisch, Queue ist eine Warteschlange. Bei Synchronize() tritt der Effekt aber nicht auf.
Alternativ:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure Test; var TestVar: string; begin TestVar := 'a'; TThread.Queue(nil, procedure begin ShowMessage(TestVar); end); while TThread.CheckTerminated=false do sleep(10); TestVar := 'b'; end; |
macht aber trotzdem irgentwie keinen Sinn, ich stelle den Inhalt eines Variable in eine ansynchrone Warteschleife und ändere den Wert anschließend und hoffe, der Wert bleibt konstant.
Schätze, das sind die Fallstricke der Multi-Thread Entwicklung.
netter Exkurs
Cherio Msch
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 23.06.13 19:43
Ich benutze deshalb eigene Implementierungen, die das umgehen. Zum Beispiel: 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46:
| type TThreadParamMethod<T> = reference to procedure(const AData: T);
TDataQueueThread = class helper for TThread public class procedure QueueData<T>(const AData: T; const AProc: TThreadParamMethod<T>); end;
class procedure TDataQueueThread.QueueData<T>(const AData: T; const AProc: TThreadParamMethod<T>); begin TThread.Queue(nil, procedure begin AProc(AData); end); end;
procedure Test; var TestVar: string; begin TestVar := 'a'; TThread.Queue(nil, procedure begin ShowMessage('Queue vorher: ' + TestVar); end); TThread.QueueData<string>(TestVar, procedure(const AData: string) begin ShowMessage('QueueData vorher: ' + AData); end); TestVar := 'b'; TThread.Queue(nil, procedure begin ShowMessage('Queue nachher: ' + TestVar); end); TThread.QueueData<string>(TestVar, procedure(const AData: string) begin ShowMessage('QueueData nachher: ' + AData); end); end;
procedure TForm259.FormCreate(Sender: TObject); begin TThread.CreateAnonymousThread(Test).Start; end; | Durch das Übergeben der Variable an eine Zwischenmethode wird ein eigener Scope für den Aufruf erzeugt, in dem die Variable nur mit dem aktuellen Wert existiert. Bei Pointertypen, die nicht von Delphi verwaltet werden, muss man natürlich trotzdem aufpassen. PChar usw. funktioniert so aber.
|
|
NOS1971 
      
Beiträge: 193
Windows 8.1 PRO 64 Bit
Delphi XE7 Professional
|
Verfasst: Mo 24.06.13 11:38
Hallo zusammen,
ich würde die Frage gern ein wenig erweitern. Ich habe es nun hinbekommen das meine Analyse durchläuft und alles daten gesynct im mainform im listview ankommen.
nun möchte ich mir eine routine schreiben die mir quasi das nächste zu bearbeitende element aus dem listview holt und damit einen neuen thread startet.
diese routine platziere ich im mainform ... beim zugriff auf den listview muss ich doch mit CriticalSection.Enter etc. arbeiten wenn ich das recht verstanden habe ... oder ?
Grüße,
Andreas
|
|
MSCH
      
Beiträge: 1448
Erhaltene Danke: 3
W7 64
XE2, SQL, DevExpress, DevArt, Oracle, SQLServer
|
Verfasst: Mo 24.06.13 13:39
Wenn du die threads aus dem Hauptthread (main) startest, nicht zwangsweise. Ueberlege aber, das die Anzahl der Thread zwar nicht begrenzt aber doch aber einer bestimmten Anzahl nicht mehr haendelbar sein wird.
Critical sektions brauchst du, wenn mehrere Thread zum beispiel auf globale Variablen zugreifen. Solange du ueber sync() bzw. Queue() auf eigenschaften des Hauptthreads zugreifst, brauchst du sie nicht.
Cheers msch
_________________ ist das politisch, wenn ich linksdrehenden Joghurt haben möchte?
Für diesen Beitrag haben gedankt: NOS1971
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 24.06.13 13:57
Es gibt für Delphi schon fertige Threadpool Implementierungen. Ich denke am sinnvollsten suchst du mal danach...
Beispiel:
www.delphipraxis.net...-delphi-2010-xe.html
Oder auch die OmniThread Library.
Für diesen Beitrag haben gedankt: NOS1971, turbo
|
|
NOS1971 
      
Beiträge: 193
Windows 8.1 PRO 64 Bit
Delphi XE7 Professional
|
Verfasst: Mo 24.06.13 15:59
Hallo und mal wieder lieben Dank für Eure Hilfe und Anregungen.
Ich lese immer Threadpool .... brauche ich das wirklich ? ich habe ausschließlich selbstterminierende Threads und habe es so gemacht das ich nen counter nutze der die anzahl der threads im auge hat ... das alles passiert gesynct im mainform ... wofür benötige ich denn einen threadpool an sich ? die Threads wollte ich in einem array mit inforecords verwalten ... das war in einem threadingbeispiel und das finde ich passend. Mache ich da einen Denkfehler ?
Grüße,
Andreas
P.S. mit der OTL habe ich angefangen aber das funzt bei mir garnicht und ich habe auch nicht das knowhow ... so dachte ich code ich es selbst um das besser zu verstehen und lieber in meinem code fehler zu suchen als so etwas komplexes wie die otl zu nutzen  Falsch ?
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mo 24.06.13 17:11
Ein Threadpool macht genau das was du beschrieben hast von wegen Anzahl aktiver Threads zählen usw. schon im Hintergrund. Deshalb macht das schon Sinn, dann muss man da das Rad nicht immer neu erfinden.
|
|
NOS1971 
      
Beiträge: 193
Windows 8.1 PRO 64 Bit
Delphi XE7 Professional
|
Verfasst: Mo 24.06.13 18:57
jaenicke hat folgendes geschrieben : | Ein Threadpool macht genau das was du beschrieben hast von wegen Anzahl aktiver Threads zählen usw. schon im Hintergrund. Deshalb macht das schon Sinn, dann muss man da das Rad nicht immer neu erfinden. |
Ich werds trotz allem mal ohne fertigen Code versuchen ... da verstehe ich die Abläufe besser ... ich hoffe weiterhin auf Deinen/Euren Beistand
Grüße,
Andreas
|
|
NOS1971 
      
Beiträge: 193
Windows 8.1 PRO 64 Bit
Delphi XE7 Professional
|
Verfasst: Di 25.06.13 13:30
Hallo,
also so halbwegs habe ich es hinbekommen ... allerdings mache ich es nur mit einem counter und ohne die threadinfos ... im moment schaut es so aus das ich zwar die neuen items zum listivew hinzufüge aber nicht alle items des listviews analysiert werden obwohl ich meine GetNextAnalyseItem routine jeweils im thread DoneEvent im Synchronize eines jeden Threads aufrufen.
hier mal der source zur ansicht
Form-Unit mit ListView etc.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268:
| unit ufrmMultiThreadedWebspider;
interface
uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ComCtrls, Vcl.ExtCtrls, Vcl.StdCtrls, uMultiThreadedWebspider, Vcl.Mask, AdvSpin, Vcl.ImgList, SyncObjs, DateUtils;
type TfrmMultiThreadedWebspider = class(TForm) Panel1: TPanel; sbWebSpider: TStatusBar; pcWebSpiderResults: TPageControl; tsInternalLinks: TTabSheet; lvInternalLinks: TListView; Panel2: TPanel; btnStartStopPause: TButton; Label1: TLabel; edURL: TComboBox; Label4: TLabel; seThreads: TAdvSpinEdit; Panel3: TPanel; cbIgnoreURLType: TCheckBox; lblStartTime: TLabel; lblStopTime: TLabel; lblNeededTime: TLabel; TabSheet1: TTabSheet; ilStatusIcons: TImageList; procedure btnStartStopPauseClick(Sender: TObject); procedure FormCreate(Sender: TObject); private public StartTime : TDateTime; StopTime : TDateTime; procedure GetNextAnalyseItem; procedure URLAnalysisBegin(); procedure URLAnalysisDone(index : integer); procedure AddInternalLinkToListView(origurl,origlink,url : string); end;
var frmMultiThreadedWebspider: TfrmMultiThreadedWebspider;
implementation
{$R *.dfm}
procedure TfrmMultiThreadedWebspider.AddInternalLinkToListView(origurl, origlink, url: string); var i : integer; TempItem : TListItem; TempData : PWebURLDataItem; TempParentItem : PWebURLParentDataItem; begin TempItem := nil; for i := 0 to lvInternalLinks.Items.Count -1 do begin if lvInternalLinks.Items[i].Caption = url then begin TempItem := lvInternallinks.Items[i]; break; end; end; if TempItem = nil then begin TempItem := lvInternalLinks.Items.Add; TempItem.Caption := url; TempItem.SubItems.Add(origurl); TempItem.SubItems.Add(origlink); TempItem.StateIndex := 0; new(TempData); TempData.URLString := url; TempData.URLLinkString := origlink; TempData.OrigURLString := origurl; TempData.URLType := utInternalURL; TempData.ParentSites := TList.Create; TempItem.Data := TempData; new(TempParentItem); TempParentItem.URLString := url; TempParentItem.URLLinkString := origlink; TempParentItem.OrigURLString := origurl; TempData.ParentSites.Add(TempParentItem); end else begin if TempItem.Data <> nil then begin if PWebURLDataItem(TempItem.Data).ParentSites <> nil then begin new(TempParentItem); TempParentItem.URLString := url; TempParentItem.URLLinkString := origlink; TempParentItem.OrigURLString := origurl; PWebURLDataItem(TempItem.Data).ParentSites.Add(TempParentItem); end; end; end; end;
procedure TfrmMultiThreadedWebspider.btnStartStopPauseClick(Sender: TObject); var TempItem : TListItem; i : integer; TempData : PWebURLDataItem; TempParentItem : PWebURLParentDataItem; AnalyseThread : TURLAnalyser; begin pcWebSpiderResults.ActivePageIndex := 0; btnStartStopPause.Enabled := false; Cursor := crHourGlass; StartTime := now; lblStartTime.caption := DateTimeToStr(StartTime); lvInternalLinks.items.Clear; TempItem := lvInternalLinks.Items.Add; TempItem.Caption := edURL.Text; TempItem.SubItems.Add(''); TempItem.SubItems.Add(''); TempItem.StateIndex := 0; new(TempData); TempData.URLString := edURL.Text; TempData.URLLinkString := ''; TempData.OrigURLString := ''; TempData.URLType := utInternalURL; TempData.ParentSites := TList.Create; new(TempParentItem); TempParentItem.URLString := edURL.Text; TempParentItem.URLLinkString := ''; TempParentItem.OrigURLString := edURL.Text; TempData.ParentSites.Add(TempParentItem); TempItem.Data := TempData; ActiveThreads := 0; inc(ActiveThreads); AnalyseThread := TURLAnalyser.Create(edURL.Text,edURL.Text,cbIgnoreURLType.Checked,TempItem.Index); AnalyseThread.FreeOnTerminate := true; AnalyseThread.URLAnalysisBegin := URLAnalysisBegin; AnalyseThread.URLAnalysisDone := URLAnalysisDone; AnalyseThread.AddInternalLink := AddInternalLinkToListView; AnalyseThread.Resume; while ((AnalyseThread.Finished = false) or (ActiveThreads > 0)) do begin Application.ProcessMessages; end; StopTime := now; lblStopTime.caption := DateTimeToStr(StopTime); lblNeededTime.caption := TimeToStr(StopTime-StartTime) + ' (' + IntToStr(SecondsBetween(StopTime,StartTime)) + ' Seconds)'; Cursor := crDefault; btnStartStopPause.Enabled := true; ShowMessage('Analysis of ' + edURL.Text + ' has been finished!'); end;
procedure TfrmMultiThreadedWebspider.FormCreate(Sender: TObject); begin pcWebSpiderResults.ActivePageIndex := 0; lblStartTime.Caption := '-'; lblStopTime.Caption := '-'; lblNeededTime.Caption := '-'; end;
procedure TfrmMultiThreadedWebspider.GetNextAnalyseItem; var i : integer; AnalyseThread : TURLAnalyser; begin for i := 0 to lvInternalLinks.Items.Count -1 do begin if PWebURLDataItem(lvInternalLinks.Items[i].Data).Analysed = false then begin if ActiveThreads <= seThreads.Value then begin inc(ActiveThreads); PWebURLDataItem(lvInternalLinks.Items[i].Data).Analysed := true; AnalyseThread := TURLAnalyser.Create(PWebURLDataItem(lvInternalLinks.Items[0].Data).URLString,PWebURLDataItem(lvInternalLinks.Items[i].Data).URLString,cbIgnoreURLType.Checked,i); AnalyseThread.FreeOnTerminate := true; AnalyseThread.URLAnalysisBegin := URLAnalysisBegin; AnalyseThread.URLAnalysisDone := URLAnalysisDone; AnalyseThread.AddInternalLink := AddInternalLinkToListView; AnalyseThread.Resume; end else begin break; end; end; end;
end;
procedure TfrmMultiThreadedWebspider.URLAnalysisBegin(); begin sbWebSpider.Panels[0].Text := 'Threads: ' + inttostr(ActiveThreads); sbWebSpider.Panels[1].Text := 'ListItems: ' + inttostr(lvInternalLinks.Items.Count); end;
procedure TfrmMultiThreadedWebspider.URLAnalysisDone(index: integer); begin if index <= lvInternalLinks.items.Count then lvInternalLinks.Items[index].StateIndex := 1; dec(ActiveThreads); sbWebSpider.Panels[0].Text := 'Threads: ' + inttostr(ActiveThreads); sbWebSpider.Panels[1].Text := 'ListItems: ' + inttostr(lvInternalLinks.Items.Count); GetNextAnalyseItem; end;
end. |
WebSpider Thread und Analyse:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 213: 214: 215: 216: 217: 218: 219: 220: 221: 222: 223: 224: 225: 226: 227: 228: 229: 230: 231: 232: 233: 234: 235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271: 272: 273: 274: 275: 276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 307: 308: 309: 310: 311: 312: 313: 314: 315: 316: 317: 318: 319: 320: 321: 322: 323: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347: 348: 349: 350: 351: 352: 353: 354: 355: 356: 357: 358: 359: 360: 361: 362: 363: 364: 365: 366: 367: 368: 369: 370: 371: 372: 373: 374: 375: 376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392: 393: 394: 395: 396: 397: 398: 399: 400: 401: 402: 403: 404: 405: 406: 407: 408: 409: 410: 411: 412: 413: 414: 415: 416: 417: 418: 419: 420: 421: 422: 423: 424: 425: 426: 427: 428: 429: 430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443: 444: 445: 446: 447: 448: 449: 450: 451: 452: 453: 454: 455: 456: 457: 458: 459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480: 481: 482: 483: 484: 485: 486: 487: 488: 489: 490: 491: 492: 493: 494: 495: 496: 497: 498: 499: 500: 501: 502: 503: 504: 505: 506: 507: 508: 509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526: 527: 528: 529: 530: 531: 532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556: 557: 558: 559: 560: 561: 562: 563: 564: 565: 566: 567: 568: 569: 570: 571: 572: 573: 574: 575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594:
| unit uMultiThreadedWebspider;
interface
uses System.Classes, System.SysUtils, Winapi.ActiveX, Winapi.UrlMon, Vcl.Dialogs;
function UrlDownloadToStream(URL: String; Stream: TStream): Boolean; function CheckUTF8(tempstring : string) : integer; function PrepareURLforAnalysis(origurl,parsedurlhost,parsedurlprotocol: string): string;
var ActiveThreads : integer;
const sGeneralLinkTagStart = '<a'; sGeneralLinkTagEnd = '/a>'; sGeneralHrefTagStart = 'href="'; sGeneralHrefTagEnd = '"'; sGeneralMetaLinkTagStart = '<link '; sGeneralMetaLinkTagEnd = '>';
utNone = 0; utInternalURL = 1; utExternalURL = 2; utInternalImageLinkURL = 3; utExternalImageLinkURL = 4; utInternalFeedLinkURL = 5; utFileLinkURL = 6; utImageLinkURL = 7; utImageURL = 8; utNotAnalysedURL = 9; utExternalFeedLinkURL = 10;
smNone = 0; smInternalURL = 1; smExternalURL = 2; smInternalImageLinkURL = 3; smExternalImageLinkURL = 4; smInternalFeedLinkURL = 5; smFileLinkURL = 6; smImageLinkURL = 7; smImageURL = 8; smNotAnalysedURL = 9; smExternalFeedLinkURL = 10;
damNone = 0; damInternalURL = 1; damExternalURL = 2; damInternalImageLinkURL = 3; damExternalImageLinkURL = 4; damInternalFeedLinkURL = 5; damFileLinkURL = 6; damImageLinkURL = 7; damImageURL = 8; damNotAnalysedURL = 9; damExternalFeedLinkURL = 10;
type TWebURLParentDataItem = packed record URLString : string; OrigURLString : string; URLLinkString : string; end; PWebURLParentDataItem = ^TWebUrlParentDataItem;
TWebURLDataItem = packed record URLString : string; OrigURLString : string; URLLinkString : string; URLType : integer; Analysed : boolean; DeepAnalysed : boolean; HTTPStatus : integer; PageRank : integer; InternalLinks : integer; ExternalLinks : integer; SiteTitle : string; METATagKeywords : string; METATagDescription : string; SiteFollowTag : integer; FileSize : int64; ParentSites : TList; end; PWebURLDataItem = ^TWebURLDataItem;
TURLAnalysisBegin = procedure() of object; TURLAnalysisDone = procedure(index : integer) of object; TAddInternalLink = procedure(origurl,origlink,url : string) of object;
TURLAnalyser = class(TThread) strict private FFormHandle : THandle; FRootURL : string; FAnalyseURL : string; FParsedURLProtocol : string; FParsedURLHost : string; FIgnoreURLType : boolean; FURLName : string; FWWWURLName : string; FItemIndex : integer; Forigurlname : string; FStripParams : boolean; FDetectAllImages : boolean; FDetectFeeds : boolean; FSenderMode : integer;
FResultOrigURL : string; FResultURL : string; FResultOrigLink : string; FURLAnalysisBegin : TURLAnalysisBegin; FURLAnalysisDone : TURLAnalysisDone; FAddInternalLink : TAddInternalLink; procedure SyncURLAnalysisBegin; procedure SyncURLAnalysisDone; procedure SyncAddInternalLink; protected procedure Execute; override; public constructor Create(RootURL,AnalyseURL : string;IgnoreURLType : boolean;Index : integer); destructor Destroy; override; property URLAnalysisBegin : TURLAnalysisBegin read FURLAnalysisBegin write FURLAnalysisBegin; property URLAnalysisDone : TURLAnalysisDone read FURLAnalysisDone write FURLAnalysisDone; property AddInternalLink : TAddInternalLink read FAddInternalLink write FAddInternalLink; end;
implementation
function UrlDownloadToStream(URL: String; Stream: TStream): Boolean; var ppStream: IStream; statstg: TStatStg; lpBuffer: Pointer; dwRead: Integer; begin result:=False;
if Assigned(Stream) then begin if (URLOpenBlockingStream(nil, PChar(URL), IStream(ppStream), 0, nil) = S_OK) then begin try if (ppStream.Stat(statstg, STATFLAG_NONAME) = S_OK) then begin if (statstg.cbSize > 0) then begin lpBuffer:=AllocMem(statstg.cbSize); try if (ppStream.Read(lpBuffer, statstg.cbSize, @dwRead) = S_OK) then begin Stream.Write(lpBuffer^, dwRead); try Stream.Seek(-dwRead, soFromCurrent); finally result:=True; end; end; finally FreeMem(lpBuffer); end; end; end; finally ppStream:=nil; end; end; end; end;
function CheckUTF8(tempstring : string) : integer; begin result := -1;
if POS('ä',tempstring,1) <> 0 then result := 1; if POS('ü',tempstring,1) <> 0 then result := 1; if POS('ö',tempstring,1) <> 0 then result := 1; if POS('Ø',tempstring,1) <> 0 then result := 1; exit; if POS('&',tempstring,1) <> 0 then result := 2; if POS('ä',tempstring,1) <> 0 then result := 2; if POS('Ä',tempstring,1) <> 0 then result := 2; if POS('ü',tempstring,1) <> 0 then result := 2; if POS('Ü',tempstring,1) <> 0 then result := 2; if POS('ö',tempstring,1) <> 0 then result := 2; if POS('Ö',tempstring,1) <> 0 then result := 2;
end;
function PrepareURLforAnalysis(origurl,parsedurlhost,parsedurlprotocol: string): string; var tempurlstring : string; begin if origurl = '' then exit; tempurlstring := origurl; if tempurlstring[1] = '.' then delete(tempurlstring,1,1); tempurlstring := StringReplace(tempurlstring,'/http','http',[rfReplaceAll]); tempurlstring := StringReplace(tempurlstring,'/https','https',[rfReplaceAll]); tempurlstring := StringReplace(tempurlstring,'http//','http://',[rfReplaceAll]); tempurlstring := StringReplace(tempurlstring,'https//','https://',[rfReplaceAll]); tempurlstring := StringReplace(tempurlstring,'http:/http','http',[rfReplaceAll]); if ((POS(UpperCase(ParsedURLHost),UpperCase(tempurlstring)) = 0) and (POS(UpperCase('http://'),UpperCase(tempurlstring)) = 0) and (POS(UpperCase('https://'),UpperCase(tempurlstring)) = 0)) then begin if ((tempurlstring[1] <> '/') and (tempurlstring[1] <> '?')) then begin if POS(UpperCase(ParsedURLProtocol + '://'),UpperCase(tempurlstring),1) = 0 then tempurlstring := '/' + tempurlstring; end; if POS(UpperCase('www.'),UpperCase(origurl),1) <> 0 then begin if POS(UpperCase(ParsedURLProtocol + '://'),UpperCase(tempurlstring),1) = 0 then tempurlstring := ParsedURLProtocol + '://www.' + ParsedURLHost + tempurlstring end else begin if POS(UpperCase(ParsedURLProtocol + '://'),UpperCase(tempurlstring),1) = 0 then tempurlstring := ParsedURLProtocol + '://' + ParsedURLHost + tempurlstring end; end; tempurlstring := StringReplace(tempurlstring,'www.www','www',[rfReplaceAll]); result := tempurlstring; end;
function parseProtocol(url: string): string; var i: integer; begin
i := pos('://', Url); if i <> 0 then begin Result := Copy(url, 1, i - 1); end else begin result := 'http'; end; end;
function parseHost(url: string): string; var i, pathSep, portSep: integer; tmpstr: string; begin tmpstr := url;
i := pos('://', tmpstr); if i <> 0 then begin Inc(i, 2); end; Delete(tmpstr, 1, i);
pathSep := Pos('/', tmpstr); if pathSep <> 0 then begin Delete(tmpstr, pathSep, Length(tmpstr)); end;
portSep := Pos(':', tmpstr); if portSep <> 0 then begin Delete(tmpstr, portSep, Length(tmpstr)); end;
i := pos('?', tmpstr); if i <> 0 then begin Delete(tmpstr, i, Length(tmpstr)); end;
i := pos('#', tmpstr); if i <> 0 then begin Delete(tmpstr, i, Length(tmpstr)); end;
Result := tmpstr; end;
function ExtractURLFileName(URL: string): string; var i: Integer; temphost : string; temppos : integer; begin Result := ''; temphost := ''; temphost := parseHost(Url); if temphost <> '' then temppos := POS(Uppercase(temphost),UpperCase(Url),1); if temppos <> 0 then begin Url := COPY(Url,temppos + Length(temphost),Length(Url)-Length(temphost)+1); end; if POS(Uppercase('http://'),UpperCase(Url),1) = 1 then Url := COPY(Url,Length('http://'),Length(Url)-Length('http://')+1); if POS(Uppercase('https://'),UpperCase(Url),1) = 1 then Url := COPY(Url,Length('https://'),Length(Url)-Length('https://')+1); if POS(Uppercase('http://www.'),UpperCase(Url),1) = 1 then Url := COPY(Url,Length('http://www.'),Length(Url)-Length('http://www.')+1); if POS(Uppercase('https://www.'),UpperCase(Url),1) = 1 then Url := COPY(Url,Length('https://www.'),Length(Url)-Length('https://www.')+1); i := LastDelimiter('/', Url); Result := Copy(Url, i + 1, Length(Url) - (i)); end;
constructor TURLAnalyser.Create(RootURL,AnalyseURL: string;IgnoreURLType : boolean;index : integer); begin inherited Create(true); Synchronize(SyncURLAnalysisBegin); FItemIndex := Index; FRootURL := RootURL; FAnalyseURL := AnalyseURL; FIgnoreURLType := IgnoreURLType; FParsedURLProtocol := parseProtocol(RootURL); FParsedURLHost := parseHost(RootURL); Furlname := FParsedURLProtocol + '://' + FParsedURLHost; Fwwwurlname := FParsedURLProtocol + '://www.' + FParsedURLHost; end;
destructor TURLAnalyser.Destroy; begin Synchronize(SyncURLAnalysisDone); inherited; end;
procedure TURLAnalyser.Execute; var URLSource : TStringStream; urlstring : string; tempencoding : TEncoding; tempSiteTitleTagString : string; tempstring,tempstatuscode,completerobotstagstring,completelinkstring,completeurlstring,completeoriginalurlstring,temppagerank : string; sitetitletagstartpos,titletagpos,URLIndex,templistindex,i,robotstagstartpos,robotstagendpos,hrefstartpos,hrefendpos,linkstartpos,linkendpos,imagetagpos : integer; urlglobalfollowtag,internallinks,externallinks : integer; tempkeywords, tempdescription, temporigurl : string; TempList : TList; begin try if FAnalyseURL <> '' then begin URLSource := TStringStream.Create; if UrlDownloadToStream(FAnalyseURL,URLSource) = true then begin case CheckUTF8(URLSource.DataString) of -1 : urlstring := URLSource.DataString; 1 : urlstring := Utf8ToString(URLSource.DataString); 2 : urlstring := URLSource.DataString; end; hrefstartpos := 1; repeat hrefstartpos := POS(UpperCase(sGeneralHrefTagStart),UpperCase(urlstring),hrefstartpos); if hrefstartpos <> 0 then begin hrefendpos := POS(UpperCase(sGeneralhreftagend),urlstring,hrefstartpos+Length(sGeneralHrefTagStart)); if hrefendpos <> 0 then begin completeoriginalurlstring := COPY(urlstring,hrefstartpos+Length(sGeneralHrefTagStart),hrefendpos-hrefstartpos-Length(sGeneralHrefTagEnd)-Length(sGeneralHrefTagStart)+1); FResultOrigURL := completeoriginalurlstring; completeurlstring := PrepareURLforAnalysis(completeoriginalurlstring,FParsedURLHost,FParsedURLProtocol); FResultURL := completeurlstring; for i := hrefstartpos downto 0 do begin if ((UpperCase(urlstring[i]) = UpperCase('<')) and (UpperCase(urlstring[i+1]) = UpperCase('a')) and (UpperCase(urlstring[i+2]) = UpperCase(' '))) then begin linkstartpos := i; linkendpos := POS(UpperCase(sGeneralLinkTagEnd),UpperCase(urlstring),linkstartpos+Length(sGeneralLinkTagEnd)); if linkendpos <> 0 then begin completelinkstring := COPY(urlstring,linkstartpos,linkendpos-linkstartpos+Length(sGeneralLinkTagEnd)); FResultOrigLink := completelinkstring; break; end else begin end; end else begin end; if ((UpperCase(urlstring[i]) = UpperCase('<')) and (UpperCase(urlstring[i+1]) = UpperCase('l')) and (UpperCase(urlstring[i+2]) = UpperCase('i')) and (UpperCase(urlstring[i+3]) = UpperCase('n')) and (UpperCase(urlstring[i+4]) = UpperCase('k')) and (UpperCase(urlstring[i+5]) = UpperCase(' '))) then begin linkstartpos := i; linkendpos := POS(UpperCase(sGeneralMetaLinkTagEnd),UpperCase(urlstring),linkstartpos+length(sGeneralMetaLinkTagStart)); if linkendpos <> 0 then begin completelinkstring := COPY(urlstring,linkstartpos,linkendpos-linkstartpos+Length(sGeneralLinkTagEnd)-1); FResultOrigLink := completelinkstring; break; end else begin end; end else begin end; end; if ((POS(UpperCase(FRootURL),UpperCase(completeurlstring),1) = 1) and (FIgnoreURLType = false)) or (((POS(UpperCase(Furlname),UpperCase(completeurlstring),1) = 1) or (POS(UpperCase(Fwwwurlname),UpperCase(completeurlstring),1) = 1)) and (FIgnoreURLType = true)) then begin tempstring := ExtractURLFileName(completeurlstring); if ((tempstring <> '') and (tempstring <> ' ') and (tempstring <> '/') and (POS('?',tempstring,1) = 0) and (POS('%',tempstring,1) = 0) and (POS('&',tempstring,1) = 0) and (POS(':',tempstring,1) = 0) and (POS('#',tempstring,1) = 0) and (POS('.php',tempstring,1) = 0) and (POS('.htm',tempstring,1) = 0) and (POS('.',tempstring,1) <> 0)) or (POS('.css',tempstring,1) <> 0) or (POS('.js',tempstring,1) <> 0) then begin end else begin Synchronize(SyncAddInternalLink);
end; end else begin
end; if linkendpos > hrefendpos then hrefstartpos := linkendpos else hrefstartpos := hrefendpos+Length(sGeneralLinkTagEnd); end else begin hrefstartpos := hrefstartpos + length(sGeneralHrefTagStart) + length(sGeneralHrefTagEnd); end; end else begin end; until hrefstartpos = 0; end else begin end; end; except on E: Exception do begin end; end; if URLSource <> nil then freeandnil(URLSource); end;
procedure TURLAnalyser.SyncAddInternalLink; begin if Assigned(AddInternalLink) then FAddInternalLink(FResultOrigURL,FResultOrigLink,FResultURL); end;
procedure TURLAnalyser.SyncURLAnalysisBegin; begin if Assigned(URLAnalysisBegin) then FURLAnalysisBegin(); end;
procedure TURLAnalyser.SyncURLAnalysisDone; begin if Assigned(URLAnalysisDone) then FURLAnalysisDone(FItemIndex); end;
end. |
Ich hoffe Ihr könnt mir sagen warum er nicht alle Items analysiert ... ich denke es ist ein syncproblem ... oder ? Nur was mache ich falsch ?
Grüße,
Andreas
|
|
|