За последние 24 часа нас посетили 31979 программистов и 1380 роботов. Сейчас ищут 920 программистов ...

Проблемы с кодировкой при парсинге

Тема в разделе "PHP для новичков", создана пользователем Priler, 12 мар 2013.

  1. Priler

    Priler Активный пользователь

    С нами с:
    2 мар 2013
    Сообщения:
    171
    Симпатии:
    0
    Адрес:
    New-York
    Доброго времени суток!
    Я парсю страницу которая находится в кодировке windows-1251, либо в др. кодировках.
    Мой скрипт в кодировке utf-8.
    Вопрос в том, как определить кодировку загруженного HTML кода и перекодировать его в utf-8?
    Перекодировать я могу(iconv), а вот как определить незнаю. Регулярные выражения не помогают, mb_detect_encoding врет.
    Регялярками пытаюсь сделать так
    Код (Text):
    1.  
    2. preg_match('/(?<=charset=)(.*)(?=(?:")|(?:\'))/iu',$html, $matches);
    Но эта регулярка работает только в том случае, если $HTML в кодировке UTF-8.
    Вот и получается что определить не получается соответственно и весь скрипт летит в топку.
    Помогите пожалуйста решить проблему!
    Заранее спасибо.
     
  2. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    добро пожаловать в интернеты)))) тут кодировку много кто устанавливает. когда я писал поисковик я опирался на:
    1. то что вебмастер написал в метатеге своего сайта
    2. то что пришло заголовками от сервера
    у второго вес больше.
    на основании полученной кодировки я пробовал перегонять текст в юникод или сразу работал с ним
     
  3. Priler

    Priler Активный пользователь

    С нами с:
    2 мар 2013
    Сообщения:
    171
    Симпатии:
    0
    Адрес:
    New-York
    У меня парсер работает только с UTF-8 другие не признает, подскажи как решить проблему, на хабре кучу статей читал результат 0 все функции обманывают
     
  4. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    единственный вопрос - откуда страница берется? если с локальной файловой системы, то я пасс - много времени потратил на изобретение идеального распознания кодировки и все бес толку, если честно. если же от веб-серверов - то, как уже было описано выше - 1 это грабим метатег из исходного кода, 2 заменяем значение из первого шага кодировкой полученной от сервера (если такая была).

    и чтоб лишних вопросов не было сразу ясность вносим - 1 я как и ты через регулярки цеплял, 2 же разбор работы cURL с опцией CURLOPT_HEADER в true
     
  5. Priler

    Priler Активный пользователь

    С нами с:
    2 мар 2013
    Сообщения:
    171
    Симпатии:
    0
    Адрес:
    New-York
    Скрипт парсит сайты в сети, не локал.
    Вот именно что регулярки не работает если кодировка загруженной страницы не UTF-8
     
  6. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    теперь внимание вопрос. какое может быть значение у метатега кодировки? [a-z0-9-] - вот все варианты которые там можно встретить. флаг юникода тут не нужен.

    как может быть задана кодировка?
    по-старинке: <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    или ближе к хтмл5: <meta charset="utf-8">
    в мозгах вебмастеров могут быть отклонения от стандартов. например, одинарные кавычки для значения атрибутов или пробел(ы) между именем-равно-значением
     
  7. Priler

    Priler Активный пользователь

    С нами с:
    2 мар 2013
    Сообщения:
    171
    Симпатии:
    0
    Адрес:
    New-York
    Я все это учитываю и все равно не находит регулярка
     
  8. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    предлагаю тебе просто забить на это - пробела между именем и значением атрибута не будет по стандарту, а одинарные кавычки или их отсутствие допускается хоть и не прощается. если человек пишет инвалидные страницы - зачем тебе прогибаться под него? он метатегом может давать тебе одну кодировку, пых быть настроен на другую, апач на третью, а файл вообще в четвертой сохранен. это клиника уже, конечно, но и такое видали...
    ищи последовательность charset= и после нее одна какая-нибудь кавычка или ее не будет и дальше все подстановкой, указанной мною выше. и всё. да, можно конечно добавить еще что это обязательно метатег должен быть, а то вдруг речь в на странице идет про кодировку.
     
  9. Priler

    Priler Активный пользователь

    С нами с:
    2 мар 2013
    Сообщения:
    171
    Симпатии:
    0
    Адрес:
    New-York
    блин задолбала эта кодировка уже...
    вот например есть три сайта
    m.mail.ru
    stihi.in.ua
    php.ru

    у всех разные кодировки, мне просто искать в коде совпадения со строками
    charset=utf-8
    charset=windows-1251
    и др.?

    Добавлено спустя 40 минут 10 секунд:
    Вроде решил трабл так:
    Код (Text):
    1.  
    2. preg_match('/(?<=charset=)(.*)(?=(?:")|(?:\'))/iu',$html, $xd);
    3. if(strlen($xd['1'])!=0) {$charset=$xd['1'];} else {$charset='';}
    4.  
    5. if($charset=='') {
    6.    
    7. $zxdsl = iconv(detect_encoding($html),'utf-8', $html);
    8. preg_match('/(windows-1251)/iu',$zxdsl, $xd).'<hr />';
    9. if(strlen($xd['1'])!=0) {$charset=$xd['1'];} else {$charset='';}
    10.  
    11. if($charset!='') {$html=$zxdsl;}
    12. else
    13. {}
    14.  
    15. }
    Работает так:
    1)Если кодировка не найдена создаем переменную $zxdsl и присваиваем ей значение переменной $html только в перекодированном из полученной с помощью detect_encoding функции(она у меня в коде, чуть ниже скину код) в utf-8 виде, что бы английские буквы стали понятны
    2)Если кодировка найдена, значит все найс
    3)Если кодировка не найден то будь что будет, сами виноваты

    А вот собственно функция detect_encoding которая работает лучше чем mb_detect_encoding.
    Код (Text):
    1.  
    2. function detect_encoding($string, $pattern_size = 50)
    3. {
    4.     $list = array('cp1251', 'utf-8', 'ascii', '855', 'KOI8R', 'ISO-IR-111', 'CP866', 'KOI8U');
    5.     $c = strlen($string);
    6.     if ($c > $pattern_size)
    7.     {
    8.         $string = substr($string, floor(($c - $pattern_size) /2), $pattern_size);
    9.         $c = $pattern_size;
    10.     }
    11.  
    12.     $reg1 = '/(\xE0|\xE5|\xE8|\xEE|\xF3|\xFB|\xFD|\xFE|\xFF)/i';
    13.     $reg2 = '/(\xE1|\xE2|\xE3|\xE4|\xE6|\xE7|\xE9|\xEA|\xEB|\xEC|\xED|\xEF|\xF0|\xF1|\xF2|\xF4|\xF5|\xF6|\xF7|\xF8|\xF9|\xFA|\xFC)/i';
    14.  
    15.     $mk = 10000;
    16.     $enc = 'ascii';
    17.     foreach ($list as $item)
    18.     {
    19.         $sample1 = @iconv($item, 'cp1251', $string);
    20.         $gl = @preg_match_all($reg1, $sample1, $arr);
    21.         $sl = @preg_match_all($reg2, $sample1, $arr);
    22.         if (!$gl || !$sl) continue;
    23.         $k = abs(3 - ($sl / $gl));
    24.         $k += $c - $gl - $sl;
    25.         if ($k < $mk)
    26.         {
    27.             $enc = $item;
    28.             $mk = $k;
    29.         }
    30.     }
    31.     return $enc;
    32. }