Почему Haskell не обрабатывает персонажей с определенного сайта?
Мне было интересно, могу ли я написать программу Haskell для проверки обновлений некоторых романов по запросу, а веб-сайт, который я использую в качестве примера, - this. И у меня возникла проблема при отображении его содержимого (на mac el capitan). Простые коды:
import Network.HTTP
openURL :: String -> IO String
openURL = (>>= getResponseBody) . simpleHTTP . getRequest
display :: String -> IO ()
display = (>>= putStrLn) . openURL
Затем, когда я запускаю display "http://www.piaotian.net/html/7/7430/"
на ghci, появляются некоторые странные символы; первые строки выглядят так:
<title>×ß½øÐÞÏÉ×îÐÂÕ½Ú,×ß½øÐÞÏÉÎÞµ¯´°È«ÎÄÔĶÁ_Æ®ÌìÎÄѧ</title>
<meta http-equiv="Content-Type" content="text/html; charset=gbk" />
<meta name="keywords" content="×ß½øÐÞÏÉ,×ß½øÐÞÏÉ×îÐÂÕ½Ú,×ß½øÐÞÏÉÎÞµ¯´° Æ®ÌìÎÄѧ" />
<meta name="description" content="Æ®ÌìÎÄÑ§ÍøÌṩ×ß½øÐÞÏÉ×îÐÂÕ½ÚÃâ·ÑÔĶÁ£¬Ç뽫×ß½øÐÞÏÉÕ½ÚĿ¼¼ÓÈëÊղط½±ãÏ´ÎÔĶÁ,Æ®ÌìÎÄѧС˵ÔĶÁÍø¾¡Á¦ÔÚµÚһʱ¼ä¸üÐÂС˵×ß½øÐÞÏÉ£¬Èç·¢ÏÖδ¼°Ê±¸üУ¬ÇëÁªÏµÎÒÃÇ¡£" />
<meta name="copyright" content="×ß½øÐÞÏɰæÈ¨ÊôÓÚ×÷ÕßÎáµÀ³¤²»¹Â" />
<meta name="author" content="ÎáµÀ³¤²»¹Â" />
<link rel="stylesheet" href="/scripts/read/list.css" type="text/css" media="all" />
<script type="text/javascript">
Я также попытался загрузить как файл следующим образом:
import Network.HTTP
openURL :: String -> IO String
openURL = (>>= getResponseBody) . simpleHTTP . getRequest
downloading :: String -> IO ()
downloading = (>>= writeFile fileName) . openURL
Но после загрузки файла это похоже на фотографию:
![введите описание изображения здесь]()
Если я загружаю страницу с помощью python (например, с помощью urllib), символы отображаются нормально. Кроме того, если я пишу китайский html и разбираю его, то, похоже, нет проблем. Таким образом, кажется, что проблема находится на веб-сайте. Тем не менее, я не вижу разницы между персонажами сайта и теми, что я пишу.
Любая помощь по этой причине хорошо оценена.
P.S.
Код python выглядит следующим образом:
import urllib
urllib.urlretrieve('http://www.piaotian.net/html/7/7430/', theFic)
theFic = file_path
И файл все в порядке и хорошо.
Ответы
Ответ 1
Поскольку вы сказали, что вас интересуют только ссылки, нет необходимости конвертировать кодировку GBK в Unicode.
Вот версия, которая печатает все ссылки, такие как "123456.html" в документе:
#!/usr/bin/env stack
{- stack
--resolver lts-6.0 --install-ghc runghc
--package wreq --package lens
--package tagsoup
-}
{-# LANGUAGE OverloadedStrings #-}
import Network.Wreq
import qualified Data.ByteString.Lazy.Char8 as LBS
import Control.Lens
import Text.HTML.TagSoup
import Data.Char
import Control.Monad
-- match \d+\.html
isNumberHtml lbs = (LBS.dropWhile isDigit lbs) == ".html"
wanted t = isTagOpenName "a" t && isNumberHtml (fromAttrib "href" t)
main = do
r <- get "http://www.piaotian.net/html/7/7430/"
let body = r ^. responseBody :: LBS.ByteString
tags = parseTags body
links = filter wanted tags
hrefs = map (fromAttrib "href") links
forM_ hrefs LBS.putStrLn
Ответ 2
Я уверен, что если вы используете Network.HTTP
с типом String
, он преобразует байты в символы, используя вашу системную кодировку, которая, как правило, неверна.
Это только одна из нескольких причин, по которым мне не нравится Network.HTTP
.
Ваши варианты:
-
Используйте интерфейс Bytestring
. Почему-то это неудобно. Это также потребует, чтобы вы вручную декодировали байты в символы. Большинство сайтов дают вам кодировку в заголовках ответов, но иногда они лгут. На самом деле это гигантский беспорядок.
-
Используйте другую библиотеку для извлечения http. Я не думаю, что кто-то удаляет беспорядок, имея дело с ложными кодировками, но они по крайней мере не затрудняют неправильное использование системного кодирования. Вместо этого я рассмотрю wreq или http-client.
Ответ 3
Если вы просто хотите загрузить файл, например. чтобы посмотреть позже, вам просто нужно использовать интерфейс ByteString. Было бы лучше использовать http-client
для этого (или wreq
, если у вас есть знания об объективе). Затем вы можете открыть его в своем браузере, который увидит, что это файл gbk. До сих пор вы просто переносили необработанные байты как ленивые байты. Если я это понимаю, это все, что делает python. Кодировки здесь не являются проблемой; браузер обрабатывает их.
Но если вы хотите просмотреть символы внутри ghci, например, основная проблема заключается в том, что ничто не будет обрабатывать кодировку gbk по умолчанию, как это может сделать браузер. Для этого вам нужно что-то вроде text-icu
и базовых библиотек C. В приведенной ниже программе библиотека http-client
вместе с text-icu
- это я считаю довольно стандартным для этой проблемы, хотя вы могли бы использовать менее мощную библиотеку encoding
для такой же проблемы, как мы ее видели до сих пор, Кажется, что все в порядке:
import Network.HTTP.Client -- http-client
import Network.HTTP.Types.Status (statusCode)
import qualified Data.Text.Encoding as T -- text
import qualified Data.Text.IO as T
import qualified Data.Text as T
import qualified Data.Text.ICU.Convert as ICU -- text-icu
import qualified Data.Text.ICU as ICU
import qualified Data.ByteString.Lazy as BL
main :: IO ()
main = do
manager <- newManager defaultManagerSettings
request <- parseRequest "http://www.piaotian.net/html/7/7430/"
response <- httpLbs request manager
gbk <- ICU.open "gbk" Nothing
let txt :: T.Text
txt = ICU.toUnicode gbk $ BL.toStrict $ responseBody response
T.putStrLn txt
Здесь txt
- значение Text
, т.е. в основном просто "кодовые точки". Последний бит T.putStrLn txt
будет использовать системную кодировку для предоставления вам текста. Вы также можете явно обрабатывать кодировку с функциями в Data.Text.Encoding
или более сложным материалом в text-icu
Например, если вы хотите сохранить текст в кодировке utf8, вы должны использовать T.encodeUtf8
Итак, в моем ghci вывод выглядит так
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>走进修仙最新章节,走进修仙无弹窗全文阅读_飘天文学</title>
<meta http-equiv="Content-Type" content="text/html; charset=gbk" />
...
Правильно ли это выглядит? В моем ghci то, что я вижу, проходит через utf8, так как это моя системная кодировка, но обратите внимание, что файл говорит, что это файл gbk, конечно. Если тогда вы хотели сделать некоторое преобразование Text
, а затем сохранили его как html файл - тогда, конечно, вам нужно убедиться, что кодировка, указанная внутри файла, соответствует кодировке, которую вы использовали, чтобы сделать bytestring, которую вы пишете, файл.
Вы также можете получить это в форме Haskell String
, заменив последние три строки на
let str :: String
str = T.unpack $ ICU.toUnicode gbk $ BL.toStrict $ responseBody response
putStrLn str
Ответ 4
Вот обновленный ответ, в котором используется пакет encoding
для преобразования GBK-кодированного содержимого в Unicode.
#!/usr/bin/env stack
{- stack
--resolver lts-6.0 --install-ghc runghc
--package wreq --package lens --package encoding --package binary
-}
{-# LANGUAGE OverloadedStrings #-}
import Network.Wreq
import qualified Data.ByteString.Lazy.Char8 as LBS
import Control.Lens
import qualified Data.Encoding as E
import qualified Data.Encoding.GB18030 as E
import Data.Binary.Get
main = do
r <- get "http://www.piaotian.net/html/7/7430/"
let body = r ^. responseBody :: LBS.ByteString
foo = runGet (E.decode E.GB18030) body
putStrLn foo
Ответ 5
Эта программа производит тот же вывод, что и команда curl:
curl "http://www.piaotian.net/html/7/7430/"
Тест с помощью
stack program > out.html
open out.html
(Если не использовать stack
, просто установите пакеты wreq
и lens
и выполните с помощью runhaskell
.)
#!/usr/bin/env stack
-- stack --resolver lts-6.0 --install-ghc runghc --package wreq --package lens --package bytestring
{-# LANGUAGE OverloadedStrings #-}
import Network.Wreq
import qualified Data.ByteString.Lazy.Char8 as LBS
import Control.Lens
main = do
r <- get "http://www.piaotian.net/html/7/7430/"
LBS.putStr (r ^. responseBody)