BinaryWorks.it Official Forum
BinaryWorks.it Official Forum
Home | Profile | Register | Active Topics | Members | Search | FAQ
Username:
Password:
Save Password
Forgot your Password? | Admin Options

 All Forums
 eXtreme Movie Manager 8, 9, 10 Forum
 Scripts
 IMDb API & Web Scraping
 New Topic  Reply to Topic
 Printer Friendly
Next Page
Author Previous Topic Topic Next Topic
Page: of 4 Lock Topic Edit Topic Delete Topic New Topic Reply to Topic

tarzibou
Starting Member

27 Posts

Posted - 10 Jun 2023 :  20:30:04  Show Profile  Edit Topic  Reply with Quote  View user's IP address  Delete Topic
Since the script probably has to be reworked again and again or does not lead to the desired results, I suggest two other approaches to scrape IMDb data:


1. API (fast, but restricted or pricy)

E.g. you can register for free on imdb-api.com (1) and use their REST API for 100 calls per day for free. A further restriction is that there is no method to get alternate titles and perhaps some other information is not available.

But as most information as well as complex searches can be queried, it would be easy to build a small app for it, esp. as there already exists a C# IMDBApiLib (2). Furthermore, the whole community could work together and pay for 1 account which could then be used by all. Thereby, the costs would be reduced dramatically for the single user.

Links:
- (1): https://imdb-api.com
- (2): https://www.nuget.org/packages/IMDbApiLib



2. Web Scraping (slow, but almost all public data can be captured)

Using Visual Studio 2019 with C# .NET (e.g. Standard 2.0 or Framework 4.7.2), you can also scrape all data of those IMDb web pages that are only displayed by clicking on "more". Herefore, you need Selenium.WebDriver (1) and the Selenium.WebDriver.ChromeDriver (2) as NuGet packages. Furthermore the HtmlAgilityPack (3) is useful to parse the HTML document and its nodes.

Links:
- (1): https://www.nuget.org/packages/Selenium.WebDriver
- (2): https://www.nuget.org/packages/Selenium.WebDriver.ChromeDriver
- (3): https://www.nuget.org/packages/HtmlAgilityPack

To execute the clicks on the "more" buttons, you need your own (extension) method:

using OpenQA.Selenium;
using System;
using System.Threading;

namespace MyNameSpace {
  public static partial class Extensions {
    #region --- safe click ----------------------------------------------------------------
    public static void SafeClick(this IWebElement element, int intervalInMilliseconds = 25, int timeoutInMilliseconds = 200) {
      bool success = false;
      int  counter = 0;
      while (!success && counter < timeoutInMilliseconds) {
        try {
          Thread.Sleep(TimeSpan.FromMilliseconds(intervalInMilliseconds));
          element.Click();
          success = true;
          return;
        } catch (Exception ex) {
          counter += intervalInMilliseconds;
        }
      }
    }
    #endregion
  }
}}


Working code example for scraping an IMDb page and parse some content:

using HtmlAgilityPack;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using System;
using System.Collections.Generic;

namespace MyNameSpace {
  public static class IMDbScraper {
    public static HtmlDocument ScrapeIMDbPage(string imdbID) {
      // --- create Selenium service and driver ----------------------------------------------------------
      ChromeDriverService driverService = ChromeDriverService.CreateDefaultService();
      driverService.HideCommandPromptWindow = true;

      ChromeOptions chromeOptions = new ChromeOptions();
      chromeOptions.AddArguments(
        "--blink-settings=imagesEnabled=false"
      );
      chromeOptions.AddUserProfilePreference("profile.managed_default_content_settings.images", 2);
      chromeOptions.AddUserProfilePreference("profile.default_content_setting_values.images", 2);
      IWebDriver driver = new ChromeDriver(driverService, chromeOptions);

      // --- call url in Selenium browser ----------------------------------------------------------------
      string url = String.Format("https://www.imdb.com/title/{0}/companycredits/", imdbID); // e.g. sub page companycredits
      driver.Navigate().GoToUrl(url);

      // --- find and click on any "more" buttons --------------------------------------------------------
      IReadOnlyCollection<IWebElement> elements = driver.FindElements(By.ClassName("ipc-see-more__text"));

      if (elements != null) {
        IJavaScriptExecutor javaScript = (IJavaScriptExecutor)driver;
        bool doIt = true;
        foreach (WebElement element in elements) {
          if (doIt) {
            try {
              if (element.Location.Y > 100) {
                string script = String.Format("window.scrollTo({0}, {1})", 0, element.Location.Y - 200);
                javaScript.ExecuteScript(script); // execute JavaScript to scroll to the button
              }

              element.SafeClick(); // element.Click() is buggy and crashes, therefore we use our extension method
              doIt = false;
            } catch { }
          } else {
            doIt = true;
          }
        }
      }

      // --- get body as HtmlDocument for further HtmlAgilityPack parsing --------------------------------
      HtmlDocument result = new HtmlDocument();
      result.LoadHtml(driver.FindElement(By.XPath(@"//body")).GetAttribute("innerHTML"));

      return result;
    }

    public static List<Company> ParseProductionCompanies(HtmlDocument document) {
      List<Company> result = new List<Company>(); // own class: see below

      // --- get node of desired section -----------------------------------------------------------------
      string path = @"//div[@data-testid=""sub-section-production""]";
      HtmlNode node = document.DocumentNode.SelectSingleNode(path);

      // --- parse content -------------------------------------------------------------------------------
      if (node != null) {
        try {
          foreach (HtmlNode entry in node.ChildNodes[0].ChildNodes) {
            if (entry.Name != "li") {
              continue;
            }

            Company company = new Company() {
              Sphere = Sphere.Production // own Enum: see below
            };

            try {
              company.ID = entry.ChildNodes[0]
                                .Attributes["href"]
                                .Value
                                .GetSubstringBetweenStrings("/company/", "?ref"); // own extension method for string: create it yourself ;)

              company.Name = entry.ChildNodes[0].InnerText;
            } catch { }

            try {
              HtmlNode details = entry.ChildNodes[1]
                                      .ChildNodes[0]
                                      .ChildNodes[0];

              try {
                company.Remark = details.ChildNodes[0].InnerText;
              } catch { }
            } catch { }

            result.Add(company);
          }
        } catch { }
      }

      return result;
    }
  }

  public class Company {
    public string Country { get; set; }
    public string ID      { get; set; }
    public string Name    { get; set; }
    public string Remark  { get; set; }
    public Sphere Sphere  { get; set; }
    public int    Year    { get; set; }
  }

  public enum Sphere {
    [Description("Distributor")]
    Distributor,
    
    [Description("Other")]
    Other,

    [Description("Production")]
    Production,
    
    [Description("Special Effects")]
    SpecialEffects
  };
}


Then you can use the parsed content for further processing, e.g. save it to your own database or export it to a file in your desired format (CSV, JSON, Text, XML).

Edited by - tarzibou on 10 Jun 2023 22:32:51

tarzibou
Starting Member

27 Posts

Posted - 22 Jul 2023 :  18:00:29  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Update:

https://github.com/tardezyx/tar.IMDbScraper

Working IMDb Scraper for C# .NET Standard v2.1
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 22 Jul 2023 :  20:35:43  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
I will have a look at in about 2 weeks when I have holidays.

Big thanks to you, tarzibou!

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 22 Jul 2023 :  21:11:34  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
You will still need to write a main app (I recommend a simply C# Winforms app) which uses this library to scrape the information for one or multiple IMDb titles and then output the data to a suitable format according to your needs (e.g. a text file, but also xml or json files would be possible).

It is not that complicated but therefore you need to learn a bit C# which is also not that hard. The time consuming part is the output formatting for which I have no time, yet as I am starting a really complex project:

A full media control app with support of
- overview of all titles (movies and series incl. existing and missing episodes)
- multiple variants of one title (e.g. having the same movie in 720p, 1080p and 4K or a Theatrical Cut and Director's Cut, etc.)
- online search and showing variants one already has
- download support via Radarr & Co.
- scraping, renaming and archiving
- variants status (added, queued, leeched, scraped, archived)
- individual collections

I guess this takes a year, at least.

Edited by - tarzibou on 22 Jul 2023 21:14:30
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 22 Jul 2023 :  22:52:08  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Well, C# is really not my friend
I'm used to program with old Delphi 7, sometimes Delphi 11.4 and really rare (and a very long time ago) with VB/VBA.
I would produce more syntax errors than lines of code! It would be like to teach an old dog new tricks .

But if you could provide the whole routine as a dll - then I can use it at least in my favourite language.

Btw. good luck for your own app!

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 23 Jul 2023 :  01:20:35  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Ich weiß nicht, ob du die .dll einfach so in Delphi invoken kannst, da es
1. .NET Standard 2.1 ist (und kein .NET Framework) und
2. weitere Paket-Abhängigkeiten bestehen, hier zum HtmlAgilityPack (und ggf. zu System.Text.Json)

Es gibt aber einige Konvertierer von Delphi zu C# und hey, so nen Winforms-Ding in C# zu basteln ist nun echt kein Hexenwerk. Es muss für deine Zwecke ja auch nicht superschön, sondern rein funktional sein. Das könnt ich dir auch noch grob vorbereiten, aber die ganze Exporterei musst du immer noch selbst machen, da ich absolut keine Ahnung habe, welche Daten du eigentlich brauchst und wie du die aufbereiten musst.
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 23 Jul 2023 :  10:09:25  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Ah, auf deutsch ist doch wesentlich einfacher
Das, was ich brauche, ist eigentlich nur der komplette HTML-Source der IMDB-Seite. Oder besser der Unterseiten auf denen diese "Javascript-Nachladungen" (See more / See all) sind. Der Rest funktioniert ja wunderbar mit MagicScript.
Das wären die Seiten mit den MovieConnections, ReleaseInfo, Locations und CompanyCredits.
In XMM10 hat Ale eine Funktion eingebaut, mit der ich eine Exe-Datei aufrufen kann, der ich als Parameter den Link zur Seite übergebe. Als Ergebnis wird dann der Source gespeichert.
Beispiel:

    #PUT#=\WebDL\Project1.exe#11#               // The EXE to call
    #GETMOVIEVAR#=#13#=#MOVIE#
                                             	// The movie to search for

    #STRING#=#13#="https://www.serienjunkies.de/tags/#13#"
    #STRING#=#13#=#13#@WEB
                                          // Parameter: WEB, NEWWEB, SLOT1, SLOT2, SLOT3, SLOT4, SLOT5
                                          // like: #OPENWEB#, #OPENNEWWEB#, #OPENWEBSLOTx#
    #EXECUTEFILEPARAMETER#=#13#           // Pass Parameter
    #EXECUTEFILEFROMPLUGINS#=#11#         // The Initial path is the EXE Folder\Plugins in XMM

    #PUT#=tempdir\temphtml.txt#11#
    
    //This instruction opens a file from a fixed location, like c:\temp...
    #OPENFILE#=#11# 

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 23 Jul 2023 :  12:26:34  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Überall dort, wo "See more" / "All" auftaucht, steckt eine JSON-Abfrage dahinter, die ich in meiner Library implementiert habe, um mit einem JSON-Request alle Einträge auf einmal zu ziehen, was auch flinker als ein HTML-Request ist. Meine Erklärung im Eingangsbeitrag (Nachbildung eines Browsers und Klicken auf die Buttons) ist dahingehend viel umständlicher und dauert auch 10x länger.

Ferner sind die JSON-Strukturen, die man durch den JSON-Request zurückerhält besser/strukturierter zu parsen als HTML-Seiten, die von Tracking- und Verschleierungsscripten sowie Stilisierungsformatierungen durchsetzt sind. Dort, wo ich HTML-Requests brauche, habe ich daher sehr viel davon ausgefiltert, um überhaupt vernünftig parsen zu können.

Letztlich holt meine Library aber so ziemlich alle Daten zu einem Titel raus. Schau dir die Bilderchen an, um zu sehen, was ich da umgesetzt habe: https://github.com/tardezyx/tar.IMDbScraper/tree/main/Images

Es wäre also völliger Blödsinn, meine Library dafür nutzen zu wollen, die HTML- oder JSON-Responses zu exportieren, weil die Daten bereits entsprechend daraus gezogen werden. Anderenfalls wäre es ja auch kein IMDb Scraper, sondern lediglich irgendeine Art "WebRequest-Exporter".

Edited by - tarzibou on 23 Jul 2023 23:15:10
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 23 Jul 2023 :  13:11:35  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
-

Edited by - tarzibou on 23 Jul 2023 23:15:45
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 23 Jul 2023 :  23:24:17  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Ich habe dir mal g'schwind was gebastelt: https://github.com/tardezyx/tar.IMDbScraper/releases/download/1.1.0/tar.IMDbExporter.zip

Lade dir dazu Visual Studio herunter: https://visualstudio.microsoft.com/de/vs/

Entpacke und öffne mein Programm in Visual Studio und führe es einfach mal aus (mit F5).

Export-Folder: hier werden Dateien abgelegt.
IMDb-ID: selbsterklärend.

Wenn du auf Process klickst, werden ein paar Scraper-Methoden ausgeführt (ich hab jene genommen, die du als relevant genannt hast).

Danach werden die Daten aufbereitet - ich habe hier nur mal die alternativen Titel (sind auf IMDb bei der ReleaseInfo zu sehen) so aufbereitet, dass alle vorhandenen "<CountryID>: <AlternateTitle>" untereinander aufgeführt werden.

Zuguterletzt wird die Aufbereitung exportiert, d.h. in eine Text-Datei namens <IMDb-ID>.txt ins Export-Verzeichnis weggeschrieben.

Zur Anpassung ist für dich folgendes relevant:

1. Doppelklicke auf Gui -> MainForm.cs und dort dann auf den "Process"-Button. Dann landest du in der relevanten Methode, wo du unter " --- daten aufbereiten ------" deine Sachen ergänzen kannst.

2. Falls du noch andere Dinge scrapen willst, kannst du einfach die weiteren Scraper-Methoden nutzen (sind alle statisch).

3. Der StringBuilder ist eine Art Hilfs-String. Den nimmt man, um Ressourcen zu schonen. Du kannst für deine Belange auch direkt string nehmen.

4. Die Zeile "alternateTitles = alternateTitles.OrderBy(x => x.Country?.ID).ToList();" sortiert die alternativen Titel nochmal nach Ländercode. Das x => x.Country usw. ist dabei Linq-Syntax und im Grunde nichts anderes als eine Art SELECT, hier wird eben Country.ID ausgewählt. Das ? nach Country ist ein Check, ob Country NULL ist - dürfte in diesem Fall nie vorkommen, da immer ein Land vorhanden sein sollte. ToList() am Ende ist notwendig, damit es wieder eine Liste wird.

5. Async wird für asynchrones Arbeiten genutzt, so dass auch die Gui responsive bleibt, da die Arbeiten in andere Tasks verlagert werden. D.h. aber auch, dass du momentan mehrfach den Process-Button drücken kannst und er das mehrfach ausführt (hier müsste man während er werkelt den Button noch deaktivieren - kannst du ja ergänzen).

6. Für die ProgressBar (die Anzeige unten) dient die Methode Scraper_ProgressUpdate. Das ist etwas komplexer und kannst du erstmal ignorieren.

Den Rest solltest du kennen.

Edited by - tarzibou on 23 Jul 2023 23:42:04
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 24 Jul 2023 :  19:50:58  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Echt klasse, tarzibou

Ich habe das gerade mal auf die Schnelle mit 3 Änderungen getestet, da ja leider nur die AlternateTitles gespeichert werden.
ReleaseDates klappt schon mal (auch wenn die Uhrzeit (00:00:00) etwas stört. (Kenne auch die C++-Syntax für das Format nicht).
Leider bekomme ich bei den FilmingLocations kein Ergebnis (tar.IMDbScraper.Models.FilmingLocation)

Und als Frage (habe wie gesagt gerade mal 10 Minuten in den Source reingeschaut):
Wo bekomme ich die Namen der Variablen her?

            // --- daten aufbereiten --------------------------------------------------------------------
            StringBuilder sbExport = new StringBuilder();

            sbExport.AppendLine($"---Alternate Titles");
            alternateTitles = alternateTitles.OrderBy(x => x.Country?.ID).ToList();
            foreach (AlternateTitle alternateTitle in alternateTitles)
            {
                sbExport.AppendLine($"{alternateTitle.Country?.ID}: {alternateTitle.Title}");
            }

            // ...
            sbExport.AppendLine($"---Release Dates");
            releaseDates = releaseDates.OrderBy(x => x.Country?.ID).ToList();
            foreach (ReleaseDate releaseDate in releaseDates)
            {
                sbExport.AppendLine($"{releaseDate.Country?.ID}: {releaseDate.Date}");
            }

            // ...
            sbExport.AppendLine($"---Filming Locations");
            filmingLocations = filmingLocations.ToList();
            foreach (FilmingLocation filmingLocation in filmingLocations)
            {
                sbExport.AppendLine($"{filmingLocation}");
            }

            // ...


In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 24 Jul 2023 :  22:13:50  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Es ist nicht C++, sondern C#.

DateTime formatieren: https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-date-and-time-format-strings
Bspw. someDate.ToString("yyyy-MM-dd") ergibt "2023-07-24". Hier ist es jedoch noch etwas umständlicher, weil du noch prüfen musst, dass Date nicht NULL ist und dann davon noch den Value nehmen musst. Bissl bekloppt an der Stelle, aber nimm es einfach mal so hin.

FilmingLocations klappen hier. Du musst natürlich die Felder nehmen und nicht die komplette Struktur.

Das Ganze sieht also bspw. so aus:


      sbExport.AppendLine("---Alternate Titles");
      alternateTitles = alternateTitles.OrderBy(x => x.Country?.ID).ToList();
      foreach (AlternateTitle alternateTitle in alternateTitles) {
        sbExport.AppendLine($"{alternateTitle.Country?.ID}: {alternateTitle.Title}");
      }

      sbExport.AppendLine("---Release Dates");
      releaseDates = releaseDates.OrderBy(x => x.Country?.ID).ToList();
      foreach (ReleaseDate releaseDate in releaseDates) {
        if (releaseDate.Date != null) {
          sbExport.AppendLine($"{releaseDate.Country?.ID}: {releaseDate.Date.Value.ToString("yyyy-MM-dd")}");
        }
      }

      sbExport.AppendLine("---Filming Locations");
      foreach (FilmingLocation filmingLocation in filmingLocations) {
        sbExport.AppendLine($"{filmingLocation.Address}");
      }


Du brauchst filmingLocations nicht nochmal als Liste zu übernehmen, weil es bereits eine Liste ist. Das musst du nur machen, wenn du es extra filterst oder umsortierst, was du ja hier nicht tust. Ferner brauchst du $ nur vor einem String, wenn du darin direkt Variablen in {} nutzen möchtest. Auch dieses // ... ist nur ein Kommentar für dich gewesen, dass du weißt, dass du hier weitermachen sollst. Der Rest passt.

Die Variablen kannst du beim Debuggen checken. Schreib mal "Debugger.Break();" irgendwo zwischenrein, damit er da stehen bleibt (oder setze einen manuelle Breakpoint) und lass das dann mit F5 laufen. Die Variablen siehst du dann unten links unter "Lokal" oder mittels Rechtsklick auf eine Variable im Code -> Schnellüberwachung. Siehe auch https://www.youtube.com/watch?v=XPbZupyqrfI

Edited by - tarzibou on 24 Jul 2023 22:52:40
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 24 Jul 2023 :  23:03:07  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
filmingLocation.Address - genau das meinte ich mit den Variablen
Ebenso die für das, was sonst noch alles extrahiert werden kann.
Gehen Personen eigentlich auch? Hab's noch nicht getestet, ob nm auch geht oder nur tt.
Die geschweiften Klammern und die Listen werden in Delphi (zum Glück!) nicht gebraucht bzw. sind anders aufgebaut. Sieht irgendwie nach einem Mischmasch aus Delphi, VB und Javascript aus , deswegen auch meine Befürchtungen mit den Syntax-Fehlern.
Und: Wenn die Seite nochmals geändert wird. Müssen dann auch die dll geändert werden?

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 24 Jul 2023 :  23:43:46  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Wie gesagt, wenn du die Variablen und deren Strukturen untersuchen möchtest, solltest du debuggen - oder noch besser: die Source vom IMDbScraper-Projekt mal runterladen und dort beim UnitTests -> TestTitle.cs die Methode WholeTitleAndDebug() mal ausführen. Die pausiert die Ausführung am Ende und du befindest dich im Debug-Modus, wo du alle lokalen Variablen siehst und deren Strukturen aufklappen und untersuchen kannst. Obacht: das dauert gut und gerne 1-2 Minuten, bis er da alles gescraped hat. Also Geduld.

Personen könnte ich ergänzen, aber ich habe mich gefragt: wozu? Man braucht deren Infos für eine Mediendatenbank eigentlich nie - da reichen die Infos, welche Personen an einem Titel beteiligt waren und genau das habe ich ja schon mit drin (bspw. https://github.com/tardezyx/tar.IMDbScraper/blob/main/Images/Crew.png). Hier fällt mir grad ein, dass du auch die Bilder durchgehen könntest - da siehst du ja die Strukturen direkt :)

Die C#-Syntax finde ich wesentlich lesbarer als VB. Bei Delphi (was wohl von Turbo Pascal kommt?) sehe ich es ähnlich. Klar, ist es etwas Umgewöhnung und das Ganze von mir für dich auch flink hingebastelt. Du kannst das alles noch sauber in separate (Hilfs-)Klassen und separate Methoden aufdröseln, damit es für dich übersichtlicher wird.

Statt Listen kannst du auch Arrays oder IENumerables nutzen - Listen sind aber einfacher zu handhaben und du kannst direkt auf die Strukturen/Klassen, die sich darin befinden, zugreifen. Genau dafür eignet sich LINQ sehr gut, dessen Syntax aber für sich wiederum gewöhnungsbedürftig ist. Bspw. könntest du so direkt den deutschen Titel rausfischen (falls vorhanden):

string? germanTitle = alternateTitles.FirstOrDefault(x => x.Country?.ID == "DE" || x.Country?.ID == "XWG")?.Title;


"XWG" ist der Ländercode bei IMDb für Westdeutschland. Denk dran, dass es auch einen für Ostdeutschland gibt, der mir aber grad entfallen ist. Ferner gibt es andere Scrapermethoden, wo du den Originaltitel und Lokalen Titel direkt erhälst. Aber ist ja nur ein Beispiel, um dir LINQ zu verdeutlichen.

Ja, sobald sich die IMDb-Seiten ändern, muss der Scraper aktualisiert werden. Deswegen wäre es mir lieber, direkt alles per JSON scrapen zu können, doch da fehlt einiges. Bspw. die Produktionstermine, da ich hier keinen Film mit mehr als 5 Einträgen gefunden habe, wo dann halt dieses "mehr"-Button auftaucht - und daher auch die JSON-Methode nicht abrufen/sehen und zum Einbauen entsprechend abkupfern konnte.

Edited by - tarzibou on 24 Jul 2023 23:48:03
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 25 Jul 2023 :  07:34:38  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Hätte ich mir auch nicht träumen lassen, dass ich mit 61 Jahren noch mal was mit C# mache
Habe mir übrigens schon mal die 3 Pakte als nupkg runtergeladen. Nur wie bekomme ich die jetzt eingebunden? Muss ich bei Gelegenheit mal Onkel Google fragen.
Und das andere schaue ich mir bei Gelegenheit an. Auf jeden Fall noch mal besten Dank!!!

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 25 Jul 2023 :  10:30:37  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Heruntergeladene nupkg-Dateien installieren:

1. Extras -> NuGet-Paket-Manager -> Paket-Manager-Einstellungen -> Paket-Quellen
Hier eine neue Quelle hinzufügen: Name "local", Quelle: der Pfad zu dem Ordner, wo deine nupkg-Dateien liegen

2. Extras -> NuGet-Paket-Manager -> NuGet-Pakete für Projektmappe verwalten -> rechts oben die Paketquelle "local" auswählen
Nun sollten alle Pakete aufgelistet werden, die als nupkg in dem Ordner liegen hast

3. Paket auswählen, dann rechts das Projekt auswählen und installieren klicken

---

Alternativ dazu kannst du auch einfach die .dll-Dateien aus den nupkg-Dateien (sind gezippte Dateien) entpacken und direkt referenzieren. NuGet bietet aber den Vorteil, dass er die Abhängigkeiten selbst erkennt und im Regelfall selbst nachlädt.
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 25 Jul 2023 :  19:53:16  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Danke
Werde ich spätestens am Wochenende machen!

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 10 Aug 2023 :  17:46:36  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Hi tarzibou!
Hat leider doch etwas länger gedauert, bis ich Zeit gefunden habe.
Heute habe ich den halben Tag versucht die Companies und Connections auszugeben. Leider ohne Erfolg. Irgendwo war immer ein Fehler drin, so dass ich noch nicht mal das Programm starten konnte. Auch fehlt mir jegliches Verständnis dafür, woran ich erkenne, dass es sich um eine Liste oder einen Text handelt. Die komplette Syntax macht mich einfach nur kirre
Seit 23 Jahren programmiere ich ausschließlich mit Delphi, da kann man sich nicht mal so auf die Schnelle an eine andere Sprache gewöhnen.
Da brauche ich leider etwas mehr Anleitung von dir, da ich auch privat niemanden kenne der üerhaupt programmiert, geschweige denn mit C/C++/C#.

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 11 Aug 2023 :  09:27:17  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Ah, er lebt noch :)

Es gibt 2 Arten von Datentypen:
- Werttypen (Value Types)
- Verweistypen (Referenced Types)

Um es für dich aber herunterzubrechen, gibt es:
- einfache Typen (bool, int, char)
- Klassen (String, DateTime), haben Methoden und Eigenschaften

Eigenschaften von Klassen können wiederum einfache Typen oder auch weitere Klassen sein.

Dann gibt es sogenannte "IEnumerables". Das sind Listen, Arrays, Dictionaries, Sets, Collectables usw. Das sind eigentlich auch nur Klassen (bei Arrays bin ich mir grad nicht sicher, aber erstmal egal) mit Methoden und Eigenschaften und sie beinhalten mehrere Einträge ein und desselben Typs. Also mehrere Einträge eines einfachen Typs oder einer Klasse.

Soweit so gut.

Welchen Datentyp eine Variable nun hat, erkennst du auf zweierlei Arten:

1. Im Code einfach mal den Cursor auf eine Klasse (nicht auf die Variable, sondern auf die Deklaration) setzen und dort F12 drücken... dann springst du in die Klasse rein und dort siehst du, was die alles beinhaltet. Du kannst dich mit F12 auch weiter durchhangeln.

2. Oder du setzt einen Breakpoint und checkst beim Debuggen die Schnellüberwachung (Rechtsklick auf eine Variable -> Schnellüberwachung) oder unten links unter "Lokal" die Übersicht der vorhandenen Variablen. In der rechten Spalte steht da der Typ. Klassen kannst du aufklappen, um deren Unterstrukturen (Eigenschaften) zu sehen. Wenn in der Mitte "Count = ..." oder "[irgendeine Zahl]" steht, dann ist es eine Liste, die man auch aufklappen kann.

Bis auf Strings kannst du Klassen nicht direkt ausgeben bzw. nur dann, wenn eine ToString()-Methode für diese implementiert ist (wie bspw. bei DateTime).

Soweit verständlich?


Wichtig ist noch der Null-Check:

Du musst, bevor du irgendein Element ansprichst, immer erst prüfen, dass es nicht null ist. Dafür ist dieses "?" da. Bspw. dieser Abschnitt:


foreach (AlternateTitle alternateTitle in alternateTitles) {
  sbExport.AppendLine($"{alternateTitle.Country?.ID}: {alternateTitle.Title}");
}


Hier wird geprüft, ob Country null ist und nur, wenn das nicht der Fall ist, wird versucht, die ID vom Land zu lesen und auszugeben.

Man könnte stattdessen auch schreiben:

foreach (AlternateTitle alternateTitle in alternateTitles) {
  if (alternateTitle.Country != null) {
    sbExport.AppendLine($"{alternateTitle.Country.ID}: {alternateTitle.Title}");
  }
}


Falls Country null wäre, hätte das Programm ein Zugriffsproblem, wenn es versucht, von etwas nicht vorhandenem die Eigenschaft "ID" auszulesen.

Zuguterletzt habe ich in meiner Bibliothek dafür gesorgt, dass alle Liste nicht null sind. D.h. sie sind zumindest immer da, auch wenn sie leer sein sollten, womit ein Null-Check für die Listen entfällt.

Edited by - tarzibou on 11 Aug 2023 09:57:11
Go to Top of Page

JDommi
Administrator

Germany
4637 Posts

Posted - 11 Aug 2023 :  11:36:08  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
Erkläre mir doch bitte mal in der foreach-Deklaration das "AlternateTitle alternateTitle in alternateTitles". Und was bedeutet die Groß-/Kleinschreibung?
In Delphi würde ich einfach schreiben: For x:= 0 to alternateTitles.Count-1 do.

Und wie müsste diese Passage korrekt sein, um nicht nur die Anzahl zu bekommen, sondern direkt den CompanyName.

            sbExport.AppendLine("---Production Companies");
            foreach (Company company in companies.Production)
            {
                sbExport.AppendLine($"{companies.Production.Count}");
            }

In order to achieve what is possible, you have to try the impossible over and over again.
Hermann Hesse
Go to Top of Page

tarzibou
Starting Member

27 Posts

Posted - 11 Aug 2023 :  19:22:06  Show Profile  Edit Reply  Reply with Quote  View user's IP address  Delete Reply
---- zusammengefasst im Folgebeitrag -----

Edited by - tarzibou on 11 Aug 2023 19:46:23
Go to Top of Page
Page: of 4 Previous Topic Topic Next Topic   Lock Topic Edit Topic Delete Topic New Topic Reply to Topic
Next Page
 New Topic  Reply to Topic
 Printer Friendly
Jump To:
BinaryWorks.it Official Forum © Binaryworks.it Go To Top Of Page
Generated in 0.23 sec. Powered By: Snitz Forums 2000 Version 3.4.07