2013-04-09 4 views
1

저는 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 ':' 

답변

1

이 방법을 고려하십시오.

는 정의 :

data RobotsDirective = RobotsDirective String String 

이것은 robots.txt 파일에서 구문 분석 지침을 나타냅니다. 첫 번째 문자열은 지시문 (즉, UserAgent, Allow, Disallow 등)이며 두 번째 문자열은 콜론 뒤에 오는 내용입니다.

지금의 파서 쓰기 RobotsDirective :

parseRD :: Parser RobotsDirective 

parseRD (문자, 숫자 및 대시를 포함 어쩌면 강조해야하는) 0 개 이상의 비 뒤에 콜론 지시문 이름을 찾습니다 - 줄 바 Y 기. 자. 적절한 경우 공백을 무시하십시오. parseRD과 같은 패턴을 발견하면 생성하고 RobotsDirective을 반환합니다. 그렇지 않으면 한 줄의 문자를 건너 뛰고 다시 시도합니다.

이제 RobotsDirective에 대한 파서가 있으므로 표준 방식으로 [RobotsDirective]에 대한 파서를 만들 수 있습니다.

이 파서는 지시문처럼 보이지 않는 모든 행을 건너 뛰고 공백 행, 주석 행 및 Don't allow...으로 시작하는 행을 포함합니다. 그러나, RobotsDirective "foo" "blah"을 반환하는 robots.txt 파일에 유효하지 않은 선, 즉 :

foo: blah 

에 대한 RobotsDirective을 반환 할 수 있습니다. robots.txt 파일을 구문 분석하고 RobotsDirective 값 목록을 얻은 후에는 해당 목록을 살펴보고 관심이없는 파일은 무시하십시오.

+0

이미 완료했습니다. 무슨 일이 일어날 필요가있다 그래서 나는 그 결과로 추가/비표준 지시어도 건너 뛸 수 있다는 나의 가정이 있기 때문에 특정 문자열과 일치하지 않는 라인을 건너 뛰어야 만한다. 나는 이미'string = '의'/ ='버전을 썼고, 그것을 바로 잡으려고한다. 우리가 볼거야. – Ixmatus

+0

당신의 접근법에 대한 또 다른 문제점은 robots.txt 파일에서 "위치에 따라"수행되는대로 지시어를 User-agent 헤더로 그룹화해야한다는 것입니다. (나는 바보 같아요.하지만 그건 모두 - 구글/빙/etc ... - 그들을 처리하고있다). 그래서 * RuleSet * 및 * RuleMap * 유형을 사용하면 쉽게 보행 할 수 있습니다. – Ixmatus

+0

내가 말한 것은 attoparsec을 사용하여 robots.txt 파일의 기본 구조를 구문 분석해야한다는 것입니다. 일단 [RobotsDirective]를 사용하면 쉽게 할 수 있습니다 : 1) 이해가되지 않는 지시어를 무시하고, 2) 지시어를 다시 정렬하십시오. – ErikR