1

私は robots.txt ファイルを解析しており、「整形式」の robots.txt ファイルを正常に解析するパーサーを作成しました。パーサーを調整して、シンボル (コメントの # や / など) で始まる行をスキップすることができましたが、inClass "#/".

私が解決できなかった問題の 1 つは、一致させたい文字列が含まれていない行をスキップすることです。

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

-- | @doParse@ 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 {} -> []

-- | @handlePartial@ 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 {})  = []

-- | @parseUABlock@ Parse a user-agent and rules block
parseUABlock = do
    ua    <- parseUACol *> uA
    rulez <- many1 parseRules

    return RuleSet { userAgent = UserAgent ua,
                     rules = rulez }

-- | @matchUACol@ 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)

-- | @parseRules@ 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"

-- | @parseDirectives@ Parse the directive column and any possibly
-- funny whitespace
parseDirectives = AC.skipSpace
                  *> directives -- <|> AC.stringCI "Crawl-delay" <|> AC.stringCI "Sitemap")
                  <* AC.skipSpace
                  <* AC.char8 ':'
4

1 に答える 1

1

このアプローチを検討してください。

定義:

data RobotsDirective = RobotsDirective String String

これは、robots.txt ファイル内の解析されたディレクティブを表します。最初の文字列はディレクティブ (つまり、、、UserAgentなど)AllowDisallow、2 番目の文字列はコロンの後のものです。

次に、 a のパーサーを作成しますRobotsDirective

parseRD :: Parser RobotsDirective

parseRDディレクティブ名 (文字、数字、ダッシュ、およびアンダースコアのみを含む必要があります) の後にコロンが続き、改行以外の文字が 0 個以上続くものを探します。必要に応じて空白を無視します。parseRDがそのようなパターンを見つけた場合、 を作成して返しますRobotsDirective。そうしないと、1 行の文字をスキップして再試行します。

のパーサーができたので、標準的な方法で のRobotsDirectiveパーサーを作成できます。[RobotsDirective]

このパーサーは、ディレクティブのように見えない行を単純にスキップします。これには、空白行、コメント行、および で始まる行が含まれますDon't allow...。ただし、RobotsDirectiverobots.txt ファイルでは無効な for 行が返される場合があります。つまり、次のようになります。

foo: blah

戻りRobotsDirective "foo" "blah"ます。robots.txt ファイルを解析してRobotsDirective値のリストを取得したら、そのリストを調べて、興味のないものは無視してください。

于 2013-04-10T05:08:53.063 に答える