twingly-search 5.0.1 → 5.1.0
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -2
- data/CHANGELOG.md +16 -0
- data/README.md +33 -34
- data/Rakefile +0 -6
- data/examples/find_all_posts_mentioning_github.rb +3 -3
- data/examples/hello_world.rb +2 -2
- data/examples/livefeed_loop.rb +24 -0
- data/lib/twingly/livefeed/client.rb +121 -0
- data/lib/twingly/livefeed/error.rb +28 -0
- data/lib/twingly/livefeed/parser.rb +96 -0
- data/lib/twingly/livefeed/post.rb +66 -0
- data/lib/twingly/livefeed/result.rb +39 -0
- data/lib/twingly/livefeed/version.rb +5 -0
- data/lib/twingly/livefeed.rb +6 -0
- data/lib/twingly/search/client.rb +3 -2
- data/lib/twingly/search/error.rb +6 -5
- data/lib/twingly/search/parser.rb +39 -13
- data/lib/twingly/search/post.rb +65 -21
- data/lib/twingly/search/query.rb +46 -16
- data/lib/twingly/search/result.rb +11 -0
- data/lib/twingly/search/version.rb +1 -1
- data/spec/client_spec.rb +2 -2
- data/spec/error_spec.rb +27 -7
- data/spec/fixtures/incomplete_result.xml +2 -0
- data/spec/fixtures/livefeed/empty_api_key_result.xml +3 -0
- data/spec/fixtures/livefeed/non_xml_result.xml +1 -0
- data/spec/fixtures/livefeed/not_found_result.xml +3 -0
- data/spec/fixtures/livefeed/service_unavailable_result.xml +3 -0
- data/spec/fixtures/livefeed/unauthorized_api_key_result.xml +3 -0
- data/spec/fixtures/livefeed/valid_empty_result.xml +2 -0
- data/spec/fixtures/livefeed/valid_result.xml +79 -0
- data/spec/fixtures/minimal_valid_result.xml +81 -52
- data/spec/fixtures/nonexistent_api_key_result.xml +3 -3
- data/spec/fixtures/service_unavailable_result.xml +3 -3
- data/spec/fixtures/unauthorized_api_key_result.xml +3 -3
- data/spec/fixtures/undefined_error_result.xml +3 -3
- data/spec/fixtures/valid_empty_result.xml +2 -2
- data/spec/fixtures/valid_links_result.xml +36 -0
- data/spec/fixtures/vcr_cassettes/livefeed_valid_request.yml +169 -0
- data/spec/fixtures/vcr_cassettes/search_for_spotify_on_sv_blogs.yml +578 -447
- data/spec/fixtures/vcr_cassettes/search_without_valid_api_key.yml +15 -14
- data/spec/livefeed/client_spec.rb +135 -0
- data/spec/livefeed/error_spec.rb +51 -0
- data/spec/livefeed/parser_spec.rb +351 -0
- data/spec/livefeed/post_spec.rb +26 -0
- data/spec/livefeed/result_spec.rb +18 -0
- data/spec/parser_spec.rb +191 -94
- data/spec/post_spec.rb +25 -6
- data/spec/query_spec.rb +41 -34
- data/spec/result_spec.rb +1 -0
- data/spec/spec_helper.rb +10 -0
- data/twingly-search-api-ruby.gemspec +2 -3
- metadata +44 -24
- data/spec/fixtures/valid_non_blog_result.xml +0 -26
- data/spec/fixtures/valid_result.xml +0 -22975
@@ -6,16 +6,16 @@ module Twingly
|
|
6
6
|
# Parse an API response body.
|
7
7
|
#
|
8
8
|
# @param [String] document containing an API response XML.
|
9
|
-
# @raise [Error] which error depends on the API response (see {Error.
|
9
|
+
# @raise [Error] which error depends on the API response (see {Error.from_api_response}).
|
10
10
|
# @return [Result] containing the result.
|
11
11
|
def parse(document)
|
12
12
|
nokogiri = Nokogiri::XML(document)
|
13
13
|
|
14
|
-
failure = nokogiri.at_xpath('
|
14
|
+
failure = nokogiri.at_xpath('/error')
|
15
15
|
handle_failure(failure) if failure
|
16
16
|
|
17
17
|
data_node = nokogiri.at_xpath('/twinglydata')
|
18
|
-
handle_non_xml_document(
|
18
|
+
handle_non_xml_document(document) unless data_node
|
19
19
|
|
20
20
|
create_result(data_node)
|
21
21
|
end
|
@@ -27,41 +27,67 @@ module Twingly
|
|
27
27
|
result.number_of_matches_returned = data_node.attribute('numberOfMatchesReturned').value.to_i
|
28
28
|
result.number_of_matches_total = data_node.attribute('numberOfMatchesTotal').value.to_i
|
29
29
|
result.seconds_elapsed = data_node.attribute('secondsElapsed').value.to_f
|
30
|
+
result.incomplete_result = incomplete_result?(data_node)
|
30
31
|
|
31
|
-
data_node.xpath('//post
|
32
|
+
data_node.xpath('//post').each do |post|
|
32
33
|
result.posts << parse_post(post)
|
33
34
|
end
|
34
35
|
|
35
36
|
result
|
36
37
|
end
|
37
38
|
|
39
|
+
def incomplete_result?(data_node)
|
40
|
+
data_node.attribute('incompleteResult').value == "true"
|
41
|
+
end
|
42
|
+
|
38
43
|
def parse_post(element)
|
39
44
|
post_params = {}
|
40
45
|
element.element_children.each do |child|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
+
post_params[child.name] =
|
47
|
+
case child.name
|
48
|
+
when *%w(tags links images)
|
49
|
+
parse_array(child)
|
50
|
+
when "coordinates"
|
51
|
+
parse_coordinates(child)
|
52
|
+
else
|
53
|
+
child.text
|
54
|
+
end
|
46
55
|
end
|
56
|
+
|
47
57
|
post = Post.new
|
48
58
|
post.set_values(post_params)
|
49
59
|
post
|
50
60
|
end
|
51
61
|
|
52
|
-
def
|
62
|
+
def parse_array(element)
|
53
63
|
element.element_children.map do |child|
|
54
64
|
child.text
|
55
65
|
end
|
56
66
|
end
|
57
67
|
|
68
|
+
# TODO: Decide if a class or hash should be used...
|
69
|
+
def parse_coordinates(element)
|
70
|
+
return {} if element.children.empty?
|
71
|
+
|
72
|
+
{
|
73
|
+
latitude: element.at_xpath("latitude/text()"),
|
74
|
+
longitude: element.at_xpath("longitude/text()"),
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
58
78
|
def handle_failure(failure)
|
59
|
-
|
79
|
+
code = failure.attribute('code').value
|
80
|
+
message = failure.at_xpath('message').text
|
81
|
+
|
82
|
+
fail Error.from_api_response(code, message)
|
60
83
|
end
|
61
84
|
|
62
85
|
def handle_non_xml_document(document)
|
63
|
-
|
64
|
-
|
86
|
+
fail ServerError, "Failed to parse response: \"#{document}\""
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_time(time)
|
90
|
+
Time.parse(time)
|
65
91
|
end
|
66
92
|
end
|
67
93
|
end
|
data/lib/twingly/search/post.rb
CHANGED
@@ -1,44 +1,88 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require 'date'
|
4
|
-
|
5
3
|
module Twingly
|
6
4
|
module Search
|
7
5
|
# A blog post
|
8
6
|
#
|
9
|
-
# @attr_reader [String]
|
10
|
-
# @attr_reader [String]
|
11
|
-
# @attr_reader [String]
|
7
|
+
# @attr_reader [String] id the post ID (Twingly internal identification)
|
8
|
+
# @attr_reader [String] author the author of the blog post
|
9
|
+
# @attr_reader [String] url the post URL
|
10
|
+
# @attr_reader [String] title the post title
|
11
|
+
# @attr_reader [String] text the blog post text
|
12
12
|
# @attr_reader [String] language_code ISO two letter language code for the
|
13
|
-
# language that the post was written in
|
14
|
-
# @attr_reader [
|
15
|
-
#
|
16
|
-
# @attr_reader [
|
17
|
-
#
|
18
|
-
# @attr_reader [
|
19
|
-
#
|
13
|
+
# language that the post was written in
|
14
|
+
# @attr_reader [String] location_code ISO two letter country code for the
|
15
|
+
# location of the blog
|
16
|
+
# @attr_reader [Hash] coordinates a hash containing :latitude and :longitude
|
17
|
+
# from the post RSS
|
18
|
+
# @attr_reader [Array] links all links from the blog post to other resources
|
19
|
+
# @attr_reader [Array] tags the post tags/categories
|
20
|
+
# @attr_reader [Array] images image URLs from the post (currently not populated)
|
21
|
+
# @attr_reader [Time] indexed_at the time, in UTC, when the post was indexed by Twingly
|
22
|
+
# @attr_reader [Time] published_at the time, in UTC, when the post was published
|
23
|
+
# @attr_reader [Time] reindexed_at timestamp when the post last was changed in our database/index
|
24
|
+
# @attr_reader [String] inlinks_count number of links to this post that was found in other blog posts
|
25
|
+
# @attr_reader [String] blog_id the blog ID (Twingly internal identification)
|
26
|
+
# @attr_reader [String] blog_name the name of the blog
|
27
|
+
# @attr_reader [String] blog_url the blog URL
|
20
28
|
# @attr_reader [Integer] blog_rank the rank of the blog, based on authority and language.
|
21
29
|
# See https://developer.twingly.com/resources/ranking/#blogrank
|
22
|
-
# @attr_reader [
|
30
|
+
# @attr_reader [Integer] authority the blog's authority/influence.
|
31
|
+
# See https://developer.twingly.com/resources/ranking/#authority
|
23
32
|
class Post
|
24
|
-
attr_reader :
|
25
|
-
:
|
33
|
+
attr_reader :id, :author, :url, :title, :text, :location_code,
|
34
|
+
:language_code, :coordinates, :links, :tags, :images, :indexed_at,
|
35
|
+
:published_at, :reindexed_at, :inlinks_count, :blog_id, :blog_name,
|
36
|
+
:blog_url, :blog_rank, :authority
|
26
37
|
|
27
38
|
# Sets all instance variables for the {Post}, given a Hash.
|
28
39
|
#
|
29
40
|
# @param [Hash] params containing blog post data.
|
30
41
|
def set_values(params)
|
42
|
+
@id = params.fetch('id')
|
43
|
+
@author = params.fetch('author')
|
31
44
|
@url = params.fetch('url')
|
32
45
|
@title = params.fetch('title')
|
33
|
-
@
|
46
|
+
@text = params.fetch('text')
|
34
47
|
@language_code = params.fetch('languageCode')
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@
|
48
|
+
@location_code = params.fetch('locationCode')
|
49
|
+
@coordinates = params.fetch('coordinates', {})
|
50
|
+
@links = params.fetch('links', [])
|
51
|
+
@tags = params.fetch('tags', [])
|
52
|
+
@images = params.fetch('images', [])
|
53
|
+
@indexed_at = Time.parse(params.fetch('indexedAt'))
|
54
|
+
@published_at = Time.parse(params.fetch('publishedAt'))
|
55
|
+
@reindexed_at = Time.parse(params.fetch('reindexedAt'))
|
56
|
+
@inlinks_count = params.fetch('inlinksCount').to_i
|
57
|
+
@blog_id = params.fetch('blogId')
|
38
58
|
@blog_name = params.fetch('blogName')
|
39
|
-
@
|
59
|
+
@blog_url = params.fetch('blogUrl')
|
40
60
|
@blog_rank = params.fetch('blogRank').to_i
|
41
|
-
@
|
61
|
+
@authority = params.fetch('authority').to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
# @deprecated Please use {#text} instead
|
65
|
+
def summary
|
66
|
+
warn "[DEPRECATION] `summary` is deprecated. Please use `text` instead."
|
67
|
+
text
|
68
|
+
end
|
69
|
+
|
70
|
+
# @deprecated Please use {#indexed_at} instead
|
71
|
+
def indexed
|
72
|
+
warn "[DEPRECATION] `indexed` is deprecated. Please use `indexed_at` instead."
|
73
|
+
indexed_at
|
74
|
+
end
|
75
|
+
|
76
|
+
# @deprecated Please use {#published_at} instead
|
77
|
+
def published
|
78
|
+
warn "[DEPRECATION] `published` is deprecated. Please use `published_at` instead."
|
79
|
+
published_at
|
80
|
+
end
|
81
|
+
|
82
|
+
# @deprecated Please use {#links} instead
|
83
|
+
def outlinks
|
84
|
+
warn "[DEPRECATION] `outlinks` is deprecated. Please use `links` instead."
|
85
|
+
links
|
42
86
|
end
|
43
87
|
end
|
44
88
|
end
|
data/lib/twingly/search/query.rb
CHANGED
@@ -5,11 +5,34 @@ module Twingly
|
|
5
5
|
module Search
|
6
6
|
# Twingly Search API query
|
7
7
|
#
|
8
|
-
# @attr [String]
|
9
|
-
# @attr [String] language language to restrict the query to.
|
8
|
+
# @attr [String] search_query the search query.
|
10
9
|
# @attr [Client] client the client that this query is connected to.
|
11
10
|
class Query
|
12
|
-
attr_accessor :
|
11
|
+
attr_accessor :search_query, :client
|
12
|
+
|
13
|
+
# @deprecated Please use {#search_query} instead
|
14
|
+
def pattern
|
15
|
+
warn "[DEPRECATION] `pattern` is deprecated. Please use `search_query` instead."
|
16
|
+
@search_query
|
17
|
+
end
|
18
|
+
|
19
|
+
# @deprecated Please use {#search_query=} instead
|
20
|
+
def pattern=(search_query)
|
21
|
+
warn "[DEPRECATION] `pattern=` is deprecated. Please use `search_query=` instead."
|
22
|
+
@search_query = search_query
|
23
|
+
end
|
24
|
+
|
25
|
+
# @deprecated Please use {#search_query} instead
|
26
|
+
def language
|
27
|
+
warn "[DEPRECATION] `language` is deprecated. Please use `search_query` instead."
|
28
|
+
@language
|
29
|
+
end
|
30
|
+
|
31
|
+
# @deprecated Please use {#search_query=} instead
|
32
|
+
def language=(language_code)
|
33
|
+
warn "[DEPRECATION] `language=` is deprecated. Please use `search_query=` instead."
|
34
|
+
@language = language_code
|
35
|
+
end
|
13
36
|
|
14
37
|
# @return [Time] the time that was set with {#start_time=}.
|
15
38
|
def start_time
|
@@ -36,7 +59,7 @@ module Twingly
|
|
36
59
|
|
37
60
|
# Executes the query and returns the result.
|
38
61
|
#
|
39
|
-
# @raise [QueryError] if {#
|
62
|
+
# @raise [QueryError] if {#search_query} is empty.
|
40
63
|
# @raise [AuthError] if the API couldn't authenticate you. Make sure your API key is correct.
|
41
64
|
# @raise [ServerError] if the query could not be executed due to a server error.
|
42
65
|
# @return [Result] the result for this query.
|
@@ -50,18 +73,21 @@ module Twingly
|
|
50
73
|
Faraday::Utils.build_query(request_parameters)
|
51
74
|
end
|
52
75
|
|
53
|
-
# @raise [QueryError] if {#
|
76
|
+
# @raise [QueryError] if {#search_query} is empty.
|
54
77
|
# @return [Hash] the request parameters.
|
55
78
|
def request_parameters
|
56
|
-
|
79
|
+
full_search_query = search_query.to_s.dup
|
80
|
+
full_search_query << " lang:#{@language}" unless @language.to_s.empty?
|
81
|
+
full_search_query << " start-date:#{formatted_start_date}" if start_time
|
82
|
+
full_search_query << " end-date:#{formatted_end_date}" if end_time
|
83
|
+
|
84
|
+
if full_search_query.to_s.empty?
|
85
|
+
fail QueryError, "Search query cannot be empty"
|
86
|
+
end
|
57
87
|
|
58
88
|
{
|
59
|
-
|
60
|
-
|
61
|
-
documentlang: language,
|
62
|
-
ts: ts,
|
63
|
-
tsTo: ts_to,
|
64
|
-
xmloutputversion: 2,
|
89
|
+
apikey: client.api_key,
|
90
|
+
q: full_search_query
|
65
91
|
}
|
66
92
|
end
|
67
93
|
|
@@ -95,12 +121,16 @@ module Twingly
|
|
95
121
|
fail QueryError, "Not a Time object" unless time.respond_to?(:to_time)
|
96
122
|
end
|
97
123
|
|
98
|
-
def
|
99
|
-
start_time
|
124
|
+
def formatted_start_date
|
125
|
+
format_timestamp_for_query(start_time) if start_time
|
126
|
+
end
|
127
|
+
|
128
|
+
def formatted_end_date
|
129
|
+
format_timestamp_for_query(end_time) if end_time
|
100
130
|
end
|
101
131
|
|
102
|
-
def
|
103
|
-
|
132
|
+
def format_timestamp_for_query(timestamp)
|
133
|
+
timestamp.to_time.utc.strftime("%FT%T")
|
104
134
|
end
|
105
135
|
end
|
106
136
|
end
|
@@ -12,6 +12,16 @@ module Twingly
|
|
12
12
|
class Result
|
13
13
|
attr_accessor :number_of_matches_returned, :number_of_matches_total,
|
14
14
|
:seconds_elapsed
|
15
|
+
attr_writer :incomplete_result
|
16
|
+
|
17
|
+
# @return [true] if one or multiple servers were too slow to respond
|
18
|
+
# within the maximum allowed query time.
|
19
|
+
# @return [false] if all servers responded within the maximum allowed
|
20
|
+
# query time.
|
21
|
+
# @see https://developer.twingly.com/resources/search/#response
|
22
|
+
def incomplete?
|
23
|
+
@incomplete_result
|
24
|
+
end
|
15
25
|
|
16
26
|
# @return [Array<Post>] all posts that matched the {Query}.
|
17
27
|
def posts
|
@@ -28,6 +38,7 @@ module Twingly
|
|
28
38
|
matches = "@posts, "
|
29
39
|
matches << "@number_of_matches_returned=#{self.number_of_matches_returned}, "
|
30
40
|
matches << "@number_of_matches_total=#{self.number_of_matches_total}"
|
41
|
+
matches << "@incomplete_result=#{self.incomplete?}"
|
31
42
|
|
32
43
|
sprintf("#<%s:0x%x %s>", self.class.name, __id__, matches)
|
33
44
|
end
|
data/spec/client_spec.rb
CHANGED
@@ -90,13 +90,13 @@ describe Client do
|
|
90
90
|
|
91
91
|
let(:query) do
|
92
92
|
query = subject.query
|
93
|
-
query.
|
93
|
+
query.search_query = "something"
|
94
94
|
query
|
95
95
|
end
|
96
96
|
|
97
97
|
it "should raise error on invalid API key" do
|
98
98
|
VCR.use_cassette("search_without_valid_api_key") do
|
99
|
-
expect { subject.execute_query(query) }.to raise_error(AuthError,
|
99
|
+
expect { subject.execute_query(query) }.to raise_error(AuthError, /Unauthorized/)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
data/spec/error_spec.rb
CHANGED
@@ -3,17 +3,37 @@ require "spec_helper"
|
|
3
3
|
describe Twingly::Search::Error do
|
4
4
|
it { is_expected.to be_a(StandardError) }
|
5
5
|
|
6
|
-
|
7
|
-
subject { described_class.from_api_response_message(server_response_message) }
|
6
|
+
let(:message) { "This is the error message!" }
|
8
7
|
|
9
|
-
|
10
|
-
|
8
|
+
describe ".from_api_response" do
|
9
|
+
subject { described_class.from_api_response(code, message) }
|
11
10
|
|
12
|
-
|
11
|
+
context "when given code 401" do
|
12
|
+
let(:code) { 401 }
|
13
|
+
|
14
|
+
it { is_expected.to be_a(AuthError) }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when given code 402" do
|
18
|
+
let(:code) { 402 }
|
19
|
+
|
20
|
+
it { is_expected.to be_a(AuthError) }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when given code 400" do
|
24
|
+
let(:code) { 400 }
|
25
|
+
|
26
|
+
it { is_expected.to be_a(QueryError) }
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when given code 404" do
|
30
|
+
let(:code) { 404 }
|
31
|
+
|
32
|
+
it { is_expected.to be_a(QueryError) }
|
13
33
|
end
|
14
34
|
|
15
|
-
context "when given
|
16
|
-
let(:
|
35
|
+
context "when given another code" do
|
36
|
+
let(:code) { 500 }
|
17
37
|
|
18
38
|
it { is_expected.to be_a(ServerError) }
|
19
39
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<html><body>This is a non XML fixture.</body></html>
|
@@ -0,0 +1,79 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<twinglydata ts="2017-04-11T15:09:48.8750635Z" from="2017-04-10T22:00:00Z" numberOfPosts="3" maxNumberOfPosts="3" firstPost="2017-04-10T22:00:29.267Z" lastPost="2017-04-10T22:11:47.243Z" nextTimestamp="2017-04-10T22:11:47.244Z">
|
3
|
+
<post>
|
4
|
+
<id>727444183574244541</id>
|
5
|
+
<author />
|
6
|
+
<url>http://flinnman.blogg.se/2017/april/mandag-igen.html</url>
|
7
|
+
<title>Måndag igen</title>
|
8
|
+
<text>Hoppla hejsan bloggy!Måndag. Förutom trist hosta på E så mår vi fint. Vi använde helgen till massa umgänge med nära och kära. Kändes extra skönt efter fredagen. Vi hade en tyst minut i en full matsal i polishuset idag. Det var fint! Well. Ny vecka och ... - Läs hela inlägget här</text>
|
9
|
+
<languageCode>sv</languageCode>
|
10
|
+
<locationCode>se</locationCode>
|
11
|
+
<coordinates />
|
12
|
+
<links />
|
13
|
+
<tags>
|
14
|
+
<tag>Jag</tag>
|
15
|
+
</tags>
|
16
|
+
<images />
|
17
|
+
<indexedAt>2017-04-10T22:00:24Z</indexedAt>
|
18
|
+
<publishedAt>2017-04-10T19:11:11Z</publishedAt>
|
19
|
+
<reindexedAt>2017-04-10T22:00:24Z</reindexedAt>
|
20
|
+
<inlinksCount>0</inlinksCount>
|
21
|
+
<blogId>10357806725947705095</blogId>
|
22
|
+
<blogName>Frida L</blogName>
|
23
|
+
<blogUrl>http://flinnman.blogg.se</blogUrl>
|
24
|
+
<blogRank>1</blogRank>
|
25
|
+
<authority>2</authority>
|
26
|
+
</post>
|
27
|
+
<post>
|
28
|
+
<id>6564050082079070812</id>
|
29
|
+
<author />
|
30
|
+
<url>http://malinbs.blogg.se/2017/april/du-ska-hedra-din-fader-och-din-moder.html</url>
|
31
|
+
<title>Du ska hedra din fader och din moder</title>
|
32
|
+
<text>Vilken solig och god helg det har varit på mina breddgrader! Minns för en massa år sedan (25? Eller lite mera?) när Herrn i Huset byggde altanen och samtidigt såg till att det blev eluttag där. Min mammas första kommentar blev: Tänk så bra med ett uttag, ... - Läs hela inlägget här</text>
|
33
|
+
<languageCode>sv</languageCode>
|
34
|
+
<locationCode>se</locationCode>
|
35
|
+
<coordinates />
|
36
|
+
<links />
|
37
|
+
<tags>
|
38
|
+
<tag>Allmänt</tag>
|
39
|
+
</tags>
|
40
|
+
<images />
|
41
|
+
<indexedAt>2017-04-10T22:00:16Z</indexedAt>
|
42
|
+
<publishedAt>2017-04-09T17:12:08Z</publishedAt>
|
43
|
+
<reindexedAt>2017-04-10T22:00:16Z</reindexedAt>
|
44
|
+
<inlinksCount>0</inlinksCount>
|
45
|
+
<blogId>14781290076709326355</blogId>
|
46
|
+
<blogName>Blogga, ett sätt att umgås!</blogName>
|
47
|
+
<blogUrl>http://malinbs.blogg.se</blogUrl>
|
48
|
+
<blogRank>1</blogRank>
|
49
|
+
<authority>4</authority>
|
50
|
+
</post>
|
51
|
+
<post>
|
52
|
+
<id>3062976931264108164</id>
|
53
|
+
<author>josegacel</author>
|
54
|
+
<url>https://josegabrielcelis.wordpress.com/2017/04/09/1476/</url>
|
55
|
+
<title />
|
56
|
+
<text>from Instagram: http://ift.tt/2ofZdhV</text>
|
57
|
+
<languageCode>sv</languageCode>
|
58
|
+
<locationCode />
|
59
|
+
<coordinates />
|
60
|
+
<links>
|
61
|
+
<link>http://www.ift.tt/2ofZdhV</link>
|
62
|
+
<link>http://feeds.wordpress.com/1.0/gocomments/josegabrielcelis.wordpress.com/1476</link>
|
63
|
+
</links>
|
64
|
+
<tags>
|
65
|
+
<tag>Fotos</tag>
|
66
|
+
<tag>Instagram</tag>
|
67
|
+
</tags>
|
68
|
+
<images />
|
69
|
+
<indexedAt>2017-04-10T22:00:28Z</indexedAt>
|
70
|
+
<publishedAt>2017-04-09T22:31:34Z</publishedAt>
|
71
|
+
<reindexedAt>2017-04-10T22:00:28Z</reindexedAt>
|
72
|
+
<inlinksCount>0</inlinksCount>
|
73
|
+
<blogId>1811310581070495497</blogId>
|
74
|
+
<blogName>José Gabriel Celis</blogName>
|
75
|
+
<blogUrl>https://josegabrielcelis.wordpress.com</blogUrl>
|
76
|
+
<blogRank>1</blogRank>
|
77
|
+
<authority>0</authority>
|
78
|
+
</post>
|
79
|
+
</twinglydata>
|