Fehler meldung und weiss nicht was ich tuen soll

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
dakin
User
Beiträge: 2
Registriert: Samstag 3. Oktober 2020, 11:46

Hey an alle, ich habe mir mit hilfe eines github source code ein crawler geschrieben es wurde in python 2 geschrieben habe es. jetzt auf python drei umgeändert bekomme eine fehler meldung die ich nicht verstehe und google hilft leider auch nicht bei meinen Probleme.

Code: Alles auswählen

import logging.config
import re
import traceback
import urllib.error
import urllib.error
import urllib.parse
import urllib.parse
import urllib.parse
import urllib.request
import urllib.request

from database import CrawlerDb

from settings import LOGGING

# Debugging
# import pdb;pdb.set_trace()

# Logging
logging.config.dictConfig (LOGGING)
logger=logging.getLogger ("crawler_logger")

google_adurl_regex=re.compile ('adurl=(.*?)"')
google_url_regex=re.compile ('url\?q=(.*?)&sa=')
email_regex=re.compile ('([A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4})', re.IGNORECASE)
url_regex=re.compile ('<a\s.*?href=[\'"](.*?)[\'"].*?>')
# Below url_regex will run into 'Castrophic Backtracking'!
# http://stackoverflow.com/questions/8010005/python-re-infinite-execution
# url_regex = re.compile('<a\s(?:.*?\s)*?href=[\'"](.*?)[\'"].*?>')

# Maximum number of search results to start the crawl
MAX_SEARCH_RESULTS=150

EMAILS_FILENAME='data/emails.csv'
DOMAINS_FILENAME='data/domains.csv'

# Set up the database
db=CrawlerDb ( )
db.connect ( )


def crawl(keywords):
    """
	This method will

	1) Google the keywords, and extract MAX_SEARCH_RESULTS
	2) For every result (aka website), crawl the website 2 levels deep.
		That is the homepage (level 1) and all it's links (level 2).
		But if level 1 has the email, then skip going to level 2.
	3) Store the html in /data/html/ and update the database of the crawled emails

	crawl(keywords):
		Extract Google search results and put all in database
		Process each search result, the webpage:
			Crawl webpage level 1, the homepage
			Crawl webpage level 2, a link away from the homepage
			Update all crawled page in database, with has_crawled = True immediately
			Store the HTML
	"""
    logger.info ("-" * 40)
    logger.info ("Keywords to Google for: %s" % keywords.decode ('utf-8'))
    logger.info ("-" * 40)

    # Step 1: Crawl Google Page
    # eg http://www.google.com/search?q=singapore+web+development&start=0
    # Next page: https://www.google.com/search?q=singapore+web+development&start=10
    # Google search results are paged with 10 urls each. There are also adurls
    for page_index in range (0, MAX_SEARCH_RESULTS, 10):
        query={'q': keywords}
        url='http://www.google.com/search?' + urllib.parse.urlencode (query) + '&start=' + str (page_index)
        data=retrieve_html (url)
        # 	print("data: \n%s" % data)
        for url in google_url_regex.findall (data):
            db.enqueue (str (url))
        for url in google_adurl_regex.findall (data):
            db.enqueue (str (url))

    # Step 2: Crawl each of the search result
    # We search till level 2 deep
    while True:
        # Dequeue an uncrawled webpage from db
        uncrawled=db.dequeue ( )
        if not uncrawled:
            break
        email_set=find_emails_2_level_deep (uncrawled.url)
        if len (email_set) > 0:
            db.crawled (uncrawled, ",".join (list (email_set)))
        else:
            db.crawled (uncrawled, None)


def retrieve_html(url):
    """
	Crawl a website, and returns the whole html as an ascii string.

	On any error, return.
	"""
    req=urllib.request.Request (url)
    req.add_header ('User-Agent', 'Just-Crawling 0.1')
    request=None
    status=0
    try:
        logger.info ("Crawling %s" % url)
        request=urllib.request.urlopen (req)
    except urllib.error.URLError as e:
        logger.error ("Exception at url: %s\n%s" % (url, e))
    except urllib.error.HTTPError as e:
        status=e.code
    except Exception as e:
        return
    if status == 0:
        status=200

    try:
        data=request.read ( )
    except Exception as e:
        return

    return str (data)


def find_emails_2_level_deep(url):
    """
	Find the email at level 1.
	If there is an email, good. Return that email
	Else, find in level 2. Store all results in database directly, and return None
	"""
    html=retrieve_html (url)
    email_set=find_emails_in_html (html)

    if len (email_set) > 0:
        # If there is a email, we stop at level 1.
        return email_set

    else:
        # No email at level 1. Crawl level 2
        logger.info ('No email at level 1.. proceeding to crawl level 2')

        link_set=find_links_in_html_with_same_hostname (url, html)
        for link in link_set:
            # Crawl them right away!
            # Enqueue them too
            html=retrieve_html (link)
            if html is None:
                continue
            email_set=find_emails_in_html (html)
            db.enqueue (link, list (email_set))

        # We return an empty set
        return set ( )


def find_emails_in_html(html):
    if html is None:
        return set ( )
    email_set=set ( )
    for email in email_regex.findall (html):
        email_set.add (email)
    return email_set


def find_links_in_html_with_same_hostname(url, html):
    """
	Find all the links with same hostname as url
	"""
    if html is None:
        return set ( )
    url=urllib.parse.urlparse (url)
    links=url_regex.findall (html)
    link_set=set ( )
    for link in links:
        if link is None:
            continue
        try:
            link=str (link)
            if link.startswith ("/"):
                link_set.add ('http://' + url.netloc + link)
            elif link.startswith ("http") or link.startswith ("https"):
                if link.find (url.netloc):
                    link_set.add (link)
            elif link.startswith ("#"):
                continue
            else:
                link_set.add (urllib.parse.urljoin (url.geturl ( ), link))
        except Exception as e:
            pass

    return link_set


if __name__ == "__main__":
    import sys

    try:
        arg=sys.argv[1].lower ( )
        if (arg == '--emails') or (arg == '-e'):
            # Get all the emails and save in a CSV
            logger.info ("=" * 40)
            logger.info ("Processing...")
            emails=db.get_all_emails ( )
            logger.info ("There are %d emails" % len (emails))
            file=open (EMAILS_FILENAME, "w+")
            file.writelines ("\n".join (emails))
            file.close ( )
            logger.info ("All emails saved to ./data/emails.csv")
            logger.info ("=" * 40)
        elif (arg == '--domains') or (arg == '-d'):
            # Get all the domains and save in a CSV
            logger.info ("=" * 40)
            logger.info ("Processing...")
            domains=db.get_all_domains ( )
            logger.info ("There are %d domains" % len (domains))
            file=open (DOMAINS_FILENAME, "w+")
            file.writelines ("\n".join (domains))
            file.close ( )
            logger.info ("All domains saved to ./data/domains.csv")
            logger.info ("=" * 40)
        else:
            # Crawl the supplied keywords!
            crawl (arg)

    except KeyboardInterrupt:
        logger.error ("Stopping (KeyboardInterrupt)")
        sys.exit ( )
    except Exception as e:
        logger.error ("EXCEPTION: %s " % e)
        traceback.print_exc ( )
die fehler meldung besagt: Type 'str' doesn't have expected attribute 'decode'
dieser fehler ist bei : crawl (arg)

ich danke im vorraus auf jede hilfe die ich bekomme.
Sirius3
User
Beiträge: 18273
Registriert: Sonntag 21. Oktober 2012, 17:20

@dakin: ganz zu Anfang: der ganze Code ist nicht brauchbar, weil man HTML nicht mit regulären Ausdrücken durchsuchen darf. Dazu ist das Format viel zu komplex. Benutze einen HTML-Parser wie beautifulsoup. Erster Schritt, das zu reparieren wäre es, alles zu löschen und von Vorne anzufangen.

warum importierst Du alles Doppelt? `logging` wird gar nicht importiert, so dass schon an der Stelle das Programm abbricht.
Benutze keine globalen Variablen. Die regulären Ausdrücke sind Konstanten und sollten auch so geschrieben werden, db sollte aber nicht auf oberster Ebene existieren.
In `crawl`: logger sind dazu da, Informationen auszugeben. Lange Reihen mit Minuszeichen sind nicht sehr informativ.
findall liefert in diesem Fall schon Strings, da str drauf anzuwenden ist unnötig. Falls findall keinen String liefert ist es falsch, mit der Stringrepräsentation eines Tupels weiterzuarbeiten. `email_set` in eine Liste umzuwandeln ist auch unnötig, weil join auch mit Sets zurechtkommt.
In `retrieve_html` ist Deine Fehlerbehandlung kaputt. Nach dem except-Block sollte das Programm wieder in einem Zustand sein, dass man normal weiterarbeiten kann, bei Dir ist aber weder ` request` noch `status` in einem definierten Zustand, was Du dann später durch die nächste "Fehlerbehandlung" zu reparieren versuchst. Behandle nur Fehler die sinnvoll behandelt werden können. In diesem Fall ist das keiner, denn beim Aufrufer von retrieve_html hast Du noch mal eine "Fehlerbehandlung", weil dort dann findall mit einem Fehler aussteigt, der eigentlich von urlopen stammt.
Der erste Fehler ist schon, dass Du request und status mit Dummy-Werten belegst. Dummywerte sind immer ein deutliches Zeichen, dass man eigentlich eine saubere Fehlerbehandlung braucht. Eine Funktion sollte IMMER explizit etwas zurückgeben, oder nie (aber wie schon geschrieben, sollte der implizite None-Rückgabewert durch eine Exception ersetzt werden).
`request.read` liefert ein Bytes-Objekt, das in seine Stringrepräsentation zu verwandeln ist ein Fehler. Das muß sauber dekodiert werden.
An vielen anderen Stellen hast Du diese explizite None-Prüfung drin, die es nicht bräuchte, wenn Du Exceptions richtig einsetzt.
Exceptions, die einfach nur ignoriert werden, sollte es gar nicht geben, weil damit Programmierfehler nie entdeckt werden können.
Erster Schritt um das zu reparieren, wäre es, allen Code zum Exceptionhandling zu löschen und dann dort, wo es sinnvoll ist, neu zu schreiben.
Für Argument-Parsing gibt es das argparse-Modul.
Man schreibt nicht 228 Zeilen Code, ohne ihn Schritt für Schritt zu testen.

Zur eigentlichen Fehlermeldung: bitte immer den kompletten Traceback ins Forum schreiben, denn dort versteckt sich oft noch mehr Informationen.
Hier ist es eigentlich ganz einfach: keyword ist vom Typ String und hat daher keine Methode decode. Also einfach weglassen. Das löst natürlich nicht die ganzen anderen Fehler im Code, die ich oben beschrieben habe.
dakin
User
Beiträge: 2
Registriert: Samstag 3. Oktober 2020, 11:46

danke für dein Feed back die meisten sachen die du beschreibst sind alle gelöst und. in einer zweiten Datei das einzige was nicht funktioniert ist wie gesagt die zeile er crawl alles bis da genau so wie er es machen soll
und zu den doppelten import ich habe es mit 2to3 skript auf die neue version gebracht. ich freue mich auf weiteres konstruktives Feedback von dir.

unn würde mich freuen wenn du mir ein code beispiel oder irgendwie sonst helfen kannst durch den text habe ich noch kein Lösungsansatz bzw versteh nicht was ich tuen soll
Sirius3
User
Beiträge: 18273
Registriert: Sonntag 21. Oktober 2012, 17:20

Wenn Du sagst, dass die meisten Sachen gelöst sind, warum postest Du dann den Code, der die Probleme hat. Zeig doch Deinen Code, den Du wirklich verwendest.
Benutzeravatar
__blackjack__
User
Beiträge: 14053
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@dakin: Sirius3 hat doch auch konkrete Massnahmen vorgeschlagen. Wobei das wichtigste was ich aus dem Beitrag mitgenommen habe: Komplett von vorne mit einem leeren Modul anfangen und das neu schreiben. Und nicht 200+ Zeilen am Stück runterschreiben und dann erst testen, sondern Funktion für Funktion entwickeln und testen.

So ganz grundsätzlich gibt es keine einfache Art das zu reparieren. Das Endergebnis davon sähe so anders aus, das ein schrittweises umschreiben letztlich mehr Arbeit ist, als einfach von vorne anzufangen, mit anderen, geeigneten Werkzeugen zum parsen von HTML und einem sinnvollen Umgang mit Ausnahmen.

Dazu wird man Python können oder lernen müssen, und HTML und dessen Dokumentenmodell und wie das Werkzeug, welches man dann zum Verarbeiten verwendet, dieses auf Python-Objekte abbildet. Eventuell kann auch noch wissen um CSS hilfreich sein.

Was ich sehr nervig finde ohne überhaupt auf den Inhalt des Codes zu schauen, sind die ganzen Leerzeichen zwischen Funktionen/Methoden und der öffnenden Klammer für den Aufruf. Das macht das ganze für Pythonprogrammierer schwerer zu lesen, weil das ungewöhnlich ist/gegen die Konventionen verstösst.

`find_emails_in_html()` ist im Grunde in `find_emails_2_level_deep()` enthalten und letzteres enthält zweimal Code der E-Mails aus HTML extrahiert. Den gibt es damit also drei mal im Programm, wo es ihn nur einmal geben sollte. Das wäre *eine* Funktion, der man als Argument mitgibt wie viele Ebenen tief gesucht werden soll.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten