SyntaxError: can't assign to function call

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.
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

Hallo liebe Forumsgemeinde,

mit folgendem Skript versuche ich mir bei Yahoo Finance den Cookiecrumb zu ziehen. Es mag vielleicht auch einfachere Wege geben als den meinen aber für mich schien es so zunächst am einfachsten. Ich suche mir dabei den Startpunkt im entsprechenden HTML-Element, addiere die Zeichen der bereits bekannten Zeichenkette hinzu um den Punkt zu bekommen an dem der Cookiecrumb beginnt und möchte dann Zeichen für Zeichen einen leeren string mit Zeichen füllen bis das Gänsefüßchen erreicht ist, dass dem Skript sagt, dass der Cookiecrumb nun vollständig ist und die Schleife beendet werden kann.

Leider egneriere ich dabei einen Fehler auf den ich mir keinen Reim machen kann.

Im Folgenden der Code:
#Get Yahoo Finance Cookiecrumb

import requests

from bs4 import BeautifulSoup


session = requests.session()

page = session.get("https://de.finance.yahoo.com/quote/DAI. ... equency=1d")

inhalt = page.content

soup = BeautifulSoup(inhalt, 'html.parser')

# Textstelle, die den Cookiecrumb beinhaltet ---> "CrumbStore":{"crumb":"DqtaDoyZCY6"

Tags = soup.find_all('script') #ElementCollection, die alle HTML-Elemente mit dem Tagnamen script beinhaltet.

count = 0

for x in Tags: #Iteration um Cookiecrumb zu finden
html = x.text

if 'CrumbStore":{"crumb":"' in (html):
index = str.find(html, 'CrumbStore":{"crumb":"')
start = index + 21 # Letzter Punkt des obigen String (Crumbstore...)
break

#print(html)

cookiecrumb = ""

z = -1

for y in cookiecrumb:
if not element([Start + 1]) is '"':
cookiecrumb([z + 1]) = element([Start + 1])
else:
break

print(cookiecrumb)
Die folgende Fehlermeldung erhalte ich:
cookiecrumb.chr([z + 1]) = element([Start + 1])
^
SyntaxError: can't assign to function call
Was genau ist an der Zeile falsch?
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pythagon: Die Fehlermeldung sagt das man einem Funktionsaufruf nichts zuweisen kann. Was sollte das denn auch bedeuten? Also ``frobnicate() = 42`` — was würde das denn Deiner Meinung nach für einen Effekt haben sollen? Python kann damit nichts anfangen. So etwas kann man beispielsweise in C++ machen wenn `frobnicate()` eine Referenz zurückgeben würde, aber in Python kann man nur an Namen und Attribute Werte zuweisen, nicht an Funktionsaufrufe.

Wobei die Fehlermeldung nicht zum Quelltext passt, denn dort steht ``cookiecrumb([z + 1]) = element([Start + 1])`` während in der Fehlermeldung ``cookiecrumb.chr([z + 1]) = element([Start + 1])`` steht. Macht natürlich keinen Unterschied, weil es egal ist wie der Ausdruck aussieht, der vor den Aufrufklammern steht, es bleibt ein Aufruf.

Du hast auch an anderer Stelle runde Klammern die keinen Sinn machen, aber nicht zu einem Fehler führen. Sollte man trotzdem nicht machen.

`element` und `Start` sind nirgends definiert.

`cookiecrumb` wird mit der leeren Zeichenkette initialisiert und dann wird in einer Schleife über jedes Zeichen in dieser Zeichenkette iteriert – da ist aber keines, also wird die Schleife auch nicht ausgeführt.

Die Bedingung ``not … is …`` würde man als ``… is not …`` schreiben. Allerdings ist ``is`` hier der falsche Operator, denn bei Zeichenketten ist nicht garantiert, dass die tatsächlich *identisch* dein müssen wenn sie gleich sind:

Code: Alles auswählen

In [28]: a
Out[28]: 'equal but not identical'

In [29]: b
Out[29]: 'equal but not identical'

In [30]: a == b
Out[30]: True

In [31]: a is b
Out[31]: False
``is`` und ``is not`` macht nur bei Singletons wie ``None`` Sinn, oder wenn man tatsächlich die Identität und nicht nur den Wert vergleichen will – was eher selten vorkommt. Um auf Werte(un)gleichheit zu testen sind die Operatoren ``==`` und ``!=`` da.

Zeichenketten sind in Python unveränderbar, man kann da also keine Zeichen einem Index zuweisen. Und ein `chr`-Attribut haben Zeichenketten ebenfalls nicht. Wie bist Du denn auf die Idee gekommen?

Mit der Laufvariable der Schleife (`y`) wird in der Schleife gar nichts gemacht.

Gross-/Kleinschreibung ist in Python wichtig. `start` ist ein anderer Name als `Start`. Man schreibt Namen in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase). `Start` und `Tags` wären also `start` und `tags`.

Da sind auch einige schlechte Namen. Finde mal für `y` einen sinnvollen Namen, dann fällt Dir vielleicht auf wie unsinnig kompliziert das in der Schleife ist. Wobei selbst dann würde man an der Stelle die Zeichen nicht einzeln verarbeiten. Schau Dir mal die Operationen auf Zeichenketten und Sequenztypen generell an – einen Teil daraus mit Angabe von Anfangs- und Endposition bekommt man ganz einfach ohne Schleife per „slicing“.

`find()` würde ich nicht verwenden weil der Rückgabewert bei Misserfolg ein gültiger Index ist, und man da immer dran denken muss diesen Fall explizit zu berücksichtigen. Entweder in dem man den Rückgabewert testet, oder in dem man vorher testet ob das was man sucht überhaupt vorhanden ist. Das macht die gleiche Arbeit dann aber unsinnigerweise zweimal. Die `index()`-Methode hat das Problem nicht.

`count` wird definiert aber nicht verwendet.

Die 21 die da addiert wird ist leicht ”magisch”. An der Stelle sollte man die gesuchte Zeichenkette als Variable oder Konstante definieren, insbesondere wenn man den gleichen Wert mehrfach verwendet, entweder direkt oder davon abgeleitete Werte wie diese 21.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

Hallo BlackJack,

ich hatte da heute Nacht in meiner schlaftrunkenheit einige Fehler gemacht beim Posten des Skripts. Um es übersichtlich zu gestalten hatte ich die Namen der Variablen nochmal angepasst und dabei vergessen im unteren Teil "element" durch "html" zu ersetzen. Und dann hatte ich auch noch versehentlich die Fehlermeldung der letzten Laufzeit eingefügt. Ich gelobe Besserung. =D

Ein paar Dinge, die Du geschrieben hast leuchten mir ein, andere noch nicht so ganz. Ich verstehe nicht weshalb Python meine Codezeile cookiecrumb([z + 1]) = element([Start + 1]) als Funktionsaufruf interpretiert. Die Zeile Cookiecrumb = "" wird ja auch nicht als Funktionsaufruf interpretiert.

Ich habe den Code nun etwas angepasst um es mal mit einer while - else Schleife zu versuchen und bekomme wieder einen ähnlichen Fehler insofern, dass Python meinen Befehl anscheinend wieder als Funktionsaufruf interpretiert:
#Get Yahoo Finance Cookiecrumb

import requests

from bs4 import BeautifulSoup


session = requests.session()

page = session.get("https://de.finance.yahoo.com/quote/DAI. ... equency=1d")

inhalt = page.content

soup = BeautifulSoup(inhalt, 'html.parser')

# Textstelle, die den Cookiecrumb beinhaltet ---> "CrumbStore":{"crumb":"DqtaDoyZCY6"

Tags = soup.find_all('script') #ElementCollection, die alle HTML-Elemente mit dem Tagnamen script beinhaltet.

for x in Tags: #Iteration um Cookiecrumb zu finden
html = x.text

if 'CrumbStore":{"crumb":"' in (html):
index = str.find(html, 'CrumbStore":{"crumb":"')
pointer = index + 21 # Letzter Punkt des obigen String (Crumbstore...)
break

#print(html)


while html([pointer + 1]) != '"':
pointer = pointer + 1
else:
cookiecrumb = html[index + 22:pointer]


print(cookiecrumb)

session.close
Der Fehler:
while html([pointer + 1]) != '"':

TypeError: 'str' object is not callable
Wieso wird der Code als Funktionsaufruf interpretiert?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ein Objekt mit Klammern dahinter stellt für Python halt einen Aufruf dar. Das ist einfach semantisch so festgelegt. Wenn das Objekt nicht aufrufbar ist, dann kommt dieser Fehler. Was sollen die Klammern dort denn deiner Meinung nach bewirken?
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

snafu hat geschrieben: Samstag 13. Juli 2019, 13:45 Ein Objekt mit Klammern dahinter stellt für Python halt einen Aufruf dar. Das ist einfach semantisch so festgelegt. Wenn das Objekt nicht aufrufbar ist, dann kommt dieser Fehler. Was sollen die Klammern dort denn deiner Meinung nach bewirken?
Besten Dank! Das war auch schon die Lösung des Problems! Ich habe die Runden Klammern entfernt und siehe da, ich bekomme den Cookiecrumb.

Tausend Dank!
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pythagon: Ein Aufruf ist in Python (genau wie in fast allen anderen imperativen Programmiersprachen die ich kenne) etwas von der Form <ausdruck der etwas aufrufbares ergibt> (<argumente>). Und genau so etwas hast Du da stehen. Mit ``spam[ i ]`` greift man auf Element `i` von `spam` zu. Mit ``spam(i)`` ruft man `spam` mit dem Argument `i` auf. Und mit ``spam([ i ])`` ruft man `spam` mit einem Argument auf das eine Liste mit einem Element ist, nämlich dem Wert von `i`. Oder eben bei ``html([pointer + 1])`` versuchst Du das Objekt hinter dem Namen `html` aufzurufen, aber Zeichenketten sind nicht aufrufbar.

`session.close` rufst Du dagegen *nicht* auf – solltest Du aber. Oder noch besser: `Session`-Objekte sind Kontextmanager, die kann man mit ``with`` verwenden.

`pointer` ist ein bisschen irreführend, weil das ein Index ist und kein Pointer die man aus anderen Programmiersprachen kennt.

Das ``else`` zum ``while`` macht keinen Sinn weil in der Schleife gar kein ``break`` vorkommt. Ein ``else`` könnte bei der ``for``-Schleife Sinn machen um den Fall zu behandeln, dass die nichts findet, denn dann rennt der Code etwas später zwangsläufig in einen `NameError` weil `html` nicht definiert ist.

Mir ist auch nicht so ganz klar warum Du da eine ``while``-Schleife benutzt um den Index eines Zeichens heraus zu finden. Du weisst doch schon das es dafür eine Methode gibt: `find()`. Beziehungsweise sollte man da wie gesagt besser `index()` verwenden um den Code einfacher, effizienter, und sicherer zu machen.

Namen: `page` stimmt nicht, `html` stimmt nicht, `x` ist zu nichtssagend, `index` könnte noch vertragen *welcher* Index das ist, und `pointer` stimmt nicht.

Und benutze doch bitte mal code statt quote Tags für den Quelltext. Einrückung ist in Python wichtig, die sollte man im Beitrag auch sehen können.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

__blackjack__ hat geschrieben: Samstag 13. Juli 2019, 13:53 @Pythagon: Ein Aufruf ist in Python (genau wie in fast allen anderen imperativen Programmiersprachen die ich kenne) etwas von der Form <ausdruck der etwas aufrufbares ergibt> (<argumente>). Und genau so etwas hast Du da stehen. Mit ``spam[ i ]`` greift man auf Element `i` von `spam` zu. Mit ``spam(i)`` ruft man `spam` mit dem Argument `i` auf. Und mit ``spam([ i ])`` ruft man `spam` mit einem Argument auf das eine Liste mit einem Element ist, nämlich dem Wert von `i`. Oder eben bei ``html([pointer + 1])`` versuchst Du das Objekt hinter dem Namen `html` aufzurufen, aber Zeichenketten sind nicht aufrufbar.

`session.close` rufst Du dagegen *nicht* auf – solltest Du aber. Oder noch besser: `Session`-Objekte sind Kontextmanager, die kann man mit ``with`` verwenden.

`pointer` ist ein bisschen irreführend, weil das ein Index ist und kein Pointer die man aus anderen Programmiersprachen kennt.

Das ``else`` zum ``while`` macht keinen Sinn weil in der Schleife gar kein ``break`` vorkommt. Ein ``else`` könnte bei der ``for``-Schleife Sinn machen um den Fall zu behandeln, dass die nichts findet, denn dann rennt der Code etwas später zwangsläufig in einen `NameError` weil `html` nicht definiert ist.

Mir ist auch nicht so ganz klar warum Du da eine ``while``-Schleife benutzt um den Index eines Zeichens heraus zu finden. Du weisst doch schon das es dafür eine Methode gibt: `find()`. Beziehungsweise sollte man da wie gesagt besser `index()` verwenden um den Code einfacher, effizienter, und sicherer zu machen.

Namen: `page` stimmt nicht, `html` stimmt nicht, `x` ist zu nichtssagend, `index` könnte noch vertragen *welcher* Index das ist, und `pointer` stimmt nicht.

Und benutze doch bitte mal code statt quote Tags für den Quelltext. Einrückung ist in Python wichtig, die sollte man im Beitrag auch sehen können.
Für mich macht die while Schleife deshalb Sinn weil sie einfach so lange die Zeichenkette durchläuft bis das nächste Gänsefüßchen gefunden wird. Ich wüsste jetzt gerade nicht wie ich die Schleife mit find() oder index() bauen müsste. Else habe ich nun heraus genommen da es - wie Du schon sagtest - unnötig war. Ich habe auch versucht herauszufinden was Code-Tags sind aber habe nichts dazu gefunden.

Nachdem ich nun soweit bin, dass ich den Cookiecrumb habe wollte ich nun mit Hilfe des Session Objekts einen weiteren Http-Request starten um über einen Link einen csv-File zu ziehen, allerdings bekomme ich einen Unauthorized Error (401)obwohl ich eigentlich dachte, dass ich genau dieses Problem mit Hilfe des Session-Objekts umgangen wäre:
#Get Yahoo Finance Cookiecrumb

import requests

from bs4 import BeautifulSoup


session = requests.session()

page = session.get("https://de.finance.yahoo.com/quote/DAI. ... equency=1d")

inhalt = page.content

soup = BeautifulSoup(inhalt, 'html.parser')

# Textstelle, die den Cookiecrumb beinhaltet ---> "CrumbStore":{"crumb":"DqtaDoyZCY6"

Tags = soup.find_all('script') #ElementCollection, die alle HTML-Elemente mit dem Tagnamen script beinhaltet.

for x in Tags: #Iteration um Cookiecrumb zu finden
html = x.text

if 'CrumbStore":{"crumb":"' in (html):
index = str.find(html, 'CrumbStore":{"crumb":"')
pointer = index + 21 # Letzter Punkt des obigen String (Crumbstore...)
break

#print(html)


while html[pointer + 1] != '"':
pointer = pointer + 1


cookiecrumb = html[index + 22:pointer + 1]


print(cookiecrumb)

link = "https://query1.finance.yahoo.com/v7/fin ... &crumb=%s/" % (cookiecrumb)

print(link)

csvfile = session.get(link)

print(csvfile)

session.close
Ich vermute, dass Yahoo den Cookie nicht akzeptiert. Aber sollte dieser nicht so lange Gültigkeit haben wie ich das selbe Session-Objekt verwende?
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Wie wäre es mit der Umsetzung von __blackjacks__ Tipps, damit wir effektiv helfen können?
Insbesondere der Code-Tags?
Er hat das ja nicht grundlos geschrieben.
Benutzeravatar
kbr
User
Beiträge: 1487
Registriert: Mittwoch 15. Oktober 2008, 09:27

Zumindest die while-Schleife kannst Du schon einmal ersetzen durch:

Code: Alles auswählen

start = index + 22
cookiecrumb = html[start:html.index('"', start)]
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

sparrow hat geschrieben: Samstag 13. Juli 2019, 15:18 Wie wäre es mit der Umsetzung von __blackjacks__ Tipps, damit wir effektiv helfen können?
Insbesondere der Code-Tags?
Er hat das ja nicht grundlos geschrieben.
Ich hatte ja geschrieben, dass ich nicht herausfinden konnte was Code-Tags sind. Wenn Du mir das sagst nehme ich diese Änderung gerne vor.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Im vollständigen Editor den </> Button drücken und zwischen die auftauchenden Tags den Code einfügen, damit die Einrückungen erhalten bleiben.

Und beachte seine Hinweise zur Benennung von Variablen.
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

kbr hat geschrieben: Samstag 13. Juli 2019, 15:21 Zumindest die while-Schleife kannst Du schon einmal ersetzen durch:

Code: Alles auswählen

start = index + 22
cookiecrumb = html[start:html.index('"', start)]
Darufhin bekomme ich folgende Fehlermeldung:
cookiecrumb = html[start:html.index('"', start)][/code]
^
SyntaxError: invalid syntax
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

sparrow hat geschrieben: Samstag 13. Juli 2019, 15:29 Im vollständigen Editor den </> Button drücken und zwischen die auftauchenden Tags den Code einfügen, damit die Einrückungen erhalten bleiben.

Und beachte seine Hinweise zur Benennung von Variablen.
Ok, jetzt verstehe ich was Du meinst. Dass die Einrückungen nicht korrekt dargestellt wurden war mir zwar aufgefallen aber ich wusste nicht wie ich es anpassen kann. Ich werde beim nächsten mal darauf achten.
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pythagon: Das [ /code] gehört ja nicht mehr zum Code sondern ist das End-Tag für's Forum um zu sagen das *da* der Code zu ende ist.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

__blackjack__ hat geschrieben: Samstag 13. Juli 2019, 15:36 @Pythagon: Das [ /code] gehört ja nicht mehr zum Code sondern ist das End-Tag für's Forum um zu sagen das *da* der Code zu ende ist.
Ok, jetzt hat es geläutet. Ich hatte bisher immer die Gänsefüßchen (Quote-Tags) verwendet.
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

Pythagon hat geschrieben: Samstag 13. Juli 2019, 15:31
kbr hat geschrieben: Samstag 13. Juli 2019, 15:21 Zumindest die while-Schleife kannst Du schon einmal ersetzen durch:

Code: Alles auswählen

start = index + 22
cookiecrumb = html[start:html.index('"', start)]
Darufhin bekomme ich folgende Fehlermeldung:
cookiecrumb = html[start:html.index('"', start)][/code]
^
SyntaxError: invalid syntax
Bitte ignorieren. Habe die while Schleife nun erfolgreich ersetzt. Funktioniert wunderbar! Besten Dank!
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

Kann es sein, dass ich den Cookiecrumb nicht nur in den Link einfügen muss sondern irgendwie zusätzlich mit dem Http-Request übergeben muss damit Yahoo erkennt, dass es die selbe Session ist?
Benutzeravatar
__blackjack__
User
Beiträge: 13103
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Pythagon: Wie und wo autorisierst Du Dich denn? Da wird doch wohl mehr erforderlich sein als vorher eine andere Seite abzurufen und da einen Parameter aus JavaScript-Code zu fischen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Pythagon
User
Beiträge: 52
Registriert: Mittwoch 3. Juli 2019, 18:21

Ich war jetzt davon ausgegangen es würde genügen den Cookiecrumb mit dem Link zu übergeben aber anscheinend erkennt Yahoo nicht, dass es sich um ein und die selbe Http-Session handelt und generiert bei jedem neuen Zugriff erneut einen anderen Cookiecrumb. Grundsätzlich sind diese csv files frei zugänglich. Man muss sich also weder einloggen, noch anderweitig authorisieren. Es muss lediglich die Cookieabfrage von Yahoo bestätigt werden wenn man das ganze mit einem Browser vornimmt.

Muss ich eine Session ID festlegen und diese mit übergeben mit jeder Anfrage?
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Evtl mal mit mechanize probieren. Ein paar Codebeispiele:
https://mechanize.readthedocs.io/en/latest/#quickstart

Das verhält sich noch ein bißchen ähnlicher wie ein Browser und löst das Problem mit den Cookies möglicherweise.

An welche Daten möchtest du denn genau kommen? Ich spreche hier natürlich vom eigentlichen Ziel und nicht von deinen Versuchen mit dem Cookiecrumb. Vielleicht liefert dir dann jemand eine Lösung oder zumindest entscheidende Schritte zum Weiterkommen....
Antworten