Fehler mit Rückgabewert eines Generators

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
DrRocket
User
Beiträge: 30
Registriert: Freitag 11. Mai 2018, 15:11

Hallo zusammen,

bei meinem Crawlerprojekt stehe ich vor der nächsten Herausforderung. Durch Data.fetcher() wird Link.crawler() aufgerufen und durch den dort enthaltenen Generator soll ein Link an die aufrufende Instanz übergeben werden. Wenn ich das ohne Klasse, nur mit Funktionen mache, funktioniert das. Sobald ich versuche den Code in Klassen zu strukturieren, bekomme ich die Meldung "Object of type 'generator' is not JSON serializable".

Code: Alles auswählen

class Link:
	
	@staticmethod
	def crawler(start_page=0):
	            for element in soup_link.select(".divLink"):
	                yield str(urljoin(url, element.attrs["href"]))
		


class Data:

	@staticmethod
	def fetcher():
        	url = Link.crawler(21)
        	print("URL aus Link.crawler():", url)
        
Mit dem Print in Data.fetcher sehe ich, dass ein Generator-Objekt zurückgegeben wird. Ist das das Problem? Sollte hier nicht der Inhalt, also der Link übergeben werden?

Code: Alles auswählen

URL aus Link.crawler(): <generator object Link.crawler at 0x0000021DED1FEBF8>
Hier der Traceback:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:/Users/Home/OneDrive/leasing_crawler/main.py", line 166, in <module>
    crawler_test.fetcher()
  File "C:/Users/Home/OneDrive/leasing_crawler/main.py", line 58, in fetcher
    driver.get(url)
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 333, in get
    self.execute(Command.GET, {'url': url})
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\site-packages\selenium\webdriver\remote\webdriver.py", line 319, in execute
    response = self.command_executor.execute(driver_command, params)
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\site-packages\selenium\webdriver\remote\remote_connection.py", line 372, in execute
    data = utils.dump_json(params)
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\site-packages\selenium\webdriver\remote\utils.py", line 33, in dump_json
    return json.dumps(json_struct)
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\json\__init__.py", line 231, in dumps
    return _default_encoder.encode(obj)
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\json\encoder.py", line 199, in encode
    chunks = self.iterencode(o, _one_shot=True)
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\json\encoder.py", line 257, in iterencode
    return _iterencode(o, 0)
  File "C:\Users\Home\AppData\Local\Programs\Python\Python36\lib\json\encoder.py", line 180, in default
    o.__class__.__name__)
TypeError: Object of type 'generator' is not JSON serializable
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

@DrRocket: nein, das liegt nicht daran, dass Du aus einfachen Funktionen sinnlose Klassen gemacht hast (Klassen designt man erst, wenn es einen Bedarf dafür gibt und nicht einfach so), sondern dass Du noch etwas anderes geändert hast.

`soup_link` kommt aus dem Nichts. Bevor Du also anfängst, Dein Programm noch komplizierter zu schreiben, solltest Du es erst einmal in einen ordentlichen Zustand bringen. `urljoin` liefert schon einen String, das nochmal umzuwandeln ist unnötig.
DrRocket
User
Beiträge: 30
Registriert: Freitag 11. Mai 2018, 15:11

Hallo Sirius3,

danke für die Rückmeldung. Der gepostet Code ist nur auf den aus meiner Sicht relevanten Teil der Logik reduziert, wo das Problem auftritt. Der Crawler hat ca. 150 Zeilen Code und besteht aktuell aus drei Klassen. Ich habe das Ganze in Klassen strukturiert, da ich dadurch mein bisher meist theoretisches Wissen über OOP an einem praktischen Beispiel festigen wollte. Ich bin übrigens nach einiger Zeit selbst auf die Lösung gestoßen.

Code: Alles auswählen

class Link:
	
	@staticmethod
	def crawler(start_page=0):
	            for element in soup_link.select(".divLink"):
	                yield str(urljoin(url, element.attrs["href"]))
		


class Data:

	@staticmethod
	def fetcher():
		# yield muss in der aufrufenden Methode in eine Schleife eingebunden
		for element in Link.crawler(21):
        		url = element
	        	print("URL aus Link.crawler():", url)
Wenn ich die yield Rückgabe in eine for Schleife einbette, funktioniert mein Programm wie vorgesehen. Wenn ich das nicht tue, ist der Rückgabewert aus yield lediglich die Speicheradresse des Generatorobjektes (so zumindest mein Verständnis).

Wenn Du willst, kann ich Dir (oder anderen interessierten) den Code mal zuschicken. Würde mich über konstruktives Feedback, was man besser machen kann, was nicht gut ist etc. freuen. Veröffentlichen möchte ich den Crawler hier nicht, da ich befürchte, dass die Seitenbetreiber in irgendeiner Form reagieren könnten, wenn etliche Leute anfangen deren Seite automatisiert zu durchforsten.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Eine Statische Methode, die nichts mit der Klasse zu tun hat, sollte auch nicht in der Klasse stehen. `soup_link` kommt immer noch aus dem Nichts.
Antworten