Верстакфорум практиков
рекламаiprazon: приватные серверные адреса IPv4 и SOCKS5, безлимитный трафик, бесплатный тест до 2 часов
ФорумРазбор ошибок

Кодировка в ответе сломана только у части страниц

Борис Лапин
Борис Лапин
Знаток
сообщений 1960
с мар
1 сентября, 08:32первое сообщение

Сайт один, раздел один, страницы отличаются только адресом. Из тысячи карточек примерно сто двадцать приходят кракозябрами, остальные читаются нормально.

Открываю такую страницу браузером: всё на месте. Тяну скриптом: «Ïðèâåò» вместо названия.

Скрипт простейший:

r = requests.get(u, timeout=20)
html = r.text

Что за выборочность такая?

rawsocket
rawsocket
Ветеран
сообщений 2290
с фев
8 февраля, 11:49#2

Печатайте рядом с адресом две вещи: r.headers.get("content-type") и r.encoding. Спорим, у сломанных ста двадцати в заголовке нет упоминания кодировки.

читаю байты, а потом уже документацию
Настя Лужина
Настя Лужина
Новичок
сообщений 265
с янв, второй сезон
15 июля, 14:06#3

у меня похожее было на прошлой неделе, помогло вот это

r.encoding = r.apparent_encoding
html = r.text
Борис Лапин
Борис Лапин
Знаток
сообщений 1960
с мар
22 декабря, 17:23#4
rawsocket: у сломанных ста двадцати в заголовке нет упоминания кодировки

Ровно так. У нормальных text/html; charset=windows-1251, у сломанных просто text/html.

Совет Насти помог примерно на девяноста страницах из ста двадцати. Тридцать штук всё равно приходят мусором, причём мусор другой на вид.

ostrov
ostrov
Участник
сообщений 505
с окт
1 мая, 08:40#5

Тут работают два разных механизма, поэтому и лечится в два приёма.

Первый. Когда сервер отдаёт text/* без указания кодировки, requests по старому соглашению подставляет ISO-8859-1. Байты при этом целые, а буквы получаются латинские с крышечками, это ваш первый случай. apparent_encoding спрашивает определитель, тот считает частоты байтов и отвечает догадкой. На связном русском тексте догадка почти всегда верная, на короткой карточке из трёх слов и артикула определитель промахивается. Отсюда ваши тридцать штук.

Второй приём надёжнее. Берите тот порядок, по которому идёт браузер: отметка в начале файла, потом заголовок ответа, потом meta внутри самой страницы. Последнюю ступень requests пропускает, хотя именно там у половины старых движков и написана правда.

Проще всего отдать эту работу разборщику, он умеет читать meta сам:

from lxml import html as lh
doc = lh.document_fromstring(r.content)   # именно content, байты

Обратите внимание на r.content. Как только вы взяли r.text, байты уже превращены в буквы по чужой догадке, и дальше спасать нечего.

okhotnik
okhotnik
Участник
сообщений 720
с авг
8 октября, 11:57#6

А выгрузку вы чем пишете? Мусор бывает и не от сайта.

tabless
tabless
Участник
сообщений 800
с июл
15 марта, 14:14#7

Присоединюсь к okhotnik. Есть две приметы, по которым видно, где именно ошиблись.

Как выглядитЧто произошлоЧем чинится
Ïðèâåòбайты cp1251 прочитали как латиницуs.encode("latin-1").decode("cp1251")
Приветбайты utf-8 прочитали как cp1251s.encode("cp1251").decode("utf-8")

Если в выгрузке видите второй вид, сайт тут ни при чём, промах случился на записи файла.

Настя Лужина
Настя Лужина
Новичок
сообщений 265
с янв, второй сезон
22 августа, 17:31#8

о, вторую примету я как раз и ловила в экселе

rawsocket
rawsocket
Ветеран
сообщений 2290
с фев
1 января, 08:48#9

Настя, эксель отдельная песня. Он открывает csv по системной кодировке и на utf-8 без отметки в начале файла показывает ровно такую кашу. Пишите с encoding="utf-8-sig", отметка встанет сама и таблица откроется как надо.

К слову о проверке: сравнивать глазами долго. Прогоните готовую выгрузку через простую проверку на подозрительные буквы, они в русском тексте почти не встречаются.

podozr = set("ÐÑÒÓÔÕרÙÚÛÜÝÞß")
for nomer, stroka in enumerate(open("out.csv", encoding="utf-8"), 1):
    if podozr & set(stroka):
        print(nomer, stroka[:80])
читаю байты, а потом уже документацию
ostrov
ostrov
Участник
сообщений 505
с окт
8 июня, 11:05#10

И держите список сломанных адресов отдельным файлом. Через пару прогонов станет видно, привязаны они к разделу сайта или разбросаны как попало.

Борис Лапин
Борис Лапин
Знаток
сообщений 1960
с мар
15 ноября, 14:22#11

Собрал по итогу такой порядок действий.

Тяну r.content, разбор отдаю lxml, он сам разбирает отметку и meta. Оставшиеся спорные страницы прогоняю через определитель и, если тот сомневается, беру кодировку из заголовка соседней карточки того же раздела. Выгрузка пишется с отметкой в начале.

Тысяча карточек прошла без единой кракозябры, ostrov про meta попал точно: у тех тридцати штук в шапке страницы стояла своя кодировка, отличная от остального сайта. Спасибо всем.