2017-01-25 5 views
1

この問題と解決方法を明確にするために、次のように書き直して、関数と解決策を一例として底部に残しました。ヘルプのJohn Colemanにもう一度感謝します!ファンクションスローエラーはベクトルに適用されますが、1つの要素には適用されません

問題:私はこのエラーを投げ、URLのベクトルを1つのURLを渡すときに働いていたデータ掻き取り機能を作成しますが、ない:

Error in data.frame(address, recipename, prept, cookt, calories, protein, : arguments imply differing number of rows: 1, 14, 0

をそれはURLの一部がIだったことが判明スクラップしようとすると、指示セクションに別のタグがありました。これにより、xpathSApplyのスクラップが発生し、長さ0のリストが返され、rbindに渡されたときにエラーが発生しました。

問題の特定は、エラーが発生したページを見つけてそのページのHTML構造を調べるまで、各URLを実行するだけの問題でした。ここで

は、私はもともと書いた関数である:

f4fscrape <- function(url) { 

#Create an empty dataframe 

    df <- data.frame(matrix(ncol = 11, nrow = 0)) 
    colnames <- c('address', 'recipename', 'prept', 'cookt', 
        'calories', 'protein', 'carbs', 'fat', 
        'servings', 'ingredients', 'instructions') 
    colnames(df) <- paste(colnames) 

    #check for the recipe url in dataframe already, 
    #only carry on if not present 

    for (i in length(url)) 
      if (url[i] %in% df$url) { next } 
    else { 

    #parse url as html 

    doc2 <-htmlTreeParse(url[i], useInternalNodes = TRUE) 

    #define the root node 

    top2 <- xmlRoot(doc2) 

    #scrape relevant data 

    address <- url[i] 
    recipename <- xpathSApply(top2[[2]], "//h1[@class='fn']", xmlValue) 
    prept <- xpathSApply(top2[[2]], "//span[@class='prT']", xmlValue) 
    cookt <- xpathSApply(top2[[2]], "//span[@class='ckT']", xmlValue) 
    calories <- xpathSApply(top2[[2]], "//span[@class='clrs']", xmlValue) 
    protein <- xpathSApply(top2[[2]], "//span[@class='prtn']", xmlValue) 
    carbs <- xpathSApply(top2[[2]], "//span[@class='crbs']", xmlValue) 
    fat <- xpathSApply(top2[[2]], "//span[@class='fat']", xmlValue) 
    servings <- xpathSApply(top2[[2]], "//span[@class='yld']", xmlValue) 
    ingredients <- xpathSApply(top2[[2]], "//span[@class='ingredient']", xmlValue) 
    instructions <- xpathSApply(top2[[2]], "//ol[@class='methodOL']", xmlValue) 

    #create a data.frame of the url and relevant data. 

    result <- data.frame(address, recipename, prept, cookt, 
         calories, protein, carbs, fat, 
         servings, list(ingredients), instructions) 

    #rename the tricky column 

    colnames(result)[10] <- 'ingredients' 

    #bind data to existing df 

    df <- rbind(df, result) 
      } 

    #return df 

    df 
} 

そして、ここでは、ソリューションです - 次のように私は単純に、条件を追加しました:

instructions <- xpathSApply(top2[[2]], "//ol[@class='methodOL']", xmlValue) 
      if (length(instructions) == 0) { 
        instructions <- xpathSApply(top2[[2]], "//ul[@class='b-list m-circle instrs']", xmlValue)} 
+0

注 - データスクレーピングで返される行の数は、各レシピの成分が異なるため、URLごとに異なります。 1つのセルにすべての成分を保存して、問題を解決する方法があれば、それはどうやって解決するのか、行の数が実際には問題になるかどうかはわかりません。 – ogriofac

+0

いくつかの位置に「NA」をプレースホルダとして挿入する必要があるように聞こえます。 –

+0

@ JohnColemanは、問題がrbindの行数に等しくないことを意味していますか? NAを挿入するように設定する方法がわからないのですが、rbindヘルプページに1つあるとは思われません。どのようにこれにアプローチする上の任意のアイデア?コメントありがとう! – ogriofac

答えて

0

私はそれが動作するようにあなたの関数を微調整することができました:

f4fscrape <- function(urls) { 

    #Create an empty dataframe 

    df <- data.frame(matrix(ncol = 11, nrow = 0)) 
    cnames <- c('address', 'recipename', 'prept', 'cookt', 
       'calories', 'protein', 'carbs', 'fat', 
       'servings', 'ingredients', 'instructions') 

    names(df) <- cnames 

    #check for the recipe url in dataframe already, 
    #only carry on if not present 

    for (i in 1:length(urls)) 
    if (urls[i] %in% df$address) { 
     next } 
    else { 
    #parse url as html 

    doc2 <-htmlTreeParse(urls[i], useInternalNodes = TRUE) 

    #define the root node 

    top2 <- xmlRoot(doc2) 

    #scrape relevant data 

    address <- urls[i] 
    recipename <- xpathSApply(top2[[2]], "//h1[@class='fn']", xmlValue) 
    prept <- xpathSApply(top2[[2]], "//span[@class='prepTime']", xmlValue) 
    cookt <- xpathSApply(top2[[2]], "//span[@class='cookTime']", xmlValue) 
    calories <- xpathSApply(top2[[2]], "//span[@class='calories']", xmlValue) 
    protein <- xpathSApply(top2[[2]], "//span[@class='protein']", xmlValue) 
    carbs <- xpathSApply(top2[[2]], "//span[@class='carbohydrates']", xmlValue) 
    fat <- xpathSApply(top2[[2]], "//span[@class='fat']", xmlValue) 
    servings <- xpathSApply(top2[[2]], "//span[@class='yield']", xmlValue) 
    ingredients <- xpathSApply(top2[[2]], "//span[@class='ingredient']", xmlValue) 
    instructions <- xpathSApply(top2[[2]], "//ol[@class='methodOL']", xmlValue) 

    #create a data.frame of the url and relevant data. 

    result <- data.frame(address, recipename, prept, cookt, 
         calories, protein, carbs, fat, 
         servings, paste0(ingredients, collapse = ", "), instructions, stringsAsFactors = FALSE) 


    df <- rbind(df, setNames(result, names(df))) 
    } 

    #return df 

    df 
} 

変更:

1)urlので、私は、列名が割り当てられていた方法を変更colnames

2)についても同様に、それurlsと改名、組み込み関数です。

3)ループfor (i in length(url))は最後のインデックスにスキップします。私は for (i in 1:length(urls))

4)存在しないカラム(url)と呼ばれる状態if (url[i] %in% df$url)にそれを変更しました。私はそれをaddressに変更しました。

5)最も重要な変更点:paste0を使用して、成分を1つの文字列に連結しました。あなたが持っていたものでは、1-urlの場合、各成分はそれ自身の行に置かれ、他の列は(リサイクルルールによって)繰り返されました。現在のコードを単一のURLで実行し、結果をView()とすると、意図したとおりではない可能性があります。したがって、「1つのURLが渡されたときに機能します」というのは当てはまりません。

6)これらの長い文字列では、stringsAsFactors = FALSEを設定すると良いようです。

7)新しい行がrbindの場合、データフレームに名前を設定する必要があります。 this質問を参照してください。私はについて十分に知らない

enter image description here

:あなたは(もちろんそうではないズームアウトが)以下を参照してくださいあなたの与えられたリストの微調整機能を実行しているのあなたはView結果

XMLライブラリを使用すると、速度に役立ちます。ときには遅く、時には速くなることもあります。そのため、ほとんどの場合、接続速度が遅くなり、制御できない場合があります。

+0

優れたソリューションであり、非常にノートに感謝します。私が実際に知っているように、forループは私にとっては悪いものです。私はちょうどうんざりしました。paste0を使用して1つの文字列に成分を連結することは最適な選択肢です。私が質問したところでは、これらを1つのセルとして扱い、必要に応じて後で操作することができてうれしいです。私はpaste0機能についていくつか読んでいます。スピードを心配することなく、自分自身を調べて最適化しようとすると良いことがあります。私はそれが接続速度だと思っていますが、私がdata.tableパッケージを使用するかどうかは、いくらか高速化する可能性があります。 – ogriofac

+0

実際にここで奇妙なことが起こった - 私があなたに与えた5のサンプルを実行すると、それは動作する。私は30の完全なリストを実行すると、 '引数は行数の違いを暗示しています:1,0'を再現します。私は入力されたURLを二重チェックし、これを調査しようとします。おそらくそれらのうちの1つが故障していて、値のヌルセットを生成していますか?それが問題ではない場合、私はなぜそれが最初の5つではなく完全なリストではうまくいかないのかということを犠牲にしています。編集:実際には、どちらのURLが動作しないのhtmlかもしれない、私はそれらを個別に実行し、htmlを見てテストします。 – ogriofac

+0

@ogriofacおそらく問題は、 '命令'が一部の人にとっては期待通りではないということでしょうか?あなたが問題を引き起こしている最初の 'url'を見つけて、そのurlだけで関数を実行して何が起こるかを見てみましょう。探しているクラスの1つがWebページの1つにない場合、 'xpathSApply'がどのように動作するのか分かりません。それが「NA」を返すなら問題ではないはずです。そうでない場合は、おそらくオプションのパラメータを設定することができます。私が私の答えで言ったように、私はそのパッケージをよく知らない。 –

関連する問題