jobs-referer-parser 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/.DS_Store ADDED
Binary file
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2012-2013 Snowplow Analytics Ltd. All rights reserved.
2
+ #
3
+ # This program is licensed to you under the Apache License Version 2.0,
4
+ # and you may not use this file except in compliance with the Apache License Version 2.0.
5
+ # You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
6
+ #
7
+ # Unless required by applicable law or agreed to in writing,
8
+ # software distributed under the Apache License Version 2.0 is distributed on an
9
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
11
+
12
+ # Author:: Yali Sassoon (mailto:support@snowplowanalytics.com)
13
+ # Copyright:: Copyright (c) 2012-2013 Snowplow Analytics Ltd
14
+ # License:: Apache License Version 2.0
15
+
16
+ module RefererParser
17
+
18
+ class RefererParserError < StandardError
19
+ attr_reader :original
20
+ def initialize(msg, original=nil);
21
+ super(msg);
22
+ @original = original;
23
+ end
24
+ end
25
+
26
+ class UnsupportedFormatError < RefererParserError; end
27
+ class InvalidUriError < RefererParserError; end
28
+ class CorruptReferersError < RefererParserError; end
29
+ end
@@ -0,0 +1,215 @@
1
+ # Copyright (c) 2014 Inside Systems, Inc All rights reserved.
2
+ #
3
+ # This program is licensed to you under the Apache License Version 2.0,
4
+ # and you may not use this file except in compliance with the Apache License Version 2.0.
5
+ # You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
6
+ #
7
+ # Unless required by applicable law or agreed to in writing,
8
+ # software distributed under the Apache License Version 2.0 is distributed on an
9
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
11
+
12
+ # Author:: Kelley Reynolds (mailto:kelley@insidesystems.net)
13
+ # Copyright:: Copyright (c) 2014 Inside Systems Inc
14
+ # License:: Apache License Version 2.0
15
+
16
+ require 'uri'
17
+ require 'cgi'
18
+
19
+ module RefererParser
20
+ class Parser
21
+ DefaultFile = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'data', 'referers.json'))
22
+
23
+ # Create a new parser from one or more filenames/uris, defaults to ../data/referers.json
24
+ def initialize(uris=DefaultFile)
25
+ @domain_index ||= {}
26
+ @name_hash ||= {}
27
+
28
+ update(uris)
29
+ end
30
+
31
+ # Update the referer database with one or more uris
32
+ def update(uris)
33
+ [uris].flatten.each do |uri|
34
+ deserialize_referer_data(read_referer_data(uri), File.extname(uri).downcase)
35
+ end
36
+
37
+ true
38
+ end
39
+
40
+ # Clean out the database
41
+ def clear!
42
+ @domain_index, @name_hash = {}, {}
43
+
44
+ true
45
+ end
46
+
47
+ # Add a referer to the database with medium, name, domain or array of domains, and a parameter or array of parameters
48
+ # If called manually and a domain is added to an existing entry with a path, you may need to call optimize_index! afterwards.
49
+ def add_referer(medium, name, domains, parameters=nil)
50
+ # The same name can be used with multiple mediums so we make a key here
51
+ name_key = "#{name}-#{medium}"
52
+
53
+ # Update the name has with the parameter and medium data
54
+ @name_hash[name_key] = {:source => name, :medium => medium, :parameters => [parameters].flatten }
55
+
56
+ # Update the domain to name index
57
+ [domains].flatten.each do |domain_url|
58
+ domain, *path = domain_url.split('/')
59
+ if domain =~ /\Awww\.(.*)\z/i
60
+ domain = $1
61
+ end
62
+
63
+ domain.downcase!
64
+
65
+ @domain_index[domain] ||= []
66
+ if !path.empty?
67
+ @domain_index[domain] << ['/' + path.join('/'), name_key]
68
+ else
69
+ @domain_index[domain] << ['/', name_key]
70
+ end
71
+ end
72
+ end
73
+
74
+ # Prune duplicate entries and sort with the most specific path first if there is more than one entry
75
+ # In this case, sorting by the longest string works fine
76
+ def optimize_index!
77
+ @domain_index.each do |key, val|
78
+ # Sort each path/name_key pair by the longest path
79
+ @domain_index[key].sort! { |a, b|
80
+ b[0].size <=> a[0].size
81
+ }.uniq!
82
+ end
83
+ end
84
+
85
+ # Given a string or URI, return a hash of data
86
+ def parse(obj)
87
+ url = obj.is_a?(URI) ? obj : URI.parse(obj.to_s)
88
+
89
+ if !['http', 'https'].include?(url.scheme)
90
+ raise InvalidUriError.new("Only HTTP and HTTPS schemes are supported -- #{url.scheme}")
91
+ end
92
+
93
+ data = { :known => false, :uri => url.to_s }
94
+
95
+ domain, name_key = domain_and_name_key_for(url)
96
+ if domain and name_key
97
+ referer_data = @name_hash[name_key]
98
+ data[:known] = true
99
+ data[:source] = referer_data[:source]
100
+ data[:medium] = referer_data[:medium]
101
+ data[:domain] = domain
102
+
103
+ # Parse parameters if the referer uses them
104
+ if url.query and referer_data[:parameters]
105
+ query_params = CGI.parse(url.query)
106
+ referer_data[:parameters].each do |param|
107
+ # If there is a matching parameter, get the first non-blank value
108
+ if !(values = query_params[param]).empty?
109
+ data[:term] = values.select { |v| v.strip != "" }.first
110
+ break if data[:term]
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ data
117
+ rescue URI::InvalidURIError
118
+ raise InvalidUriError.new("Unable to parse URI, not a URI? -- #{obj.inspect}", $!)
119
+ end
120
+
121
+ protected
122
+
123
+ # Determine the correct name_key for this host and path
124
+ def domain_and_name_key_for(uri)
125
+ # Create a proc that will return immediately
126
+ check = Proc.new do |domain|
127
+ domain.downcase!
128
+ if paths = @domain_index[domain]
129
+ paths.each do |path, name_key|
130
+ return [domain, name_key] if uri.path.include?(path)
131
+ end
132
+ end
133
+ end
134
+
135
+ # First check hosts with and without the www prefix with the path
136
+ if uri.host =~ /\Awww\.(.+)\z/i
137
+ check.call $1
138
+ else
139
+ check.call uri.host
140
+ end
141
+
142
+ # Remove subdomains until only three are left (probably good enough)
143
+ host_arr = uri.host.split(".")
144
+ while host_arr.size > 2 do
145
+ host_arr.shift
146
+ check.call host_arr.join(".")
147
+ end
148
+
149
+ nil
150
+ end
151
+
152
+ def deserialize_referer_data(data, ext)
153
+ # Parse the loaded data with the correct parser
154
+ deserialized_data = if ['.yml', '.yaml'].include?(ext)
155
+ deserialize_yaml(data)
156
+ elsif ext == '.json'
157
+ deserialize_json(data)
158
+ else
159
+ raise UnsupportedFormatError.new("Only yaml and json file formats are currently supported -- #{@msg}")
160
+ end
161
+
162
+ begin
163
+ parse_referer_data deserialized_data
164
+ rescue
165
+ raise CorruptReferersError.new("Unable to parse data file -- #{$!.class} #{$!.to_s}", $!)
166
+ end
167
+ end
168
+
169
+ def deserialize_yaml(data)
170
+ require 'yaml'
171
+ YAML.load(data)
172
+ rescue Exception => e
173
+ raise CorruptReferersError.new("Unable to YAML file -- #{e.to_s}", e)
174
+ end
175
+
176
+ def deserialize_json(data)
177
+ require 'json'
178
+ JSON.parse(data)
179
+ rescue JSON::ParserError
180
+ raise CorruptReferersError.new("Unable to JSON file -- #{$!.to_s}", $!)
181
+ end
182
+
183
+ def read_referer_data(uri)
184
+ # Attempt to read the data from the network if application, or the file on the local system
185
+ if uri =~ /\A(?:ht|f)tps?:\/\//
186
+ require 'open-uri'
187
+ begin
188
+ open(uri).read
189
+ rescue OpenURI::HTTPError
190
+ raise InvalidUriError.new("Cannot load referer data from URI #{uri} -- #{$!.to_s}", $!)
191
+ end
192
+ else
193
+ File.read(uri)
194
+ end
195
+ end
196
+
197
+ # Create an index that maps domains/paths to their name/medium and a hash that contains their metadata
198
+ # The index strips leading www in order to keep the index smaller
199
+ # Format of the domain_index:
200
+ # { domain => [[path1, name_key], [path2, name_key], ... ] }
201
+ # Format of the name_hash:
202
+ # { name_key => {:source, :medium, :parameters} }
203
+ def parse_referer_data(data)
204
+ data.each do |medium, name_hash|
205
+ name_hash.each do |name, name_data|
206
+ add_referer(medium, name, name_data['domains'], name_data['parameters'])
207
+ end
208
+ end
209
+
210
+ optimize_index!
211
+ rescue
212
+ raise CorruptReferersError.new("Unable to parse referer data", $!)
213
+ end
214
+ end
215
+ end
@@ -0,0 +1,19 @@
1
+ # Copyright (c) 2012-2013 Snowplow Analytics Ltd. All rights reserved.
2
+ #
3
+ # This program is licensed to you under the Apache License Version 2.0,
4
+ # and you may not use this file except in compliance with the Apache License Version 2.0.
5
+ # You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
6
+ #
7
+ # Unless required by applicable law or agreed to in writing,
8
+ # software distributed under the Apache License Version 2.0 is distributed on an
9
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
11
+
12
+ # Original Author:: Yali Sassoon (mailto:support@snowplowanalytics.com)
13
+ # Copyright:: Copyright (c) 2012-2013 Snowplow Analytics Ltd
14
+ # License:: Apache License Version 2.0
15
+
16
+ module RefererParser
17
+ NAME = "jobs-referer-parser"
18
+ VERSION = "0.0.1"
19
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2012-2013 Snowplow Analytics Ltd. All rights reserved.
2
+ #
3
+ # This program is licensed to you under the Apache License Version 2.0,
4
+ # and you may not use this file except in compliance with the Apache License Version 2.0.
5
+ # You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
6
+ #
7
+ # Unless required by applicable law or agreed to in writing,
8
+ # software distributed under the Apache License Version 2.0 is distributed on an
9
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
11
+
12
+ # Author:: Yali Sassoon (mailto:support@snowplowanalytics.com)
13
+ # Copyright:: Copyright (c) 2012-2013 Snowplow Analytics Ltd
14
+ # License:: Apache License Version 2.0
15
+
16
+ require "referer-parser/version"
17
+ require "referer-parser/errors"
18
+ require "referer-parser/parser"
19
+
20
+ module RefererParser
21
+ end
@@ -0,0 +1,38 @@
1
+ # Copyright (c) 2012-2013 Snowplow Analytics Ltd. All rights reserved.
2
+ #
3
+ # This program is licensed to you under the Apache License Version 2.0,
4
+ # and you may not use this file except in compliance with the Apache License Version 2.0.
5
+ # You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
6
+ #
7
+ # Unless required by applicable law or agreed to in writing,
8
+ # software distributed under the Apache License Version 2.0 is distributed on an
9
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ # See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
11
+
12
+ # Author:: Yali Sassoon (mailto:support@snowplowanalytics.com)
13
+ # Copyright:: Copyright (c) 2012-2013 Snowplow Analytics Ltd
14
+ # License:: Apache License Version 2.0
15
+
16
+ # -*- encoding: utf-8 -*-
17
+ lib = File.expand_path('../lib', __FILE__)
18
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
19
+ require 'referer-parser/version'
20
+
21
+ Gem::Specification.new do |gem|
22
+ gem.authors = ["Yali Sassoon", "Martin Loy", "Alex Dean", "Kelley Reynolds", "Shiv Bharthur"]
23
+ gem.email = ["shiv@recroup.com"]
24
+ gem.description = %q{Library for extracting marketing attribution data from referer URLs}
25
+ gem.summary = %q{Library for extracting marketing attribution data (e.g. search terms) from referer (sic) URLs. This is used by Recroup (http://github.com/bharthur/jobs-referer-parser). Originally developed by Snowplow (http://github.com/snowplow/snowplow).}
26
+ gem.homepage = "http://github.com/bharthur/jobs-referer-parser"
27
+
28
+ gem.files = `git ls-files`.split($/)
29
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
30
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
31
+ gem.name = RefererParser::NAME
32
+ gem.version = RefererParser::VERSION
33
+ gem.platform = Gem::Platform::RUBY
34
+ gem.require_paths = ["lib"]
35
+
36
+ gem.add_development_dependency "rspec", "~> 2.6"
37
+ gem.add_development_dependency "rake", ">= 0.9.2"
38
+ end
@@ -0,0 +1,9 @@
1
+ {
2
+ "internal": {
3
+ "SnowPlow": {
4
+ "domains": [
5
+ "www.snowplowanalytics.com"
6
+ ]
7
+ }
8
+ }
9
+ }
@@ -0,0 +1 @@
1
+ This has the right extension but is unparsable gibberish to json:{}}}}
@@ -0,0 +1,2 @@
1
+ this:is invalid:yaml:
2
+ !!!
@@ -0,0 +1,234 @@
1
+ [
2
+ {
3
+ "spec": "Google Images search",
4
+ "uri": "http://www.google.fr/imgres?q=Ogham+the+celtic+oracle&hl=fr&safe=off&client=firefox-a&hs=ZDu&sa=X&rls=org.mozilla:fr-FR:unofficial&tbm=isch&prmd=imvnsa&tbnid=HUVaj-o88ZRdYM:&imgrefurl=http://www.psychicbazaar.com/oracles/101-ogham-the-celtic-oracle-set.html&docid=DY5_pPFMliYUQM&imgurl=http://mdm.pbzstatic.com/oracles/ogham-the-celtic-oracle-set/montage.png&w=734&h=250&ei=GPdWUIePCOqK0AWp3oCQBA&zoom=1&iact=hc&vpx=129&vpy=276&dur=827&hovh=131&hovw=385&tx=204&ty=71&sig=104115776612919232039&page=1&tbnh=69&tbnw=202&start=0&ndsp=26&ved=1t:429,r:13,s:0,i:114&biw=1272&bih=826",
5
+ "medium": "search",
6
+ "source": "Google Images",
7
+ "term": "Ogham the celtic oracle",
8
+ "known": true
9
+ },
10
+ {
11
+ "spec": "Yahoo! Images search",
12
+ "uri": "http://it.images.search.yahoo.com/images/view;_ylt=A0PDodgQmGBQpn4AWQgdDQx.;_ylu=X3oDMTBlMTQ4cGxyBHNlYwNzcgRzbGsDaW1n?back=http%3A%2F%2Fit.images.search.yahoo.com%2Fsearch%2Fimages%3Fp%3DEarth%2BMagic%2BOracle%2BCards%26fr%3Dmcafee%26fr2%3Dpiv-web%26tab%3Dorganic%26ri%3D5&w=1064&h=1551&imgurl=mdm.pbzstatic.com%2Foracles%2Fearth-magic-oracle-cards%2Fcard-1.png&rurl=http%3A%2F%2Fwww.psychicbazaar.com%2Foracles%2F143-earth-magic-oracle-cards.html&size=2.8+KB&name=Earth+Magic+Oracle+Cards+-+Psychic+Bazaar&p=Earth+Magic+Oracle+Cards&oid=f0a5ad5c4211efe1c07515f56cf5a78e&fr2=piv-web&fr=mcafee&tt=Earth%2BMagic%2BOracle%2BCards%2B-%2BPsychic%2BBazaar&b=0&ni=90&no=5&ts=&tab=organic&sigr=126n355ib&sigb=13hbudmkc&sigi=11ta8f0gd&.crumb=IZBOU1c0UHU",
13
+ "medium": "search",
14
+ "source": "Yahoo! Images",
15
+ "term": "Earth Magic Oracle Cards",
16
+ "known": true
17
+ },
18
+ {
19
+ "spec": "Powered by Google",
20
+ "uri": "http://isearch.avg.com/pages/images.aspx?q=tarot+card+change&sap=dsp&lang=en&mid=209215200c4147d1a9d6d1565005540b-b0d4f81a8999f5981f04537c5ec8468fd5234593&cid=%7B50F9298B-C111-4C7E-9740-363BF0015949%7D&v=12.1.0.21&ds=AVG&d=7%2F23%2F2012+10%3A31%3A08+PM&pr=fr&sba=06oENya4ZG1YS6vOLJwpLiFdjG91ICt2YE59W2p5ENc2c4w8KvJb5xbvjkj3ceMjnyTSpZq-e6pj7GQUylIQtuK4psJU60wZuI-8PbjX-OqtdX3eIcxbMoxg3qnIasP0ww2fuID1B-p2qJln8vBHxWztkpxeixjZPSppHnrb9fEcx62a9DOR0pZ-V-Kjhd-85bIL0QG5qi1OuA4M1eOP4i_NzJQVRXPQDmXb-CpIcruc2h5FE92Tc8QMUtNiTEWBbX-QiCoXlgbHLpJo5Jlq-zcOisOHNWU2RSHYJnK7IUe_SH6iQ.%2CYT0zO2s9MTA7aD1mNjZmZDBjMjVmZDAxMGU4&snd=hdr&tc=test1",
21
+ "medium": "search",
22
+ "source": "Google",
23
+ "term": "tarot card change",
24
+ "known": true
25
+ },
26
+ {
27
+ "spec": "Google search #1",
28
+ "uri": "http://www.google.com/search",
29
+ "medium": "search",
30
+ "source": "Google",
31
+ "term": null,
32
+ "known": true
33
+ },
34
+ {
35
+ "spec": "Google search #2",
36
+ "uri": "http://www.google.com/search?q=gateway+oracle+cards+denise+linn&hl=en&client=safari",
37
+ "medium": "search",
38
+ "source": "Google",
39
+ "term": "gateway oracle cards denise linn",
40
+ "known": true
41
+ },
42
+ {
43
+ "spec": "Yahoo! search",
44
+ "uri": "http://es.search.yahoo.com/search;_ylt=A7x9QbwbZXxQ9EMAPCKT.Qt.?p=BIEDERMEIER+FORTUNE+TELLING+CARDS&ei=utf-8&type=685749&fr=chr-greentree_gc&xargs=0&pstart=1&b=11",
45
+ "medium": "search",
46
+ "source": "Yahoo!",
47
+ "term": "BIEDERMEIER FORTUNE TELLING CARDS",
48
+ "known": true
49
+ },
50
+ {
51
+ "spec": "PriceRunner search",
52
+ "uri": "http://www.pricerunner.co.uk/search?displayNoHitsMessage=1&q=wild+wisdom+of+the+faery+oracle",
53
+ "medium": "search",
54
+ "source": "PriceRunner",
55
+ "term": "wild wisdom of the faery oracle",
56
+ "known": true
57
+ },
58
+ {
59
+ "spec": "Bing Images search",
60
+ "uri": "http://www.bing.com/images/search?q=psychic+oracle+cards&view=detail&id=D268EDDEA8D3BF20AF887E62AF41E8518FE96F08",
61
+ "medium": "search",
62
+ "source": "Bing Images",
63
+ "term": "psychic oracle cards",
64
+ "known": true
65
+ },
66
+ {
67
+ "spec": "IXquick search",
68
+ "uri": "https://s3-us3.ixquick.com/do/search",
69
+ "medium": "search",
70
+ "source": "IXquick",
71
+ "term": null,
72
+ "known": true
73
+ },
74
+ {
75
+ "spec": "AOL search",
76
+ "uri": "http://aolsearch.aol.co.uk/aol/search?s_chn=hp&enabled_terms=&s_it=aoluk-homePage50&q=pendulums",
77
+ "medium": "search",
78
+ "source": "AOL",
79
+ "term": "pendulums",
80
+ "known": true
81
+ },
82
+ {
83
+ "spec": "AOL search.com",
84
+ "uri": "http://www.aolsearch.com/search?s_pt=hp&s_gl=NL&query=voorbeeld+cv+competenties&invocationType=tb50hpcnnbie7-nl-nl",
85
+ "medium": "search",
86
+ "source": "AOL",
87
+ "term": "voorbeeld cv competenties",
88
+ "known": true
89
+ },
90
+ {
91
+ "spec": "Ask search",
92
+ "uri": "http://uk.search-results.com/web?qsrc=1&o=1921&l=dis&q=pendulums&dm=ctry&atb=sysid%3D406%3Aappid%3D113%3Auid%3D8f40f651e7b608b5%3Auc%3D1346336505%3Aqu%3Dpendulums%3Asrc%3Dcrt%3Ao%3D1921&locale=en_GB",
93
+ "medium": "search",
94
+ "source": "Ask",
95
+ "term": "pendulums",
96
+ "known": true
97
+ },
98
+ {
99
+ "spec": "Mail.ru search",
100
+ "uri": "http://go.mail.ru/search?q=Gothic%20Tarot%20Cards&where=any&num=10&rch=e&sf=20",
101
+ "medium": "search",
102
+ "source": "Mail.ru",
103
+ "term": "Gothic Tarot Cards",
104
+ "known": true
105
+ },
106
+ {
107
+ "spec": "Yandex search",
108
+ "uri": "http://images.yandex.ru/yandsearch?text=Blue%20Angel%20Oracle%20Blue%20Angel%20Oracle&noreask=1&pos=16&rpt=simage&lr=45&img_url=http%3A%2F%2Fmdm.pbzstatic.com%2Foracles%2Fblue-angel-oracle%2Fbox-small.png",
109
+ "medium": "search",
110
+ "source": "Yandex Images",
111
+ "term": "Blue Angel Oracle Blue Angel Oracle",
112
+ "known": true
113
+ },
114
+ {
115
+ "spec": "Ask toolbar search",
116
+ "uri": "http://search.tb.ask.com/search/GGmain.jhtml?cb=AYY&pg=GGmain&p2=%5EAYY%5Exdm071%5EYYA%5Eid&n=77fdaa55&qid=c2678d9147654034bb8b16daa7bfb48c&ss=sub&st=hp&ptb=F9FC6C22-EAE6-4D1E-8126-A70119B6E02F&si=flvrunner&tpr=hst&searchfor=CARA+MEMASAK+CUMI+CUMI&ots=1219016089614",
117
+ "medium": "search",
118
+ "source": "Ask Toolbar",
119
+ "term": "CARA MEMASAK CUMI CUMI",
120
+ "known": true
121
+ },
122
+ {
123
+ "spec": "Ask toolbar search #2",
124
+ "uri": "http://search.tb.ask.com/search/GGmain.jhtml?&st=hp&p2=%5EZU%5Exdm458%5EYYA%5Eus&n=77fda1bd&ptb=F0B68CA5-4791-4376-BFCC-5F0100329FB6&si=CMKg9-nX07oCFSjZQgodcikACQ&tpr=hpsbsug&searchfor=test",
125
+ "medium": "search",
126
+ "source": "Ask Toolbar",
127
+ "term": "test",
128
+ "known": true
129
+ },
130
+ {
131
+ "spec": "Voila search",
132
+ "uri": "http://search.ke.voila.fr/?module=voila&bhv=web_fr&kw=test",
133
+ "medium": "search",
134
+ "source": "Voila",
135
+ "term": "test",
136
+ "known": true
137
+ },
138
+ {
139
+ "spec": "Dale search",
140
+ "uri": "http://www.dalesearch.com/?q=+lego.nl+%2Fclub&s=web&as=0&rlz=0&babsrc=HP_ss",
141
+ "medium": "search",
142
+ "source": "Dalesearch",
143
+ "term": " lego.nl /club",
144
+ "known": true
145
+ },
146
+ {
147
+ "spec": "Twitter redirect",
148
+ "uri": "http://t.co/chrgFZDb",
149
+ "medium": "social",
150
+ "source": "Twitter",
151
+ "term": null,
152
+ "known": true
153
+ },
154
+ {
155
+ "spec": "Facebook social",
156
+ "uri": "http://www.facebook.com/l.php?u=http%3A%2F%2Fwww.psychicbazaar.com&h=yAQHZtXxS&s=1",
157
+ "medium": "social",
158
+ "source": "Facebook",
159
+ "term": null,
160
+ "known": true
161
+ },
162
+ {
163
+ "spec": "Facebook mobile",
164
+ "uri": "http://m.facebook.com/l.php?u=http%3A%2F%2Fwww.psychicbazaar.com%2Fblog%2F2012%2F09%2Fpsychic-bazaar-reviews-tarot-foundations-31-days-to-read-tarot-with-confidence%2F&h=kAQGXKbf9&s=1",
165
+ "medium": "social",
166
+ "source": "Facebook",
167
+ "term": null,
168
+ "known": true
169
+ },
170
+ {
171
+ "spec": "Odnoklassniki",
172
+ "uri": "http://www.odnoklassniki.ru/dk?cmd=logExternal&st._aid=Conversations_Openlink&st.name=externalLinkRedirect&st.link=http%3A%2F%2Fwww.psychicbazaar.com%2Foracles%2F187-blue-angel-oracle.html",
173
+ "medium": "social",
174
+ "source": "Odnoklassniki",
175
+ "term": null,
176
+ "known": true
177
+ },
178
+ {
179
+ "spec": "Tumblr social #1",
180
+ "uri": "http://www.tumblr.com/dashboard",
181
+ "medium": "social",
182
+ "source": "Tumblr",
183
+ "term": null,
184
+ "known": true
185
+ },
186
+ {
187
+ "spec": "Tumblr w subdomain",
188
+ "uri": "http://psychicbazaar.tumblr.com/",
189
+ "medium": "social",
190
+ "source": "Tumblr",
191
+ "term": null,
192
+ "known": true
193
+ },
194
+ {
195
+ "spec": "Yahoo! Mail",
196
+ "uri": "http://36ohk6dgmcd1n-c.c.yom.mail.yahoo.net/om/api/1.0/openmail.app.invoke/36ohk6dgmcd1n/11/1.0.35/us/en-US/view.html/0",
197
+ "medium": "email",
198
+ "source": "Yahoo! Mail",
199
+ "term": null,
200
+ "known": true
201
+ },
202
+ {
203
+ "spec": "Outlook.com mail",
204
+ "uri": "http://co106w.col106.mail.live.com/default.aspx?rru=inbox",
205
+ "medium": "email",
206
+ "source": "Outlook.com",
207
+ "term": null,
208
+ "known": true
209
+ },
210
+ {
211
+ "spec": "Orange Webmail",
212
+ "uri": "http://webmail1m.orange.fr/webmail/fr_FR/read.html?FOLDER=SF_INBOX&IDMSG=8594&check=&SORTBY=31",
213
+ "medium": "email",
214
+ "source": "Orange Webmail",
215
+ "term": null,
216
+ "known": true
217
+ },
218
+ {
219
+ "spec": "Internal HTTP",
220
+ "uri": "http://www.snowplowanalytics.com/about/team",
221
+ "medium": "internal",
222
+ "source": "SnowPlow",
223
+ "term": null,
224
+ "known": true
225
+ },
226
+ {
227
+ "spec": "Internal HTTPS",
228
+ "uri": "https://www.snowplowanalytics.com/account/profile",
229
+ "medium": "internal",
230
+ "source": "SnowPlow",
231
+ "term": null,
232
+ "known": true
233
+ }
234
+ ]