awl*_*wer 15 html haskell content-type html-parsing non-ascii-characters
我想知道我是否可以编写一个Haskell程序来按需检查一些小说的更新,我使用的网站就是这个.我在显示它的内容时遇到了问题(在mac 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)
文件都很好,很好.
我很确定如果您使用Network.HTTP
该String
类型,它会使用您的系统编码将字节转换为字符,这通常是错误的.
这只是我不喜欢的几个原因之一Network.HTTP
.
你的选择:
使用Bytestring
界面.由于某种原因,它更尴尬.它还需要您手动将字节解码为字符.大多数网站都会在响应标头中为您提供编码,但有时它们会撒谎.这真是一个巨大的混乱.
使用不同的http抓取库.我认为没有任何消除处理谎言编码的混乱,但它们至少不会使不使用系统编码错误更尴尬.我会调查wreq或http-client.
如果您只想下载文件,例如稍后查看,您只需要使用ByteString接口.最好是为此使用http-client
(或者wreq
如果你有一些镜头知识).然后你可以在浏览器中打开它,这将看到它是一个gbk文件.到目前为止,您只需将原始字节作为lazy bytestring进行传输.如果我明白了,那就是所有的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
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)
这是一个更新的答案,它使用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)
由于您说您只对链接感兴趣,因此无需将 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)
归档时间: |
|
查看次数: |
960 次 |
最近记录: |