» »

[Python] Response encoding

[Python] Response encoding

HotBurek ::

Dobro jutro.

Evo, nov dan, nov izziv.

Tokrat je izziv z uporabo requests modula za python.

Zanima me, kako oz. po kakšni logiki ta modul določi encoding, da uspešno dekodira response za spodnji primer.

Sample code:
import requests;

url = "https://shop.itallibri.de/Letteratura-italiana/Le-notti-difficili::733.html";

response = requests.get(url);

print(response.encoding);
# utf-8

print(response.headers);
# {
  'Server': 'nginx',
  'Date': 'Sat, 20 Jan 2024 13:17:18 GMT',
  'Content-Type': 'text/html; charset=utf-8',
  'Transfer-Encoding': 'chunked',
  'Connection': 'keep-alive',
  'X-Powered-By': 'PHP/7.4.33, PleskLin',
  'Expires': 'Thu, 19 Nov 1981 08:52:00 GMT',
  'Cache-Control': 'no-store, no-cache, must-revalidate, max-age=1, private, must-revalidate',
  'Pragma': 'no-cache',
  'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '1',
  'X-Content-Type-Options': 'nosniff',
  'Content-Encoding': 'gzip',
  'Vary': 'Accept-Encoding',
  'Set-Cookie': 'MODsid=0123456789; path=/; domain=.shop.itallibri.de; secure; HttpOnly',
  'X-Cache-Status': 'MISS'
}

print(response.content.decode(response.encoding));
# error

print(response.text);
# ok

Se pravi, response objekt za funkcijo text pravi:

Content of the response, in unicode. If Response.encoding is None, encoding will be guessed using ``charset_normalizer`` or ``chardet``. The encoding of the response content is determined based solely on HTTP headers, following RFC 2616 to the letter. If you can take advantage of non-HTTP knowledge to make a better guess at the encoding, you should set ``r.encoding`` appropriately before accessing this property.

Torej bi morala "text" funkcija uporabit utf-8.

Ampak če vsebino poizkušam dekodirat z utf-8, dobim error:
UnicodeDecodeError:
'utf-8' codec can't decode byte 0xdc in position 56196: invalid continuation byte

Zakaj vrne error?
root@debian:/# iptraf-ng
fatal: This program requires a screen size of at least 80 columns by 24 lines
Please resize your window
  • spremenilo: HotBurek ()

Ales ::

Ker slepo zaupaš nekomu, ki je v 'Content-Type' zapisal, da je UTF-8, dejansko pa zajeta vsebina ni pravilen UTF-8 ali pa sploh ni UTF-8?

bemfa ::

Browser ob obisku URL-ja izpiše "The byte stream was erroneous according to the character encoding that was declared. The character encoding declaration may be incorrect." v konzoli, tak da potrjuje Alesovo domnevo.

Zgodovina sprememb…

  • spremenilo: bemfa ()

LightBit ::

Če hočeš da ignorira neveljavne znake uporabi:
response.content.decode(response.encoding, errors='ignore')

mm&r ::

Tukaj gre za primer chunked encoding torej dobivaš iz strežnika stream chunkov, kot vidiš v headerju 'Transfer-Encoding': 'chunked'.

Najprej moraš prebrat vse podatke, potem boš pa decodal.

HotBurek ::

Ok, to mi je dosti pomagalo.

Sem prvo nastavitl na "ignore", a rezultat ni bil isti, kot ga vrača funkcija text za response objekt.

Potem sem pa pogruntal, da lahko grem pogledat, kako je ta (text) funkcija narejena in sem hitro našel, da uporablja "replace".

Se pravi, končna rešitev:

response.content.decode(response.encoding, errors="replace");
root@debian:/# iptraf-ng
fatal: This program requires a screen size of at least 80 columns by 24 lines
Please resize your window

Zgodovina sprememb…

  • spremenilo: HotBurek ()

mm&r ::

HotBurek je izjavil:

Ok, to mi je dosti pomagalo.

Sem prvo nastavitl na "ignore", a rezultat ni bil isti, kot ga vrača funkcija text za response objekt.

Potem sem pa pogruntal, da lahko grem pogledat, kako je ta (text) funkcija narejena in sem hitro našel, da uporablja "replace".

Se pravi, končna rešitev:

response.content.decode(response.encoding, errors="replace");


Pa si prepričan, da ti to dela pravilno, ta koda povzroči samo to da klic metode ne crkne?

errors="replace" pomeni, da se znaki, ki jih ne zna iz unicode dekodirat zamenjajo z '�', torej na koncu preveri še vsebino, ki si jo dekodiral, če niso kakšni '�' vmes.

mr_chai ::

Pa je tist kar dobis iz serverja res utf-8 ? Glede na error, ki si ga dobil bi rekel da ne.
Za foro probaj dekodirati v latin-1

Rias Gremory ::

@burek
Poskusi Phind, je kot iskalnik za razvijalce.
Mirno gledamo, kako naš svet propada,
saj za časa našega življenja ne bo popolnoma propadel.

HotBurek ::

mm&r, to sedaj gledam in ja, so smotani znaki na output-u.

Naredil sem test (glej sliko spodaj):

- Levo zgoraj utf-8 + errors=replace
- Desno zgoraj iso-8859-1 (zanimivo, da za ta encoding ne vrača error-jev sploh)
- Spodaj na sredini: firefox view-source



mr_chai, sem poizskusil tudi z latin-1, a je isti šmorn, kot iso-8859-1.

Tole je pa link do serverja, ki vrača to, kar pač vrača:

https://shop.itallibri.de/Letteratura-i...
root@debian:/# iptraf-ng
fatal: This program requires a screen size of at least 80 columns by 24 lines
Please resize your window

Zgodovina sprememb…

  • spremenilo: HotBurek ()

mr_chai ::

iso-8859-1 je latin-1, gre za isti encoding.

probaj se 8859-2. Pa mora biti nek lib v pythonu ki ti ugane encoding.

Zgodovina sprememb…

  • spremenilo: mr_chai ()

HotBurek ::

Isto so grabljice.

Encoding je 100% utf-8: ga vrne tako na HTTP, kot na HTML.

Problem je tam okrog enojnega narekovaja (l'incubo). Tam ne vem, kaj se nahaja.

Kako bi lahko za določeno pozicijo, en korak nazaj, en pa naprej, prebral bay-te iz responsa?

Posamezen byte lahko izpišem, ampak ugibam, da morajo biti po 4-je skupaj.

Tako, da si lahko naredil nek kratek string baytov, namesto vseh špagetov.
root@debian:/# iptraf-ng
fatal: This program requires a screen size of at least 80 columns by 24 lines
Please resize your window

Ales ::

HotBurek je izjavil:

Isto so grabljice.

Encoding je 100% utf-8: ga vrne tako na HTTP, kot na HTML.

Kaj ti "vrne" encoding, s čim ga ugotavljaš? Slepo verjameš nečemu, kar je nekdo ročno definiral nekje, ali..?

Problem je tam okrog enojnega narekovaja (l'incubo). Tam ne vem, kaj se nahaja.

Kako bi lahko za določeno pozicijo, en korak nazaj, en pa naprej, prebral bay-te iz responsa?

Kaj je to bay? >:D

Biti in bajti, se reče pri nas, izberi po potrebi... tudi če že v angleščini, striktno prav piši, da ne bo zmede.

Posamezen byte lahko izpišem, ampak ugibam, da morajo biti po 4-je skupaj.

Tako, da si lahko naredil nek kratek string baytov, namesto vseh špagetov.

Za začetek poglej dokumentacijo funkcij in knjižnic, ki jih uporabljaš. Ni hudiča, da ne bi znal izpisati več kot en bit. Zdaj, koliko jih boš izpisal in kje boš začel... vidiš problem?

Ne svetujem ti, da greš pisati lastne funkcije za izpis posameznih znakov iz nekega encodiranega stringa. To ni pravi pristop k reševanju težave.

HotBurek ::

Zdejle sem šel "korak po korak" gledat v tist del stringa, kjer se pojavi napaka.

Tole je ta master class koda:

import requests;

url = "https://shop.itallibri.de/Letteratura-italiana/Le-notti-difficili::733.html";

response = requests.get(url);

index_1 = 272;

print("hex string" + "\t\t\t\t" + "str(bytes)" + "\t\t\t" + "utf-8");

for i in range(0, 6):

    hex_string = str(hex(response.content[index_1])) + " " + \
        str(hex(response.content[index_1 + 1])) + " " + \
        str(hex(response.content[index_1 + 2])) + " " + \
        str(hex(response.content[index_1 + 3]));

    # print(hex_string);

    bytes_1 = bytes.fromhex(hex_string.replace("0x", ""));

    # print(bytes_1);
    
    index_1 = index_1 + 4;

    print(hex_string + "\t\t" + str(bytes_1) + "\t\t\t\t" + "->>>" + str(bytes_1.decode("utf-8")) + "<<<-");

Output:

hex string			str(bytes)			utf-8
0x6f 0x73 0x61 0x2c		b'osa,'				->>>osa,<<<-
0x20 0x72 0x65 0x61		b' rea'				->>> rea<<<-
0x6c 0x65 0x2c 0x20		b'le, '				->>>le, <<<-
0x6c 0xe2 0x80 0x99		b'l\xe2\x80\x99'		->>>l'<<<-
0x69 0x6e 0x63 0x75		b'incu'				->>>incu<<<-
0x62 0x6f 0x2c 0x20		b'bo, '				->>>bo, <<<-

Tu pa decoding z utf-8 dela.

Tole je pa string v tistem rangu: misteriosa, reale, l'incubo, della

Krneki...
root@debian:/# iptraf-ng
fatal: This program requires a screen size of at least 80 columns by 24 lines
Please resize your window

Zgodovina sprememb…

  • spremenilo: HotBurek ()

HotBurek ::

Sedajle sem našel en nasty fix:

print(response.content.replace(b"\xe2\x80\x99", b"\x27").decode("utf-8", errors="replace"));

Povezave:

https://stackoverflow.com/questions/448...
https://stackoverflow.com/questions/766...

Tole je hint:

fox's uses U+0027 (APOSTROPHE), "brown" uses U+201C (LEFT DOUBLE QUOTATION MARK) and U+201D (RIGHT DOUBLE QUOTATION MARK), and it's uses U+2019 (RIGHT SINGLE QUOTATION MARK)
root@debian:/# iptraf-ng
fatal: This program requires a screen size of at least 80 columns by 24 lines
Please resize your window

Ales ::

Torej zdaj kar apriori spreminjaš vsak U+2019 (right single quotation mark) v nekaj drugega?

Se ti to zdi prav? Bo vedno prav?

Kaj je sploh problem:

Python 3.11.6 (main, Nov 14 2023, 09:36:21) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import requests
>>> from bs4 import BeautifulSoup
>>> 
>>> url = "https://shop.itallibri.de/Letteratura-italiana/Le-notti-difficili::733.html"
>>> sestavinezajuhco = requests.get(url).text
>>> 
>>> juha = BeautifulSoup(sestavinezajuhco, 'html.parser')
>>> #print(juha.prettify())
>>> 
>>> sestavine = juha.find_all("meta")
>>> #print(sestavine)
>>> 
>>> for s in sestavine:
...     if 'name' in s.attrs:
...         spaget = s.attrs['name']
...         norispageti = ['keywords', 'Keywords']
...         if spaget in norispageti:
...             njam = s.attrs['content']
...             print(njam)
... 
notti, difficili, racconti, brevi, ancora, volta, dimensione, misteriosa, reale, l&#8217;incubo, della, paura, morte, quotidianit&#224;, percorsa

kuall ::

Ales + burek = drim tim:D

HotBurek ::

Ales, ja tiste posevne narekovaje (enojne, dvoje) želim spremenit v te "navadne" narekovaje, brez raznih poševnosti.

Kar se pa parsanja tiče, v mojem primeru ne uporabljam bs4. Mogoče so v bs4 stvari drugače porihtane.

Trenutno sem za te "poševne" narekovaje rešil takole:

response_content = response.content;

# LEFT SINGLE QUOTATION MARK
response_content = response_content.replace(b"\xe2\x80\x98", b"\x27");

# RIGHT SINGLE QUOTATION MARK
response_content = response_content.replace(b"\xe2\x80\x99", b"\x27");

# LEFT DOUBLE QUOTATION MARK
response_content = response_content.replace(b"\xe2\x80\x9c", b"\x22");

# RIGHT DOUBLE QUOTATION MARK
response_content = response_content.replace(b"\xe2\x80\x9d", b"\x22");

# set html response text
html_text = response_content.decode(use_this_encoding, errors="replace");

S tem sem rešil "vse" probleme, razen enega. Umlaut!

Sem "vzel" tvoj sample, in res, za poševne narekovaje dela.

Ampak za umlaut, pa vseeno vrača nek znak z vprašajem. Tudi v Firefox-u ne dela. Tu je pa verjetno res problem na strani serverja.

Umlaut je vnešen v alt tag od img elementa. V html source se ga lahko poišče z "berweisung Logo".
root@debian:/# iptraf-ng
fatal: This program requires a screen size of at least 80 columns by 24 lines
Please resize your window

Zgodovina sprememb…

  • spremenilo: HotBurek ()

Ales ::



Vredno ogleda ...

TemaSporočilaOglediZadnje sporočilo
TemaSporočilaOglediZadnje sporočilo
»

[Python] Kako dekodirat sledeč string?

Oddelek: Programiranje
331652 (940) Spura
»

Kaj pomeni %uFFFC v URL-ju?

Oddelek: Programiranje
5796 (541) HotBurek
»

[Java] Prevajanje in šumniki v ubuntu

Oddelek: Programiranje
202453 (2070) mmaestro
»

Jasper subreport

Oddelek: Programiranje
131612 (1361) nightrage
»

Kako z VS.NET priti do izvorne kode neke html strani?

Oddelek: Programiranje
91324 (1119) Microsoft

Več podobnih tem