Effektiv viele Dateien von Webseite herunterladen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
leex
User
Beiträge: 6
Registriert: Dienstag 29. November 2022, 20:02

Hallo zusammen,

ich suche nach einer Möglichkeit, wie ich den Download von sehr vielen + ständig neu hinzukommenden Dateien, effektiv bewerkstelligen kann. Derzeit habe ich es so implementiert, dass eine Linkliste erstellt wird und danach dann beim abarbeiten geprüft wird, ob es den Pfad schon gibt -> wenn nicht, downloaden. Mir wäre es aber lieber, wenn ich nicht immer wieder alle links holen müsste.

Konkret geht es um diese Webseitdaten: https://gentool.net/data/zh/


Hat jemand einen Lösungsvorschlag/Hinweis/Framework wie ich das angehen könnte?


LG Leex
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

Ein klassischer Ansatz dafür (unter Unix/Linux) ist wget mit den richtigen Parametern + cronjob/systemd-timer. Unter Windows kann man sich evtl. mal https://www.httrack.com/ oder vergleichbare Tools anschauen. Da braucht man kein Python oder sonst irgendeine Programmiersprache für. Geht natürlich schon, ist aber größerer Aufwand. Interessant wird das vor allem dann, wenn man irgendeine spezielle "Nachverarbeitungs-Logik" braucht.
leex
User
Beiträge: 6
Registriert: Dienstag 29. November 2022, 20:02

Danke für deine Antwort. Läuft alles unter Linux und wird auch danach noch mit python-skripten weiter verarbeitet und in elastic indexiert.

wget kenne ich natürlich, mir ist aber nicht klar wie ich hier die Anforderung umsetzten kann, dass er eben nicht alle Seiten nochmal durchgeht, sondern das er sich merkt wo er aufgehört hat. Das ist ja mein Problem, nicht das downloaden allgemein (das hab ich ja bereits).

Hast du evtl. ein Beispiel?
leex
User
Beiträge: 6
Registriert: Dienstag 29. November 2022, 20:02

Hier mal noch mein aktueller Code, der noch eine Einschränkung auf den Monat hat, sodass ich zumindest nur immer den aktuellen Monat neu crawle:

Code: Alles auswählen

import os
import pathlib
import re
import time
import urllib.request
from datetime import datetime

from bs4 import BeautifulSoup
from pysitemap import crawler

start = f'https://www.gentool.net/data/zh/'
download_path = "./downloads"


def getLinks(start_url):
    print(f"Start-URL: {start_url}")
    path = pathlib.PurePath(start_url)
    print(path.parts[(len(path.parts) - 1)])

    str_path = path.parts[(len(path.parts) - 1)]
    html_page = urllib.request.urlopen(start_url)
    soup = BeautifulSoup(html_page, "lxml")
    links = []

    for link in soup.findAll('a'):
        regex = re.compile(r'\?|data')
        if not regex.search(link.get('href')):
            print(f"Link: {link.get('href')}")

            if not str_path in "zh":
                create_path = f"{download_path}/{str_path}/{link.get('href')}"
                print(f"Create Path: {create_path}")
            else:
                create_path = f"{download_path}/{link.get('href')}"
                print("no path")
            links.append(link.get('href'))

            if not os.path.exists(create_path):
                os.makedirs(create_path)
    return links


def cleanup_links(filename):
    with open(filename, "r+") as f:
        d = f.readlines()
        f.seek(0)
        for idx, i in enumerate(d):
            if "txt" in i:
                print(idx, i)
                f.write(i)
        f.truncate()

        if os.stat(filename).st_size == 0:
            print(" Removing ", filename)
            os.remove(filename)


if __name__ == '__main__':
    while True:
        months = getLinks(start)
        for month in months:
            if month == "2022_11_November/":
                filename = f'{month.replace("/", "")}.txt'
                print(start + month)
                # getLinks(start + month)
                crawler(start + month, out_file=filename, exclude_urls=[".rep", ".jpg", "?"], out_format="txt")
        cleanup_links("2022_11_November.txt")
        print(f"Sleeping 30min {datetime.now()}")
        time.sleep(900)
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

So ganz ist mir nicht klar, was du dann noch benötigst und was dein Skript (von dem für mich aus dem Eingangspost nicht klar war, dass es existiert; das klang eher manuell) im Detail tut. Wenn es darum geht, HTTP Requests einzusparen und aus der Struktur, wie die heruntergeladenen Dateien lokal abgelegt werden der ursprüngliche Remote-Pfad/Link nicht mehr hervorgeht, kannst du dir diesen doch einfach nach dem erfolgreichen Herunterladen einer Datei in z.B. einem set merken. Das serialisierst du bei Programmende als z.B. JSON, initialisierst damit das set beim nächsten Durchlauf erneut und überspringst die bereits besuchten Links. Zumindest die Indices musst du aber stets neu abfragen. Falls sich auch an bereits heruntergeladenen Dateien etwas ändern kann, wird es etwas komplizierter und geht nicht ohne zumindest einen HEAD-Request.

EDIT: Das zwischenzeitlich gepostete Skript habe ich noch nicht angesehen.
Zuletzt geändert von nezzcarth am Freitag 2. Dezember 2022, 22:40, insgesamt 1-mal geändert.
leex
User
Beiträge: 6
Registriert: Dienstag 29. November 2022, 20:02

Danke, werde das mit dem "set" mal versuchen/recherchieren.

Das Skript liest eigentlich nur alle Links aus und speichert sie in einer Textdatei (2022_11_November.txt). Mit einem zweiten Skript gehe ich die Links dann durch und lade alles herunter, wenn es nicht schon auf der Festplatte (Pfad) vorhanden ist.

Bestehende Links ändern sich nicht mehr.
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Du hast eine seltsame Verwendung von pathlib. Für URLs ist es nicht gedacht, aber dort, wo es sinnvoll wäre, benutzt Du Format-Strings um Pfade zusammenzufummeln, und os.path um Verzeichnisse zu erzeugen.
In `cleanup_links` benutzt Du fast ausschließlich einbuchstabige nichtssagende Variablennamen, um das Lesen besonders schwierig zu machen. Der Filemode r+ wird auch fast nie gebraucht, wie hier, wo es viel klarer und einfacher wäre, einfach eine neue Datei zu erzeugen. Der in-Vergleich ist etwas zu optimistisch.

Code: Alles auswählen

def cleanup_links(filename):
    with open(filename, encoding="ascii") as links:
        cleaned_links = [
            link for link in links
            if "txt" in link
        ]
    if cleaned_links:
        with open(filename, "w", encoding="ascii") as links:
            links.writelines(cleaned_links)
    else:
        os.remove(filename)
leex
User
Beiträge: 6
Registriert: Dienstag 29. November 2022, 20:02

Danke für das Feedback. Ist ein Freizeitprojekt und habe auch lange daran nichts gemacht und bin gerade jetzt dabei das wieder anzugehen und zu optimieren / weiter zu lernen, weshalb ich auch auf das Forum gestoßen bin. Werde versuchen das umsetzten / ändern ). Bin über jeden Tipp/Hinweis/Lösungsvorschläge/Denkanstöße dankbar. Erwarte keine fertigen Lösungen.
nezzcarth
User
Beiträge: 1632
Registriert: Samstag 16. April 2011, 12:47

Ich habe noch nicht verstanden, warum das zwei getrennte Skripte sein müssen. Links suchen, bereits besuchte Links entfernen und den Rest runterladen geht doch in einem Skript, das auch nicht besonders lang ist.
leex
User
Beiträge: 6
Registriert: Dienstag 29. November 2022, 20:02

Da hast du recht. Hat keine logischen Grund. Ist einfach so einfacher für mich als Anfänger gewesen das alles zu trennen. Wenn ich mal alles soweit habe, dass es funktioniert wie ich will, kann ich auch zusammenfassen.
Antworten