web-dev-qa-db-ja.com

ログファイルのURLからクエリ文字列を取得する

次の形式の多数のログエントリを含むサーバーログファイルがあります。

193.1.172.46 - - [23/Mar/2008:03:57:38 +0000] "GET /robots.txt HTTP/1.0" 404 289 "-" "gsa-crawler (Enterprise; M2-N7RQ5RABCA2JT; [email protected],[email protected])"

Googleの検索エンジンを使用するすべてのエントリを特定し、それらからクエリ文字列を取得して、クエリ文字列のみを出力に表示するように求められました。

したがって、grepコマンドを使用して、次のように検索エンジンにアクセスするすべてのエントリを識別しました。

 grep "http://www.google.com/search?" logs.txt 

次のようなエントリのリストが表示されます。

143.183.121.3 - - [23/Mar/2008:00:16:59 +0000] "GET /staff/jcarthy/home/2ndYearUnix/usefulcommands2col.pdf HTTP/1.0" 200 78866 "http://www.google.com/search?hl=en&q=frequently+used+unix+aliases&btnG=Google+Search"; "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; InfoPath.1)"

エントリのhl = en&q = frequencyly + used + unix + aliases&btnG = Google + Search部分のみが表示されたリストを表示するにはどうすればよいですか?

4
Joe

これはかなり読みやすいsedアプローチです

$ cat log.txt | grep "http://www.google.com/search?" | sed  s/^.*search?// | sed s/\"\;.*//

つまり.

行の先頭を削除します。

s/   # replace a match which is:
  ^       # from the start of the line
  .*      # any number of any characters
  search? # the text "search?"
//   # with nothing (remove it)

次に、行の終わりを削除します

s/    # replace a match which is:
  \"    # a double quote (escaped with backslash)
  \;    # a semicolon (escaped with backslash)
  .*    # any number of characters
//    # with nothing (remove it)

パラメータだけを残します

1
Michael Durrant

ここでの他のソリューションはすべて、一部のログエントリで失敗する可能性があります。参照元フィールド内にスペースがあるか、引用符とバックスラッシュが追加されたもの、大文字のドメイン名、httpの代わりにhttps、または場所フィールドと参照元フィールド内のキーワード。

例えば:

1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /a b/ HTTP/1.0" 200 0 "http://www.google.com/search?..." "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /i/love/http://www.google.com/search?ing HTTP/1.0" 200 0 "http://www.google.com/search?..." "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET / HTTP/1.0" 200 0 "http://www.google.com/search?spaces in referrer" "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /nohttpver" 200 0 "http://www.google.com/search?spaces in referrer" "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /" 200 0 "http://example.org/http://www.google.com/search?spaces in referrer" "Mozilla/4.0"
1.2.3.4 - - [23/Mar/2008:00:16:59 +0000] "GET /" 200 0 "http://WWW.GOOGLE.COM/search?spaces in referrer" "Mozilla/4.0"

これらに対処するには、最初に2番目の二重引用符で囲まれたフィールドを適切に抽出する必要があります。 Apacheログファイルでは、バックスラッシュを使用して余分な引用符やその他の特殊文字をエスケープすることに注意してください。つまり、"[^"]*"などの単純な正規表現では不十分です。

Grepを使用してリファラーフィールド(2番目の二重引用符で囲まれたフィールド)を抽出する:

grep -oP '^[^"]+"[^"\\]*(?:\\.[^"\\]*)*"[^"]+"\K[^"\\]*(?:\\.[^"\\]*)*(?=")' logfile.txt

クレイジーに見えます!分解してみましょう:

  • oへのgrep引数は、行の残りの部分ではなく、行の一致する部分のみを取得することを意味します
  • Pへのgrep引数は、Perl互換の正規表現を使用するように指示します
  • ここで使用されている正規表現...\K...(?=...)の全体的な構造は、パターン全体をチェックしていることを意味しますが、\K(?=...)の間のものだけが出力されます

正規表現をさらに分解する:

  1. ^[^"]+ –行頭から最初の"までのすべてを取得します
  2. "[^"\\]*(?:\\.[^"\\]*)*" –最初の二重引用符で囲まれた文字列全体を取得します。この回答を見る https://stackoverflow.com/a/5696141/1764245
  3. [^"]+ – 2つの文字列間のすべてを取得する
  4. "\K[^"\\]*(?:\\.[^"\\]*)*(?=")上記と同じですが、最初の\Kの後に"があり、その後にデータのマッチングを開始し、(?=")は最後の"の前にあるデータのマッチングを停止します。

この時点を過ぎると、引用符を気にしてログファイルからフィールドを適切に抽出する必要がなくなるため、データの処理がはるかに簡単になります。

たとえば、出力を別のgrepにパイプすることができます。

grep -oP ... logfile.txt | grep -oPi '^https?://www\.google\.com/search\?\K.*'

ここで、2番目のgrepのiオプションは、大文字と小文字を区別しません。

または、google.comリファラーの開始のチェックを最初の正規表現に直接追加し、必要に応じて\Kを移動することもできますが、1つのジョブとそれを実行する2つの正規表現を実行することをお勧めします。それらを組み合わせて、その仕事が明確ではないものにするよりも。

他のGoogleドメインから参照元を収集する場合は、正規表現をかなり変更する必要があることに注意してください。 Googleは 多数の検索ドメイン を所有しています。

Google以外のいくつかのサイトにアクセスする可能性を気にしない場合は、次のようにできます。

... | grep -oPi '^https?://(www\.)?google\.[a-z]{2,3}(\.[a-z]{2})?/search\?\K.*'

それ以外の場合は、常に移動するターゲットであるGoogle所有の検索ドメインのみに一致するように試みる必要があります。

... | grep -oPi '^https?://(www\.)?google\.(a[cdelmstz]|b[aefgijsty]|cat|c[acdfghilmnvz]|co\.(ao|bw|c[kr]|i[dln]|jp|k[er]|ls|m[az]|nz|t[hz]|u[gkz]|v[ei]|z[amw])|com(\.(a[fgiru]|b[dhnorz]|c[ouy]|do|e[cgt]|fj|g[hit]|hk|jm|k[hw]|l[bcy]|m[mtxy]|n[afgip]|om|p[aeghkry]|qa|s[abglv]|t[jrw]|u[ay]|v[cn]))?|d[ejkmz]|e[es]|f[imr]|g[aefglmpry]|h[nrtu]|i[emoqst]|j[eo]|k[giz]|l[aiktuv]|m[degklnsuvw]|n[eloru]|p[lnst]|r[osuw]|s[cehikmnort]|t[dgklmnot]|us|v[gu]|ws)/search\?\K.*'

また、Googleの画像検索やその他の検索サブドメインを含める場合は、上記のいずれかのgrepコマンドの(www\.)?((www|images|other|sub|domains)\.)?などに変更する必要があります。

4
codebeard

一般的なバージョン

_awk '$11 ~ /?/ { printf "%s\n",substr($11,1+index($11,"?")) ;}'
_

どこ

  • _$11 ~ /\?/_を検索? URLで
  • substr($11,1+index($11,"?")の後の検索部分?
  • 引数は解析されないことに注意してください。
  • これはURLのエスケープを解除しません(例: space _%20_として表示されます)

前のバージョン

_awk '$11 ~ /http:\/\/www.google.com\/search?/ { print substr($11,26) ;}' 
_

どこ

  • _$11_は参照を保持するフィールドの数であり、調整する必要がある場合があります
  • _28_は「 http://www.google.com/search ?」の長さです
3
Archemar

私はそれを理解したと思います。

grep "http://www.google.com/search?" logs.txt | cut -d" " -f11 |  sed -r 's/^.{30}//'

これは許容できる解決策のように見えますか?

0
Joe
awk -F"[?|;]" '/google.com\/search/{print $2}' log.txt

awk -F? '/google.com\/search/{gsub(";.*","",$2);print $2}' log.txt
0
Kamaraj