저는 robots.txt 파일을 구문 분석 중이며 올바르게 작성된 robots.txt 파일을 구문 분석하기 위해 파서를 작성했습니다. 필자는 파서가 # 또는/또는 주석과 같이 기호로 시작하는 줄을 건너 뛰도록 조정할 수 있었지만 inClass "#/"
만 사용했습니다.Attoparsec/= stringCI 버전
해결할 수없는 한 가지 문제는 일치시키고 자하는 문자열이 포함되어 있지 않으면 줄을 건너 뛰는 것입니다.
User-agent: *
Disallow: /includes/
Disallow: /misc/
Disallow: /modules/
Doesn't belong here
Disallow: /profiles/
Disallow: /scripts/
Disallow: /themes/
내가 먼저 사용하여 일치하는 시도 :
satisfy (notInClass "DdUu") *> skipWhile (not . isEndOfLine)
을 그리고 해시 또는 슬래시 문자에 해당하지 않는 특정 주석 행 파서에 대한 나의 필요를 부정하는 것입니다 그런 식으로 일을 생각 수업. 문제는 이것이 작동하지 않는다는 것입니다.
'허용하지 않음'과 '허용하지 않음'과 같은 일치 항목을 해결할 수 없기 때문에 가능하다면 언제든지 작동하지 않는다는 것을 알았습니다.
여기 (이것은 단지 잘 형성 robots.txt를 작동, 주석 건너 뛰는 코드없이) 구문 분석 코드입니다 :
{-# LANGUAGE OverloadedStrings, RankNTypes #-}
import Prelude hiding (takeWhile)
import Control.Applicative hiding (many)
import Data.Char
import Data.Text as T hiding (toLower)
import Data.Text.Encoding as E
import Control.Monad
import Data.Attoparsec.ByteString
import qualified Data.Attoparsec.Char8 as AC
import Data.Array.Unboxed
import Data.ByteString as B hiding (takeWhile)
import qualified Data.ByteString.Internal as BI
import Data.Word (Word8)
type RuleMap = [(ByteString, ByteString)]
-- newtype for indexable ua
newtype UserAgent = UserAgent { unUA :: ByteString }
deriving (Eq, Ord, Show)
data RuleSet = RuleSet
{ userAgent :: UserAgent,
rules :: RuleMap }
deriving (Eq, Ord, Show)
main = do
r <- B.readFile "/Users/ixmatus/Desktop/robots.txt"
print $ parse (many1 parseUABlock) r
stripper = E.encodeUtf8 . T.strip . E.decodeUtf8
isNotEnd = not . AC.isEndOfLine
-- | Catch all character matching, basically
matchALL :: Word8 -> Bool
matchALL = inClass ":/?#[]@!$&'()*%+,;=.~a-zA-Z0-9 _-"
-- | @[email protected] Run the parser, complete the partial if the end of the stream has
-- a newline with an empty string
doParse :: ByteString -> [RuleSet]
doParse cont =
case parse (many1 parseUABlock) cont of
Done _ set -> set
Partial f -> handlePartial (f B.empty)
Fail {} -> []
-- | @[email protected] Handle a partial with empty string by simply
-- returning the last completion
handlePartial :: forall t a. IResult t [a] -> [a]
handlePartial (Done _ r) = r
handlePartial (Fail {}) = []
-- | @[email protected] Parse a user-agent and rules block
parseUABlock = do
ua <- parseUACol *> uA
rulez <- many1 parseRules
return RuleSet { userAgent = UserAgent ua,
rules = rulez }
-- | @[email protected] Parse the UA column and value taking into account
-- possible whitespace craziness
parseUACol = AC.skipSpace
*> AC.stringCI "User-Agent"
<* AC.skipSpace
*> AC.char8 ':'
*> AC.skipSpace
uA = do
u <- takeWhile1 isNotEnd
return (stripper u)
-- | @[email protected] Parse the directives row
parseRules = (,) <$> parseTransLower
<*> directiveRule
directiveRule = do
rule <- takeWhile1 matchALL <* many1 AC.endOfLine
return (stripper rule)
parseTransLower = do
res <- parseDirectives <* AC.skipSpace
return (lowercase res)
ctypeLower = listArray (0,255) (Prelude.map (BI.c2w . toLower) ['\0'..'\255']) :: UArray Word8 Word8
lowercase = B.map (\x -> ctypeLower!x)
directives = AC.stringCI "Disallow" <|> AC.stringCI "Allow"
-- | @[email protected] Parse the directive column and any possibly
-- funny whitespace
parseDirectives = AC.skipSpace
*> directives -- <|> AC.stringCI "Crawl-delay" <|> AC.stringCI "Sitemap")
<* AC.skipSpace
<* AC.char8 ':'
이미 완료했습니다. 무슨 일이 일어날 필요가있다 그래서 나는 그 결과로 추가/비표준 지시어도 건너 뛸 수 있다는 나의 가정이 있기 때문에 특정 문자열과 일치하지 않는 라인을 건너 뛰어야 만한다. 나는 이미'string = '의'/ ='버전을 썼고, 그것을 바로 잡으려고한다. 우리가 볼거야. – Ixmatus
당신의 접근법에 대한 또 다른 문제점은 robots.txt 파일에서 "위치에 따라"수행되는대로 지시어를 User-agent 헤더로 그룹화해야한다는 것입니다. (나는 바보 같아요.하지만 그건 모두 - 구글/빙/etc ... - 그들을 처리하고있다). 그래서 * RuleSet * 및 * RuleMap * 유형을 사용하면 쉽게 보행 할 수 있습니다. – Ixmatus
내가 말한 것은 attoparsec을 사용하여 robots.txt 파일의 기본 구조를 구문 분석해야한다는 것입니다. 일단 [RobotsDirective]를 사용하면 쉽게 할 수 있습니다 : 1) 이해가되지 않는 지시어를 무시하고, 2) 지시어를 다시 정렬하십시오. – ErikR