feedparser 1.1.1 → 1.2.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/Manifest.txt +36 -7
- data/README.md +145 -10
- data/Rakefile +1 -1
- data/lib/feedparser.rb +3 -0
- data/lib/feedparser/author.rb +39 -0
- data/lib/feedparser/builder/atom.rb +64 -17
- data/lib/feedparser/builder/json.rb +46 -10
- data/lib/feedparser/builder/rss.rb +94 -15
- data/lib/feedparser/feed.rb +30 -6
- data/lib/feedparser/generator.rb +34 -0
- data/lib/feedparser/item.rb +31 -6
- data/lib/feedparser/tag.rb +23 -0
- data/lib/feedparser/version.rb +2 -2
- data/test/feeds/books/nostarch.rss +125 -0
- data/test/feeds/books/oreilly.feedburner.atom +387 -0
- data/test/feeds/books/pragprog.rss +148 -0
- data/test/feeds/byparker.json +4 -4
- data/test/feeds/daringfireball.atom +8 -1
- data/test/feeds/daringfireball.json +28 -0
- data/test/feeds/googlegroups.atom +2 -2
- data/test/feeds/googlegroups2.atom +1 -1
- data/test/feeds/headius.atom +6 -5
- data/test/feeds/inessential.json +13 -4
- data/test/feeds/jsonfeed.json +8 -3
- data/test/feeds/{lambdatheultimate.rss2 → lambdatheultimate.rss} +0 -0
- data/test/feeds/{learnenough.atom → learnenough.feedburner.atom} +12 -9
- data/test/feeds/news/nytimes-blogs-bits.rss +333 -0
- data/test/feeds/news/nytimes-paul-krugman.rss +60 -0
- data/test/feeds/news/nytimes-tech.rss +653 -0
- data/test/feeds/news/nytimes-thomas-l-friedman.rss +80 -0
- data/test/feeds/news/nytimes.rss +607 -0
- data/test/feeds/news/washingtonpost-blogs-innovations.rss +183 -0
- data/test/feeds/news/washingtonpost-politics.rss +35 -0
- data/test/feeds/news/washingtonpost-world.rss +29 -0
- data/test/feeds/ongoing.atom +1619 -0
- data/test/feeds/osm/blog.openstreetmap.rss +252 -0
- data/test/feeds/osm/blogs.openstreetmap.rss +585 -0
- data/test/feeds/osm/mapbox.rss +1883 -0
- data/test/feeds/{railstutorial.atom → railstutorial.feedburner.atom} +28 -27
- data/test/feeds/{rubyflow.rss2 → rubyflow.feedburner.rss} +7 -3
- data/test/feeds/{rubymine.rss2 → rubymine.feedburner.rss} +5 -6
- data/test/feeds/scripting.rss +881 -0
- data/test/feeds/{sitepoint.rss2 → sitepoint.rss} +8 -9
- data/test/feeds/spec/atom/author.atom +48 -0
- data/test/feeds/spec/atom/authors.atom +70 -0
- data/test/feeds/spec/atom/categories.atom +66 -0
- data/test/feeds/spec/json/microblog.json +11 -2
- data/test/feeds/spec/json/tags.json +33 -0
- data/test/feeds/spec/rss/author.rss +41 -0
- data/test/feeds/spec/rss/categories.rss +64 -0
- data/test/feeds/spec/rss/creator.rss +38 -0
- data/test/feeds/{xkcd.rss2 → xkcd.rss} +0 -0
- data/test/helper.rb +21 -16
- data/test/test_atom.rb +1 -1
- data/test/test_atom_live.rb +1 -1
- data/test/test_authors.rb +26 -0
- data/test/test_books.rb +25 -0
- data/test/test_feeds.rb +4 -2
- data/test/test_rss.rb +4 -4
- data/test/test_tags.rb +25 -0
- metadata +39 -10
@@ -32,34 +32,50 @@ class JsonFeedBuilder
|
|
32
32
|
feed.summary = h['description']
|
33
33
|
|
34
34
|
|
35
|
+
if h['author']
|
36
|
+
feed.authors << build_author( h['author'] )
|
37
|
+
end
|
38
|
+
|
35
39
|
|
36
|
-
items = []
|
37
40
|
h['items'].each do |hash_item|
|
38
|
-
items <<
|
41
|
+
feed.items << build_item( hash_item )
|
39
42
|
end
|
40
|
-
feed.items = items
|
41
43
|
|
42
44
|
feed # return new feed
|
43
45
|
end # method build_feed_from_json
|
44
46
|
|
45
47
|
|
48
|
+
def build_author( h )
|
49
|
+
author = Author.new
|
50
|
+
|
51
|
+
author.name = h['name']
|
52
|
+
author.url = h['url']
|
53
|
+
author.avatar = h['avatar']
|
54
|
+
|
55
|
+
author
|
56
|
+
end
|
57
|
+
|
46
58
|
|
47
|
-
|
59
|
+
|
60
|
+
def build_item( h )
|
48
61
|
item = Item.new # Item.new
|
49
62
|
|
50
|
-
item.guid
|
51
|
-
item.title
|
52
|
-
item.url
|
63
|
+
item.guid = h['id']
|
64
|
+
item.title = h['title']
|
65
|
+
item.url = h['url']
|
66
|
+
item.external_url = h['external_url']
|
53
67
|
|
54
68
|
## convert date if present (from string to date type)
|
55
69
|
date_published_str = h['date_published']
|
56
70
|
if date_published_str
|
57
|
-
item.
|
71
|
+
item.published_local = DateTime.iso8601( date_published_str )
|
72
|
+
item.published = item.published_local.utc
|
58
73
|
end
|
59
74
|
|
60
75
|
date_modified_str = h['date_modified']
|
61
76
|
if date_modified_str
|
62
|
-
item.
|
77
|
+
item.updated_local = DateTime.iso8601( date_modified_str )
|
78
|
+
item.updated = item.updated_local.utc
|
63
79
|
end
|
64
80
|
|
65
81
|
|
@@ -67,8 +83,28 @@ class JsonFeedBuilder
|
|
67
83
|
item.content_text = h['content_text']
|
68
84
|
item.summary = h['summary']
|
69
85
|
|
86
|
+
if h['author']
|
87
|
+
item.authors << build_author( h['author'] )
|
88
|
+
end
|
89
|
+
|
90
|
+
if h['tags']
|
91
|
+
h['tags'].each do |json_tag|
|
92
|
+
item.tags << build_tag( json_tag )
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
70
96
|
item
|
71
|
-
end # method
|
97
|
+
end # method build_item
|
98
|
+
|
99
|
+
|
100
|
+
def build_tag( json_tag )
|
101
|
+
## pp rss_cat
|
102
|
+
tag = Tag.new
|
103
|
+
|
104
|
+
tag.name = json_tag
|
105
|
+
|
106
|
+
tag
|
107
|
+
end # build_tag
|
72
108
|
|
73
109
|
|
74
110
|
end # JsonFeedBuilder
|
@@ -35,25 +35,85 @@ class RssFeedBuilder
|
|
35
35
|
feed.summary = handle_content( rss_feed.channel.description, 'feed.description => summary' ) # required
|
36
36
|
feed.url = rss_feed.channel.link # required
|
37
37
|
|
38
|
-
feed.
|
39
|
-
feed.
|
38
|
+
feed.updated_local = handle_date( rss_feed.channel.lastBuildDate, 'feed.lastBuildDate => updated' ) # optional
|
39
|
+
feed.updated = feed.updated_local.utc if feed.updated_local
|
40
40
|
|
41
|
-
|
42
|
-
feed.
|
41
|
+
feed.published_local = handle_date( rss_feed.channel.pubDate, 'feed.pubDate => published' ) # optional
|
42
|
+
feed.published = feed.published_local.utc if feed.published_local
|
43
43
|
|
44
44
|
logger.debug " rss | feed.generator >#{rss_feed.channel.generator}< : #{rss_feed.channel.generator.class.name}"
|
45
45
|
|
46
|
+
feed.generator.text = rss_feed.channel.generator # optional
|
47
|
+
feed.generator.name = feed.generator.text ## note: for now set also name/title to "unparsed" (content) line (may change in the future!!!)
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
## check for managingEditor and/or webMaster
|
52
|
+
|
53
|
+
if rss_feed.channel.managingEditor
|
54
|
+
author = Author.new
|
55
|
+
author.text = rss_feed.channel.managingEditor.strip
|
56
|
+
author.name = author.text ## note: for now use "unparsed" (content) line also for name
|
57
|
+
feed.authors << author
|
58
|
+
end
|
59
|
+
|
60
|
+
if rss_feed.channel.webMaster
|
61
|
+
author = Author.new
|
62
|
+
author.text = rss_feed.channel.webMaster.strip
|
63
|
+
author.name = author.text ## note: for now use "unparsed" (content) line also for name
|
64
|
+
feed.authors << author
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
## check for dublin core (dc) metadata
|
69
|
+
|
70
|
+
if rss_feed.channel.dc_creator
|
71
|
+
## note: dc_creator wipes out authors if set with rss tag(s)
|
72
|
+
authors = []
|
73
|
+
authors << build_author_from_dublic_core_creator( rss_feed.channel.dc_creator )
|
74
|
+
feed.authors = authors
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
### check for categories (tags)
|
79
|
+
|
80
|
+
rss_feed.channel.categories.each do |rss_cat|
|
81
|
+
feed.tags << build_tag( rss_cat )
|
82
|
+
end
|
83
|
+
|
46
84
|
|
47
|
-
items = []
|
48
85
|
rss_feed.items.each do |rss_item|
|
49
|
-
items <<
|
86
|
+
feed.items << build_item( rss_item )
|
50
87
|
end
|
51
|
-
feed.items = items
|
52
88
|
|
53
89
|
feed # return new feed
|
54
90
|
end
|
55
91
|
|
56
|
-
|
92
|
+
|
93
|
+
def build_author_from_dublic_core_creator( dc_creator )
|
94
|
+
author = Author.new
|
95
|
+
author.text = dc_creator.strip
|
96
|
+
author.name = author.text # note: for now use "unparsed" creator line also for name (may change in the future!!!)
|
97
|
+
author
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
def build_tag( rss_cat )
|
103
|
+
## pp rss_cat
|
104
|
+
tag = Tag.new
|
105
|
+
|
106
|
+
## note: always strip leading n trailing spaces
|
107
|
+
## and add if preset (not blank/empty e.g. not nil or "")
|
108
|
+
tag.name = rss_cat.content.strip if rss_cat.content
|
109
|
+
tag.domain = rss_cat.domain.strip if rss_cat.domain
|
110
|
+
|
111
|
+
tag
|
112
|
+
end # build_tag
|
113
|
+
|
114
|
+
|
115
|
+
|
116
|
+
def build_item( rss_item )
|
57
117
|
|
58
118
|
item = Item.new
|
59
119
|
|
@@ -79,7 +139,8 @@ class RssFeedBuilder
|
|
79
139
|
logger.debug " rss | item.content_encoded[0..40] >#{rss_item.content_encoded ? rss_item.content_encoded[0..40] : ''}< : #{rss_item.content_encoded.class.name}"
|
80
140
|
|
81
141
|
|
82
|
-
item.
|
142
|
+
item.updated_local = handle_date( rss_item.pubDate, 'item.pubDate => updated' )
|
143
|
+
item.updated = item.updated_local.utc if item.updated_local
|
83
144
|
|
84
145
|
|
85
146
|
## fix/todo: check if rss_item.guid present? !!!!
|
@@ -94,13 +155,31 @@ class RssFeedBuilder
|
|
94
155
|
logger.warn " rss | item.guid.content missing !!!! - using link for guid"
|
95
156
|
end
|
96
157
|
|
97
|
-
### todo: add support or authors (incl. dc:creator)
|
98
|
-
## <dc:creator>Dhaivat Pandya</dc:creator>
|
99
158
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
159
|
+
if rss_item.author
|
160
|
+
author = Author.new
|
161
|
+
author.text = rss_item.author.strip
|
162
|
+
author.name = author.text ## note: for now use "unparsed" (content) line also for name
|
163
|
+
item.authors << author
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
## check for dublin core (dc) metadata
|
168
|
+
|
169
|
+
if rss_item.dc_creator
|
170
|
+
## note: dc_creator wipes out authors if set with rss tag(s)
|
171
|
+
authors = []
|
172
|
+
authors << build_author_from_dublic_core_creator( rss_item.dc_creator )
|
173
|
+
item.authors = authors
|
174
|
+
end
|
175
|
+
|
176
|
+
|
177
|
+
### check for categories (tags)
|
178
|
+
|
179
|
+
rss_item.categories.each do |rss_cat|
|
180
|
+
item.tags << build_tag( rss_cat )
|
181
|
+
end
|
182
|
+
|
104
183
|
|
105
184
|
item
|
106
185
|
end # method build_feed_item_from_rss
|
data/lib/feedparser/feed.rb
CHANGED
@@ -4,14 +4,27 @@ module FeedParser
|
|
4
4
|
|
5
5
|
class Feed
|
6
6
|
|
7
|
-
attr_accessor :format # e.g. atom|rss 2.0|etc.
|
7
|
+
attr_accessor :format # e.g. atom|rss 2.0|json etc.
|
8
8
|
attr_accessor :title
|
9
9
|
attr_accessor :url ## todo - add alias site_url/home_page_url/page_url - why? why not??
|
10
|
-
attr_accessor :feed_url
|
10
|
+
attr_accessor :feed_url
|
11
11
|
|
12
12
|
|
13
13
|
attr_accessor :items
|
14
14
|
|
15
|
+
attr_accessor :authors
|
16
|
+
def authors?() @authors && @authors.size > 0; end
|
17
|
+
## note: author? is an alias for authors?
|
18
|
+
alias :author? :authors?
|
19
|
+
|
20
|
+
## add author shortcut e.g. equals authors[0] - for now only read only
|
21
|
+
## fix: also add author= why? why not???
|
22
|
+
def author() @authors[0]; end
|
23
|
+
|
24
|
+
|
25
|
+
attr_accessor :tags
|
26
|
+
def tags?() @tags && @tags.size > 0; end
|
27
|
+
|
15
28
|
|
16
29
|
def summary?() @summary.nil? == false; end
|
17
30
|
attr_accessor :summary # e.g. description (rss)|subtitle (atom)
|
@@ -26,24 +39,35 @@ class Feed
|
|
26
39
|
## for now alias summary to subtitle
|
27
40
|
alias :subtitle :summary
|
28
41
|
alias :subtitle= :summary=
|
42
|
+
alias :subtitle? :summary?
|
29
43
|
|
30
44
|
|
31
45
|
|
32
46
|
def updated?() @updated.nil? == false; end
|
33
|
-
attr_accessor :updated # e.g. lastBuildDate (rss)|updated (atom)
|
47
|
+
attr_accessor :updated # e.g. lastBuildDate (rss)|updated (atom) -- always (converted) to utc
|
48
|
+
attr_accessor :updated_local # "unparsed" local datetime as in feed (NOT converted to utc)
|
34
49
|
|
35
50
|
def published?() @published.nil? == false; end
|
36
|
-
attr_accessor :published
|
51
|
+
attr_accessor :published # e.g. pubDate (rss)\n/a (atom) -- note: published is basically an alias for created
|
52
|
+
attr_accessor :published_local # "unparsed" local datetime as in feed (NOT converted to utc)
|
37
53
|
|
38
54
|
|
39
55
|
attr_accessor :generator
|
40
|
-
attr_accessor :generator_version # e.g. @version (atom)
|
41
|
-
attr_accessor :generator_uri # e.g. @uri (atom) - use alias url/link ???
|
42
56
|
|
43
57
|
|
44
58
|
## fix:
|
45
59
|
# add pretty printer/inspect (exclude object)
|
46
60
|
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
## note: make items, authors, tags empty arrays on startup (e.g. not nil)
|
64
|
+
@items = []
|
65
|
+
@authors = []
|
66
|
+
@tags = []
|
67
|
+
|
68
|
+
@generator = Generator.new
|
69
|
+
end
|
70
|
+
|
47
71
|
end # class Feed
|
48
72
|
|
49
73
|
end # module FeedParser
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FeedParser
|
4
|
+
|
5
|
+
class Generator
|
6
|
+
|
7
|
+
attr_accessor :name
|
8
|
+
## note: title is an alias for name
|
9
|
+
alias :title :name
|
10
|
+
alias :title= :name=
|
11
|
+
|
12
|
+
attr_accessor :version
|
13
|
+
|
14
|
+
attr_accessor :url
|
15
|
+
## note: uri is an alias for url
|
16
|
+
alias :uri :url ## add atom alias for uri - why? why not?
|
17
|
+
alias :uri= :url=
|
18
|
+
|
19
|
+
|
20
|
+
attr_accessor :text # note: holds "unparsed" text (content) line form rss:generator
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
## note: to_s - allows to use just generator in templates
|
24
|
+
## will by default return name if present or as fallback "unparsed" text line
|
25
|
+
if @name ## not blank
|
26
|
+
@name
|
27
|
+
else
|
28
|
+
@text
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end # class Generator
|
33
|
+
|
34
|
+
end # module FeedParser
|
data/lib/feedparser/item.rb
CHANGED
@@ -6,6 +6,11 @@ class Item
|
|
6
6
|
|
7
7
|
attr_accessor :title
|
8
8
|
attr_accessor :url
|
9
|
+
attr_accessor :external_url
|
10
|
+
|
11
|
+
# note: related_url is an alias for external_url
|
12
|
+
alias :related_url :external_url ## link rel=related used in atom
|
13
|
+
alias :related_url= :external_url=
|
9
14
|
|
10
15
|
|
11
16
|
## note: only content/content_html should use html;
|
@@ -33,20 +38,40 @@ class Item
|
|
33
38
|
|
34
39
|
def updated?() @updated.nil? == false; end
|
35
40
|
attr_accessor :updated # pubDate (RSS)|updated (Atom)
|
41
|
+
attr_accessor :updated_local # "unparsed" local datetime as in feed (NOT converted to utc)
|
36
42
|
|
37
43
|
def published?() @published.nil? == false; end
|
38
44
|
attr_accessor :published # note: published is basically an alias for created
|
45
|
+
attr_accessor :published_local # "unparsed" local datetime as in feed (NOT converted to utc)
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
attr_accessor :id
|
50
|
+
|
51
|
+
## note: guid is an alias for id
|
52
|
+
alias :guid :id
|
53
|
+
alias :guid= :id=
|
39
54
|
|
55
|
+
attr_accessor :authors
|
56
|
+
## add author shortcut e.g. equals authors[0] - for now only read only
|
57
|
+
## fix: also add author= why? why not???
|
58
|
+
def authors?() @authors && @authors.size > 0; end
|
59
|
+
## note: author? is an alias for authors?
|
60
|
+
alias :author? :authors?
|
40
61
|
|
41
|
-
|
62
|
+
## add author shortcut e.g. equals authors[0] - for now only read only
|
63
|
+
## fix: also add author= why? why not???
|
64
|
+
def author() @authors[0]; end
|
42
65
|
|
43
|
-
## note: guid is an alias for id; todo/fix: reverse - make guid an alias of id
|
44
|
-
alias :id :guid
|
45
|
-
alias :id= :guid=
|
46
66
|
|
67
|
+
attr_accessor :tags
|
68
|
+
def tags?() @tags && @tags.size > 0; end
|
47
69
|
|
48
|
-
|
49
|
-
##
|
70
|
+
def initialize
|
71
|
+
## note: make authors, tags empty arrays on startup (e.g. not nil)
|
72
|
+
@authors = []
|
73
|
+
@tags = []
|
74
|
+
end
|
50
75
|
|
51
76
|
end # class Item
|
52
77
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module FeedParser
|
4
|
+
|
5
|
+
class Tag
|
6
|
+
|
7
|
+
attr_accessor :name
|
8
|
+
## note: title n term are aliases for name
|
9
|
+
alias :title :name
|
10
|
+
alias :title= :name=
|
11
|
+
|
12
|
+
alias :term :name
|
13
|
+
alias :term= :name=
|
14
|
+
|
15
|
+
|
16
|
+
attr_accessor :scheme ## use scheme_url -why? why not? is it always a url/uri??
|
17
|
+
## note: domain (rss) is an alias for scheme (atom)
|
18
|
+
alias :domain :scheme
|
19
|
+
alias :domain= :scheme=
|
20
|
+
|
21
|
+
end # class Tag
|
22
|
+
|
23
|
+
end # module FeedParser
|
data/lib/feedparser/version.rb
CHANGED
@@ -0,0 +1,125 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<rss version="2.0" xml:base="https://www.nostarch.com/frontpage_comingsoon"
|
3
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
4
|
+
xmlns:atom="http://www.w3.org/2005/Atom">
|
5
|
+
<channel>
|
6
|
+
<title>No Starch Press - Coming Soon</title>
|
7
|
+
<link>https://www.nostarch.com/frontpage_comingsoon</link>
|
8
|
+
<description>Keep an eye on what's coming out down the line.</description>
|
9
|
+
<language>en</language>
|
10
|
+
<atom:link href="https://www.nostarch.com/feeds/comingsoon.xml?startat=tcpip" rel="self" type="application/rss+xml" />
|
11
|
+
<item>
|
12
|
+
<title>PoC||GTFO</title>
|
13
|
+
<link>https://www.nostarch.com/gtfo</link>
|
14
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/gtfo"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/GTFO_cover.png?itok=DUUsVJxz" width="168" height="225" alt="PoC||GTFO" title="PoC||GTFO" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="https://www.nostarch.com/gtfo">PoC||GTFO</a> is a compilation of the wildly popular hacker zine of the same name. Contributions range from humorous poems to deeply technical essays.</p>
|
15
|
+
</div></div></div></description>
|
16
|
+
<pubDate>Tue, 11 Apr 2017 23:50:54 +0000</pubDate>
|
17
|
+
<dc:creator>Caitlin Griffin</dc:creator>
|
18
|
+
<guid isPermaLink="false">423 at https://www.nostarch.com</guid>
|
19
|
+
</item>
|
20
|
+
<item>
|
21
|
+
<title>Coding iPhone Apps for Kids</title>
|
22
|
+
<link>https://www.nostarch.com/iphoneappsforkids</link>
|
23
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/iphoneappsforkids"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/CodingiPhoneAppsforKids_cover.png?itok=zfRGgmaL" width="170" height="225" alt="Coding iPhone Apps for Kids" title="Coding iPhone Apps for Kids" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="/iphoneappsforkids">Coding iPhone Apps for Kids</a> teaches you how to program the iOS apps and games you’ve always wanted to make! <i>PDF Available Now!</i></p>
|
24
|
+
</div></div></div></description>
|
25
|
+
<pubDate>Fri, 11 Mar 2016 19:07:21 +0000</pubDate>
|
26
|
+
<dc:creator>Caitlin Griffin</dc:creator>
|
27
|
+
<guid isPermaLink="false">371 at https://www.nostarch.com</guid>
|
28
|
+
</item>
|
29
|
+
<item>
|
30
|
+
<title>Gray Hat C#</title>
|
31
|
+
<link>https://www.nostarch.com/grayhatcsharp</link>
|
32
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/grayhatcsharp"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/GrayHatC_cover.png?itok=oFuFmPd9" width="170" height="225" alt="Gray Hat C#" title="Gray Hat C#" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="https://www.nostarch.com/GrayHatCSharp">Gray Hat C#</a> shows you how to use C#'s powerful set of core libraries to create and automate security tools. <i>PDF Available Now!</i></p>
|
33
|
+
</div></div></div></description>
|
34
|
+
<pubDate>Wed, 27 Jul 2016 18:35:33 +0000</pubDate>
|
35
|
+
<dc:creator>Caitlin Griffin</dc:creator>
|
36
|
+
<guid isPermaLink="false">389 at https://www.nostarch.com</guid>
|
37
|
+
</item>
|
38
|
+
<item>
|
39
|
+
<title>The Arduino Inventor's Guide</title>
|
40
|
+
<link>https://www.nostarch.com/sparkfunarduino</link>
|
41
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/sparkfunarduino"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/ArduinoInventor%27sGuide_cover_0.png?itok=eSSQoSei" width="170" height="225" alt="The Arduino Inventor&#039;s Guide" title="The Arduino Inventor&#039;s Guide" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="/sparkfunarduino">The Arduino Inventor's Guide</a> is a hands-on introduction to exploring electronics with Arduino for total beginners. <i>Now Available in Early Access!</i></p>
|
42
|
+
</div></div></div></description>
|
43
|
+
<pubDate>Tue, 19 May 2015 23:41:36 +0000</pubDate>
|
44
|
+
<dc:creator>Anonymous</dc:creator>
|
45
|
+
<guid isPermaLink="false">346 at https://www.nostarch.com</guid>
|
46
|
+
</item>
|
47
|
+
<item>
|
48
|
+
<title>The Manga Guide to Microprocessors</title>
|
49
|
+
<link>https://www.nostarch.com/microprocessors</link>
|
50
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/microprocessors"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/MangaMicroprocessors_cover_0.png?itok=MxmdxH9e" width="170" height="225" alt="The Manga Guide to Microprocessors" title="The Manga Guide to Microprocessors" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="/microprocessors">The Manga Guide to Microprocessors</a> is a comic guide to microprocessors, computer architecture, and digital operations. <i>Now Available in Early Access!</i></p>
|
51
|
+
</div></div></div></description>
|
52
|
+
<pubDate>Mon, 24 Oct 2016 20:43:55 +0000</pubDate>
|
53
|
+
<dc:creator>ahariri</dc:creator>
|
54
|
+
<guid isPermaLink="false">393 at https://www.nostarch.com</guid>
|
55
|
+
</item>
|
56
|
+
<item>
|
57
|
+
<title>Arduino Project Handbook, Vol. 2</title>
|
58
|
+
<link>https://www.nostarch.com/arduinohandbook2</link>
|
59
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/arduinohandbook2"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/ArduinoProjectHandbookII_cover.png?itok=FkF3sw-p" width="170" height="225" alt="Arduino Project Handbook Vol. 2" title="Arduino Project Handbook Vol. 2" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="/arduinohandbook2">Arduino Project Handbook, Vol. 2</a> is a full-color guide to building 25 fun and practical projects with the low-cost Arduino microcontroller. <i>Now Available in Early Access!</i></p>
|
60
|
+
</div></div></div></description>
|
61
|
+
<pubDate>Mon, 24 Oct 2016 21:24:45 +0000</pubDate>
|
62
|
+
<dc:creator>ahariri</dc:creator>
|
63
|
+
<guid isPermaLink="false">394 at https://www.nostarch.com</guid>
|
64
|
+
</item>
|
65
|
+
<item>
|
66
|
+
<title>Think Like a Programmer, Python Edition</title>
|
67
|
+
<link>https://www.nostarch.com/thinkpython</link>
|
68
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/thinkpython"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/ThinkLikeaProgrammerPython_cover.png?itok=1yCoVJfA" width="170" height="225" alt="Think Like a Programmer, Python Edition" title="Think Like a Programmer, Python Edition" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p>The Python edition of <a href="https://www.nostarch.com/thinkpython">Think Like a Programmer</a> will teach you how to solve problems with programming.</p>
|
69
|
+
</div></div></div></description>
|
70
|
+
<pubDate>Fri, 10 Mar 2017 23:50:24 +0000</pubDate>
|
71
|
+
<dc:creator>ahariri</dc:creator>
|
72
|
+
<guid isPermaLink="false">399 at https://www.nostarch.com</guid>
|
73
|
+
</item>
|
74
|
+
<item>
|
75
|
+
<title>Computers for Seniors</title>
|
76
|
+
<link>https://www.nostarch.com/computersforseniors</link>
|
77
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/computersforseniors"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/ComputersforSeniors_cover.png?itok=sWPaSVC7" width="170" height="225" alt="Computers for Seniors" title="Computers for Seniors" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="https://www.nostarch.com/computersforseniors">Computers for Seniors</a> is a step-by-step guide to learning basic computer skills.</p>
|
78
|
+
</div></div></div></description>
|
79
|
+
<pubDate>Wed, 13 Apr 2016 20:07:07 +0000</pubDate>
|
80
|
+
<dc:creator>Caitlin Griffin</dc:creator>
|
81
|
+
<guid isPermaLink="false">375 at https://www.nostarch.com</guid>
|
82
|
+
</item>
|
83
|
+
<item>
|
84
|
+
<title>Game Console</title>
|
85
|
+
<link>https://www.nostarch.com/gameconsole</link>
|
86
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/gameconsole"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/gameconsole_cover.png?itok=S9JH4c1z" width="176" height="225" alt="The Game Console" title="The Game Console" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><b><a href="/gameconsole">The Game Console</a></b> is a visual history of video game technology, with gorgeous photos of more than 80 video game consoles manufactured since 1972.</p>
|
87
|
+
</div></div></div></description>
|
88
|
+
<pubDate>Sat, 09 Jan 2016 00:31:43 +0000</pubDate>
|
89
|
+
<dc:creator>Caitlin Griffin</dc:creator>
|
90
|
+
<guid isPermaLink="false">362 at https://www.nostarch.com</guid>
|
91
|
+
</item>
|
92
|
+
<item>
|
93
|
+
<title>Serious Cryptography</title>
|
94
|
+
<link>https://www.nostarch.com/seriouscrypto</link>
|
95
|
+
<description><div class="field field-name-field-image-cache field-type-image field-label-hidden"><div class="field-items"><div class="field-item even"><a href="/seriouscrypto"><img class="img-responsive" src="https://www.nostarch.com/sites/default/files/styles/uc_product/public/SeriousCrypto_cover_0.png?itok=s0OdQ-Pl" width="170" height="225" alt="Serious Cryptography" title="Serious Cryptography" /></a></div></div></div><div class="field field-name-body field-type-text-with-summary field-label-hidden"><div class="field-items"><div class="field-item even"><p><a href="https://www.nostarch.com/SeriousCrypto">Serious Cryptography</a> is a practical guide to the past, present, and future of cryptographic systems and algorithms. <i>Now Available in Early Access!</i></p>
|
96
|
+
</div></div></div></description>
|
97
|
+
<pubDate>Tue, 25 Apr 2017 22:25:46 +0000</pubDate>
|
98
|
+
<dc:creator>Caitlin Griffin</dc:creator>
|
99
|
+
<guid isPermaLink="false">428 at https://www.nostarch.com</guid>
|
100
|
+
</item>
|
101
|
+
</channel>
|
102
|
+
</rss>
|
103
|
+
|
104
|
+
---
|
105
|
+
|
106
|
+
feed.format: rss 2.0
|
107
|
+
feed.title: No Starch Press - Coming Soon
|
108
|
+
feed.url: https://www.nostarch.com/frontpage_comingsoon
|
109
|
+
feed.summary: Keep an eye on what's coming out down the line.
|
110
|
+
|
111
|
+
|
112
|
+
feed.items.size: >>> 10
|
113
|
+
|
114
|
+
feed.items[0].title: PoC||GTFO
|
115
|
+
feed.items[0].url: https://www.nostarch.com/gtfo
|
116
|
+
feed.items[0].guid: 423 at https://www.nostarch.com
|
117
|
+
feed.items[0].updated: >>> DateTime.new( 2017, 4, 11, 23, 50, 54 )
|
118
|
+
|
119
|
+
feed.items[1].title: Coding iPhone Apps for Kids
|
120
|
+
feed.items[1].url: https://www.nostarch.com/iphoneappsforkids
|
121
|
+
feed.items[1].guid: 371 at https://www.nostarch.com
|
122
|
+
feed.items[1].updated: >>> DateTime.new( 2016, 3, 11, 19, 7, 21 )
|
123
|
+
|
124
|
+
|
125
|
+
>>> pp feed.items[0].summary
|