murmuring_spider 0.0.2
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/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE +22 -0
- data/README.md +40 -0
- data/Rakefile +14 -0
- data/lib/murmuring_spider.rb +15 -0
- data/lib/murmuring_spider/operation.rb +82 -0
- data/lib/murmuring_spider/status.rb +68 -0
- data/lib/murmuring_spider/version.rb +3 -0
- data/murmuring_spider.gemspec +37 -0
- data/spec/murmuring_spider/operation_spec.rb +118 -0
- data/spec/murmuring_spider/status_spec.rb +56 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/twitter_search_status.dump +1 -0
- data/spec/twitter_status.dump +0 -0
- metadata +200 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 tomykaira
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# MurmuringSpider
|
2
|
+
|
3
|
+
[](http://travis-ci.org/tomykaira/murmuring_spider)
|
4
|
+
|
5
|
+
|
6
|
+
MurmuringSpider is a concise Twitter crawler.
|
7
|
+
|
8
|
+
When we write a data-mining / text-mining application based on twitter's timeline, we have to collect and store tweets first.
|
9
|
+
|
10
|
+
I am irritated with writing such crawler repeatedly, so I wrote this.
|
11
|
+
|
12
|
+
What you have to do is only to add query and to run them periodically.
|
13
|
+
|
14
|
+
Thanks to consistent Twitter API and [twitter gem](http://twitter.rubyforge.org/), it is quite easy to track various types of timelines (such as user_timeline, home_timeline, search...)
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
gem 'murmuring_spider'
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install murmuring_spider
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
[Usage of murmuring spider — Gist](https://gist.github.com/2060445)
|
33
|
+
|
34
|
+
## Contributing
|
35
|
+
|
36
|
+
1. Fork it
|
37
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
38
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
39
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
40
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rspec/core'
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
8
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
task :default => :spec
|
12
|
+
rescue LoadError
|
13
|
+
puts 'RSpec is not installed'
|
14
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-migrations'
|
3
|
+
|
4
|
+
require "murmuring_spider/version"
|
5
|
+
require "murmuring_spider/operation"
|
6
|
+
require "murmuring_spider/status"
|
7
|
+
|
8
|
+
module MurmuringSpider
|
9
|
+
extend MurmuringSpider
|
10
|
+
|
11
|
+
def database_init(db)
|
12
|
+
DataMapper.setup(:default, db)
|
13
|
+
DataMapper.auto_upgrade!
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-validations'
|
3
|
+
require 'twitter'
|
4
|
+
|
5
|
+
#
|
6
|
+
# Operation: represents request to Twitter
|
7
|
+
#
|
8
|
+
module MurmuringSpider
|
9
|
+
class Operation
|
10
|
+
include DataMapper::Resource
|
11
|
+
|
12
|
+
property :id, Serial
|
13
|
+
property :type, String
|
14
|
+
property :target, String
|
15
|
+
property :opts, Object
|
16
|
+
|
17
|
+
validates_uniqueness_of :target, :scope => :type
|
18
|
+
|
19
|
+
has n, :statuses
|
20
|
+
|
21
|
+
self.raise_on_save_failure = true
|
22
|
+
|
23
|
+
class << self
|
24
|
+
#
|
25
|
+
# Add an operation
|
26
|
+
# * _type_ : request type. Name of a Twitter's method
|
27
|
+
# * _target_ : First argument of the Twitter's method. Usually, an user or a operation
|
28
|
+
# * _opts_ : options. Second argument of the Twitter's method.
|
29
|
+
#
|
30
|
+
# returns : created Operation instance
|
31
|
+
#
|
32
|
+
# raises : DataMapper::SaveFailureError
|
33
|
+
#
|
34
|
+
def add(type, target, opts = {})
|
35
|
+
create(:type => type, :target => target, :opts => opts)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Run all queries
|
40
|
+
#
|
41
|
+
def run_all(client = Twitter)
|
42
|
+
all.map { |o| o.run(client) }
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Remove an operation specified by type and target
|
47
|
+
#
|
48
|
+
def remove(type, target)
|
49
|
+
first(:type => type, :target => target).destroy
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
#
|
54
|
+
# Execute Twitter request and update :since_id of _opts_
|
55
|
+
# This method has side effect
|
56
|
+
#
|
57
|
+
# returns : Array of Twitter::Status
|
58
|
+
#
|
59
|
+
def collect_statuses(client = Twitter)
|
60
|
+
res = client.__send__(type, target, opts)
|
61
|
+
unless res.empty?
|
62
|
+
self.opts = opts.merge(:since_id => res.first.id)
|
63
|
+
save
|
64
|
+
end
|
65
|
+
res
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# Collect tweet statuses and save them
|
70
|
+
# Return value should not be used
|
71
|
+
#
|
72
|
+
def run(client = Twitter)
|
73
|
+
collect_statuses(client).each do |s|
|
74
|
+
|
75
|
+
# not to raise error on save, remove an invalid status beforehand
|
76
|
+
last = self.statuses.new(s)
|
77
|
+
self.statuses.pop unless last.valid?
|
78
|
+
end
|
79
|
+
save
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'twitter/status'
|
3
|
+
|
4
|
+
class Twitter::Status
|
5
|
+
def url_expanded_text
|
6
|
+
if @attrs['entities'].nil?
|
7
|
+
text
|
8
|
+
else
|
9
|
+
@url_expanded_text ||= Array(@attrs['entities']['urls']).reduce(text) do |t, url|
|
10
|
+
t.gsub(url['url'], url['expanded_url'])
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
module MurmuringSpider
|
17
|
+
class Status
|
18
|
+
include DataMapper::Resource
|
19
|
+
property :id, Serial
|
20
|
+
property :tweet_id, String, :unique => :operation_id
|
21
|
+
property :text, String, :length => 255
|
22
|
+
property :user_id, String
|
23
|
+
property :screen_name, String
|
24
|
+
property :created_at, DateTime
|
25
|
+
property :extended, Object
|
26
|
+
|
27
|
+
belongs_to :operation
|
28
|
+
|
29
|
+
@@extended_fields = {}
|
30
|
+
|
31
|
+
class << self
|
32
|
+
|
33
|
+
#
|
34
|
+
# extend fields
|
35
|
+
# You can save a parameter of status which is not supported by default
|
36
|
+
# If block given, initializer gives the _Twitter::Status_ object to it,
|
37
|
+
# and the result of the given block is used as the field value
|
38
|
+
#
|
39
|
+
# * _field_ : field name. String or Symbol is expected.
|
40
|
+
# _Twitter::Status_ should have the same name method.
|
41
|
+
# * _&b_ : block to get the field value from _Twitter::Status_ object.
|
42
|
+
#
|
43
|
+
def extend(field, &b)
|
44
|
+
@@extended_fields[field] = b
|
45
|
+
define_method(field.to_s) do
|
46
|
+
extended[field]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(s)
|
52
|
+
values = {}
|
53
|
+
@@extended_fields.each do |field, func|
|
54
|
+
if func
|
55
|
+
values[field] = func.call(s)
|
56
|
+
else
|
57
|
+
values[field] = s.__send__(field)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
super(:tweet_id => s.id,
|
61
|
+
:text => s.url_expanded_text,
|
62
|
+
:user_id => s.user ? s.user.id : s.from_user_id,
|
63
|
+
:screen_name => s.user ? s.user.screen_name : s.from_user,
|
64
|
+
:created_at => s.created_at,
|
65
|
+
:extended => values)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/murmuring_spider/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["tomykaira"]
|
6
|
+
gem.email = ["tomykaira@gmail.com"]
|
7
|
+
gem.description = %q{MurmuringSpider is a concise Twitter crawler.
|
8
|
+
|
9
|
+
When we write a data-mining / text-mining application based on twitter timeline, we have to collect and store tweets first.
|
10
|
+
|
11
|
+
I am irritated with writing such crawler repeatedly, so I wrote this.
|
12
|
+
|
13
|
+
What you have to do is only to add query and to run them periodically.
|
14
|
+
|
15
|
+
Thanks to consistent Twitter API and twitter gem (http://twitter.rubyforge.org/), it is quite easy to track various types of timelines (such as user_timeline, home_timeline, search...)}
|
16
|
+
gem.summary = %q{MurmuringSpider is a concise Twitter crawler with DataMapper.}
|
17
|
+
gem.homepage = "https://github.com/tomykaira/murmuring_spider"
|
18
|
+
|
19
|
+
gem.add_dependency('dm-core')
|
20
|
+
gem.add_dependency('dm-migrations')
|
21
|
+
gem.add_dependency('dm-validations')
|
22
|
+
gem.add_dependency('twitter')
|
23
|
+
|
24
|
+
gem.add_development_dependency('rspec')
|
25
|
+
gem.add_development_dependency('guard')
|
26
|
+
gem.add_development_dependency('guard-rspec')
|
27
|
+
gem.add_development_dependency('database_cleaner')
|
28
|
+
gem.add_development_dependency('dm-sqlite-adapter')
|
29
|
+
gem.add_development_dependency('rake')
|
30
|
+
|
31
|
+
gem.files = `git ls-files`.split($\)
|
32
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
33
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
34
|
+
gem.name = "murmuring_spider"
|
35
|
+
gem.require_paths = ["lib"]
|
36
|
+
gem.version = MurmuringSpider::VERSION
|
37
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe MurmuringSpider::Operation do
|
4
|
+
let(:operation) { MurmuringSpider::Operation.add(:user_timeline, 'fake-user') }
|
5
|
+
|
6
|
+
subject { MurmuringSpider::Operation }
|
7
|
+
describe 'add' do
|
8
|
+
context 'when an user_timeline operation is added' do
|
9
|
+
before { subject.add(:user_timeline, 'tomy_kaira') }
|
10
|
+
it { should have(1).item }
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when the same operation is added' do
|
14
|
+
before { subject.add(:user_timeline, 'tomy_kaira') }
|
15
|
+
it "should raise error" do
|
16
|
+
expect { subject.add(:user_timeline, 'tomy_kaira') }.to raise_error(DataMapper::SaveFailureError)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when an operation with different types and the same target is added' do
|
21
|
+
before { subject.add(:user_timeline, 'tomy_kaira') }
|
22
|
+
it "should create new operation" do
|
23
|
+
subject.add(:search, 'tomy_kaira')
|
24
|
+
subject.count.should == 2
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'run_all' do
|
30
|
+
before do
|
31
|
+
subject.add(:user_timeline, 'fake-user')
|
32
|
+
subject.add(:favorite, 'fake-user2')
|
33
|
+
|
34
|
+
Twitter.should_receive(:user_timeline).with('fake-user', anything).and_return([])
|
35
|
+
Twitter.should_receive(:favorite).with('fake-user2', anything).and_return([])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should run all tasks" do
|
39
|
+
subject.run_all
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe 'remove' do
|
44
|
+
before do
|
45
|
+
operation.should_not be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should remove the operation" do
|
49
|
+
subject.remove(:user_timeline, 'fake-user')
|
50
|
+
subject.get(operation.id).should be_nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'collect_statuses' do
|
55
|
+
let(:response) { [status_mock(:id => 10), status_mock(:id => 7)] }
|
56
|
+
before { twitter_expectation }
|
57
|
+
|
58
|
+
context 'when the request succeeds' do
|
59
|
+
subject { operation.collect_statuses }
|
60
|
+
it { should == response }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when requested twice' do
|
64
|
+
before do
|
65
|
+
twitter_expectation({:since_id => 10}, [])
|
66
|
+
operation.collect_statuses.should == response
|
67
|
+
end
|
68
|
+
|
69
|
+
subject { MurmuringSpider::Operation.get(operation.id).collect_statuses }
|
70
|
+
it { should be_empty }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when an instance of Twitter::Client is given' do
|
75
|
+
let(:client) { mock(Twitter::Client) }
|
76
|
+
it "should use the instance, not Twitter module" do
|
77
|
+
client.should_receive(:user_timeline).with('fake-user', anything).and_return([])
|
78
|
+
operation.collect_statuses(client).should == []
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe 'run' do
|
83
|
+
let(:user) { mock(Twitter::User, :id => 12345, :screen_name => 'fake-user', :name => 'fake user') }
|
84
|
+
let(:status) { double(:id => 10,
|
85
|
+
:user => user,
|
86
|
+
:text => 'test tweet',
|
87
|
+
:created_at => "Fri Mar 16 09:04:34 +0000 2012").as_null_object }
|
88
|
+
before { twitter_expectation({}, [status]) }
|
89
|
+
|
90
|
+
it 'should create Status instance' do
|
91
|
+
operation.run
|
92
|
+
MurmuringSpider::Status.should have(1).item
|
93
|
+
status = MurmuringSpider::Status.first(:tweet_id => 10)
|
94
|
+
status.should_not be_nil
|
95
|
+
status.operation.id.should == operation.id
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when the same tweet is returned by API twice' do
|
99
|
+
before do
|
100
|
+
operation.run
|
101
|
+
Twitter.should_receive(:user_timeline).and_return([status])
|
102
|
+
operation.run
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should create only one instance' do
|
106
|
+
MurmuringSpider::Status.should have(1).item
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def status_mock(opts = {})
|
112
|
+
mock(Twitter::Status, opts)
|
113
|
+
end
|
114
|
+
|
115
|
+
def twitter_expectation(opts = {}, resp = response)
|
116
|
+
Twitter.should_receive(:user_timeline).with('fake-user', opts).and_return(resp)
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
3
|
+
|
4
|
+
shared_examples_for 'my tweet object' do
|
5
|
+
its(:tweet_id) { should == '180864326289207297' }
|
6
|
+
its(:text) { should == "OCaml でなにか書く課題がほしいです。プロコン的問題はのぞく" }
|
7
|
+
its(:user_id) { should == '287606751' }
|
8
|
+
its(:screen_name) { should == 'tomy_kaira' }
|
9
|
+
its(:created_at) { should == DateTime.parse("Sat Mar 17 03:53:10 +0000 2012") }
|
10
|
+
end
|
11
|
+
|
12
|
+
describe MurmuringSpider::Status do
|
13
|
+
let(:twitter_status) { Marshal.load(File.read(File.dirname(__FILE__) + '/../' + filename)) }
|
14
|
+
subject { MurmuringSpider::Status.new(twitter_status) }
|
15
|
+
|
16
|
+
context 'from user_timeline result' do
|
17
|
+
let(:filename) { 'twitter_status.dump' }
|
18
|
+
it_should_behave_like 'my tweet object'
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'from search result' do
|
22
|
+
let(:filename) { 'twitter_search_status.dump' }
|
23
|
+
it_should_behave_like 'my tweet object'
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'when the user extend the field' do
|
27
|
+
let(:filename) { 'twitter_status.dump' }
|
28
|
+
before do
|
29
|
+
MurmuringSpider::Status.extend(:source)
|
30
|
+
end
|
31
|
+
it_should_behave_like 'my tweet object'
|
32
|
+
its(:source) { should include 'web' }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'when the user extend the field with get strategy' do
|
36
|
+
before do
|
37
|
+
MurmuringSpider::Status.extend(:user_name) { |status| status.user ? status.user.name : status.from_user_name }
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'with user_timeline result' do
|
41
|
+
let(:filename) { 'twitter_status.dump' }
|
42
|
+
its(:user_name) { should include 'といれ' }
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'with search result' do
|
46
|
+
let(:filename) { 'twitter_search_status.dump' }
|
47
|
+
its(:user_name) { should include 'といれ' }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when the tweet has shortened URL' do
|
52
|
+
let(:twitter_status) { Twitter::Status.new('text' => 'expansion test http://example.com/shortened', 'entities' => { 'urls' => [{"expanded_url"=>"http://www.example.com/expanded", "url"=>"http://example.com/shortened"}] }) }
|
53
|
+
subject { MurmuringSpider::Status.new(twitter_status) }
|
54
|
+
its(:text) { should == 'expansion test http://www.example.com/expanded'}
|
55
|
+
end
|
56
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper.rb"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'database_cleaner'
|
9
|
+
require 'murmuring_spider'
|
10
|
+
|
11
|
+
$LOAD_PATH.push File.expand_path(__FILE__ + '/../lib')
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.run_all_when_everything_filtered = true
|
15
|
+
config.filter_run :focus
|
16
|
+
|
17
|
+
config.before(:suite) do
|
18
|
+
DatabaseCleaner.strategy = :truncation
|
19
|
+
end
|
20
|
+
|
21
|
+
config.before(:each) do
|
22
|
+
DatabaseCleaner.start
|
23
|
+
end
|
24
|
+
|
25
|
+
config.after(:each) do
|
26
|
+
DatabaseCleaner.clean
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
MurmuringSpider.database_init('sqlite3::memory:')
|
@@ -0,0 +1 @@
|
|
1
|
+
o:Twitter::Status:@attrs{I"created_at:ETI"$Sat, 17 Mar 2012 03:53:10 +0000;TI"from_user;TI"tomy_kaira;TI"from_user_id;Ti߇$I"from_user_id_str;TI"287606751;TI"from_user_name;TI"といれ;TI"geo;T0I"id;Tl+ 0�%��I"id_str;TI"180864326289207297;TI"iso_language_code;TI"ja;TI"
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: murmuring_spider
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.2
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- tomykaira
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2012-04-13 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: dm-core
|
17
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *id001
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: dm-migrations
|
28
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *id002
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: dm-validations
|
39
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
type: :runtime
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *id003
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: twitter
|
50
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
type: :runtime
|
57
|
+
prerelease: false
|
58
|
+
version_requirements: *id004
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: rspec
|
61
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ">="
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: "0"
|
67
|
+
type: :development
|
68
|
+
prerelease: false
|
69
|
+
version_requirements: *id005
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: guard
|
72
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: *id006
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: guard-rspec
|
83
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: "0"
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: *id007
|
92
|
+
- !ruby/object:Gem::Dependency
|
93
|
+
name: database_cleaner
|
94
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
95
|
+
none: false
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: "0"
|
100
|
+
type: :development
|
101
|
+
prerelease: false
|
102
|
+
version_requirements: *id008
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: dm-sqlite-adapter
|
105
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
106
|
+
none: false
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: "0"
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: *id009
|
114
|
+
- !ruby/object:Gem::Dependency
|
115
|
+
name: rake
|
116
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: "0"
|
122
|
+
type: :development
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: *id010
|
125
|
+
description: |-
|
126
|
+
MurmuringSpider is a concise Twitter crawler.
|
127
|
+
|
128
|
+
When we write a data-mining / text-mining application based on twitter timeline, we have to collect and store tweets first.
|
129
|
+
|
130
|
+
I am irritated with writing such crawler repeatedly, so I wrote this.
|
131
|
+
|
132
|
+
What you have to do is only to add query and to run them periodically.
|
133
|
+
|
134
|
+
Thanks to consistent Twitter API and twitter gem (http://twitter.rubyforge.org/), it is quite easy to track various types of timelines (such as user_timeline, home_timeline, search...)
|
135
|
+
email:
|
136
|
+
- tomykaira@gmail.com
|
137
|
+
executables: []
|
138
|
+
|
139
|
+
extensions: []
|
140
|
+
|
141
|
+
extra_rdoc_files: []
|
142
|
+
|
143
|
+
files:
|
144
|
+
- .gitignore
|
145
|
+
- .rspec
|
146
|
+
- .travis.yml
|
147
|
+
- Gemfile
|
148
|
+
- Guardfile
|
149
|
+
- LICENSE
|
150
|
+
- README.md
|
151
|
+
- Rakefile
|
152
|
+
- lib/murmuring_spider.rb
|
153
|
+
- lib/murmuring_spider/operation.rb
|
154
|
+
- lib/murmuring_spider/status.rb
|
155
|
+
- lib/murmuring_spider/version.rb
|
156
|
+
- murmuring_spider.gemspec
|
157
|
+
- spec/murmuring_spider/operation_spec.rb
|
158
|
+
- spec/murmuring_spider/status_spec.rb
|
159
|
+
- spec/spec_helper.rb
|
160
|
+
- spec/twitter_search_status.dump
|
161
|
+
- spec/twitter_status.dump
|
162
|
+
homepage: https://github.com/tomykaira/murmuring_spider
|
163
|
+
licenses: []
|
164
|
+
|
165
|
+
post_install_message:
|
166
|
+
rdoc_options: []
|
167
|
+
|
168
|
+
require_paths:
|
169
|
+
- lib
|
170
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
171
|
+
none: false
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
hash: -814915267
|
176
|
+
segments:
|
177
|
+
- 0
|
178
|
+
version: "0"
|
179
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
180
|
+
none: false
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
hash: -814915267
|
185
|
+
segments:
|
186
|
+
- 0
|
187
|
+
version: "0"
|
188
|
+
requirements: []
|
189
|
+
|
190
|
+
rubyforge_project:
|
191
|
+
rubygems_version: 1.8.17
|
192
|
+
signing_key:
|
193
|
+
specification_version: 3
|
194
|
+
summary: MurmuringSpider is a concise Twitter crawler with DataMapper.
|
195
|
+
test_files:
|
196
|
+
- spec/murmuring_spider/operation_spec.rb
|
197
|
+
- spec/murmuring_spider/status_spec.rb
|
198
|
+
- spec/spec_helper.rb
|
199
|
+
- spec/twitter_search_status.dump
|
200
|
+
- spec/twitter_status.dump
|