为什么Haskell不能处理特定网站的字符?

awl*_*wer 15 html haskell content-type html-parsing non-ascii-characters

我想知道我是否可以编写一个Haskell程序来按需检查一些小说的更新,我使用的网站就是这个.我在显示它的内容时遇到了问题(在ma​​c capitan上).简单的代码如下:

import Network.HTTP

openURL :: String -> IO String
openURL = (>>= getResponseBody) . simpleHTTP . getRequest

display :: String -> IO ()
display = (>>= putStrLn) . openURL
Run Code Online (Sandbox Code Playgroud)

然后,当我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">
Run Code Online (Sandbox Code Playgroud)

我也尝试下载如下文件:

import Network.HTTP

openURL :: String -> IO String
openURL = (>>= getResponseBody) . simpleHTTP . getRequest

downloading :: String -> IO ()
downloading = (>>= writeFile fileName) . openURL
Run Code Online (Sandbox Code Playgroud)

但下载文件后,就像在照片中一样: 在此输入图像描述

如果我通过python下载页面(例如使用urllib),则会正常显示字符.另外,如果我写一个中文html并解析它,那么似乎没有问题.因此,似乎问题出现在网站上.但是,我认为网站的字符与我写的字符没有任何区别.

任何有关这背后原因的帮助都非常感谢.

PS
python代码如下:

import urllib

urllib.urlretrieve('http://www.piaotian.net/html/7/7430/', theFic)

theFic = file_path
Run Code Online (Sandbox Code Playgroud)

文件都很好,很好.

Car*_*arl 8

我很确定如果您使用Network.HTTPString类型,它会使用您的系统编码将字节转换为字符,这通常是错误的.

这只是我不喜欢的几个原因之一Network.HTTP.

你的选择:

  1. 使用Bytestring界面.由于某种原因,它更尴尬.它还需要您手动将字节解码为字符.大多数网站都会在响应标头中为您提供编码,但有时它们会撒谎.这真是一个巨大的混乱.

  2. 使用不同的http抓取库.我认为没有任何消除处理谎言编码的混乱,但它们至少不会使不使用系统编码错误更尴尬.我会调查wreqhttp-client.


Mic*_*ael 6

如果您只想下载文件,例如稍后查看,您只需要使用ByteString接口.最好是为此使用http-client(或者wreq如果你有一些镜头知识).然后你可以在浏览器中打开它,这将看到它是一个gbk文件.到目前为止,您只需将原始字节作为lazy bytestring进行传输.如果我明白了,那就是所有的python都在做.编码不是问题; 浏览器正在处理它们.

但是,如果你想查看ghci中的字符,例如,主要的问题是没有什么能像浏览器一样默认处理gbk编码.为此你需要类似的东西text-icu和底层的C库.下面的程序使用了http-clienttext-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
Run Code Online (Sandbox Code Playgroud)

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" />

...
Run Code Online (Sandbox Code Playgroud)

那看起来不错吗?在我的ghci中,我看到的是通过utf8,因为这是我的系统编码,但请注意,该文件说它是一个gbk文件,当然.那么,如果你想进行一些Text转换,然后将其保存为html文件 - 那么当然你需要确保文件中提到的字符集与你用来创建写入文件的字节串的编码相匹配.

当然,您也可以String通过替换最后三行来将其变为Haskell的形状

let str :: String
    str = T.unpack $ ICU.toUnicode gbk $ BL.toStrict $ responseBody response
putStrLn str
Run Code Online (Sandbox Code Playgroud)

  • 要使用上面的代码片段编写一个"gbk"编码的文件,你需要编写`B.writeFile"this.html"$ ICU.fromUnicode gbk $ T.pack str` (2认同)

Eri*_*ikR 6

这是一个更新的答案,它使用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
Run Code Online (Sandbox Code Playgroud)


Eri*_*ikR 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
Run Code Online (Sandbox Code Playgroud)