├── MANIFEST.in
├── .gitignore
├── setup.py
├── UNLICENSE
├── EXAMPLES.rst
├── src
└── jsonpipe
│ ├── sh.py
│ ├── __init__.py
│ └── pipe.py
├── README.rst
├── distribute_setup.py
└── example.json
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include distribute_setup.py
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.egg-info
2 | *.pyc
3 | *.pyo
4 | .DS_Store
5 | build
6 | dist
7 | MANIFEST
8 | test/example/*.sqlite3
9 | doc/.build
10 | distribute-*.egg
11 | distribute-*.tar.gz
12 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | from distribute_setup import use_setuptools
5 | use_setuptools()
6 |
7 | import re
8 | from setuptools import setup, find_packages
9 | import os.path as p
10 |
11 |
12 | def get_version():
13 | source = open(p.join(p.dirname(p.abspath(__file__)),
14 | 'src', 'jsonpipe', '__init__.py')).read()
15 | match = re.search(r'__version__\s*=\s*[\'"]([^\'"]+)[\'"]', source)
16 | if not match:
17 | raise RuntimeError("Couldn't find the version string in src/jsonpipe/__init__.py")
18 | return match.group(1)
19 |
20 |
21 | setup(
22 | name='jsonpipe',
23 | version=get_version(),
24 | description="Convert JSON to a UNIX-friendly line-based format.",
25 | author='Zachary Voase',
26 | author_email='z@dvxhouse.com',
27 | url='http://github.com/dvxhouse/jsonpipe',
28 | package_dir={'': 'src'},
29 | packages=find_packages(where='src'),
30 | entry_points={'console_scripts': ['jsonpipe = jsonpipe:main',
31 | 'jsonunpipe = jsonpipe:main_unpipe']},
32 | install_requires=['simplejson>=2.1.3', 'argparse>=1.1', 'calabash==0.0.3'],
33 | test_suite='jsonpipe._get_tests',
34 | )
35 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
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 NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
`` is worth a thousand words. For simple JSON values::
28 |
29 | $ echo '"Hello, World!"' | jsonpipe
30 | / "Hello, World!"
31 | $ echo 123 | jsonpipe
32 | / 123
33 | $ echo 0.25 | jsonpipe
34 | / 0.25
35 | $ echo null | jsonpipe
36 | / null
37 | $ echo true | jsonpipe
38 | / true
39 | $ echo false | jsonpipe
40 | / false
41 |
42 | The 'root' of the object tree is represented by a single ``/`` character, and
43 | for simple values it doesn't get any more complex than the above. Note that a
44 | single tab character separates the path on the left from the literal value on
45 | the right.
46 |
47 | Composite data structures use a hierarchical syntax, where individual
48 | keys/indices are children of the path to the containing object::
49 |
50 | $ echo '{"a": 1, "b": 2}' | jsonpipe
51 | / {}
52 | /a 1
53 | /b 2
54 | $ echo '["foo", "bar", "baz"]' | jsonpipe
55 | / []
56 | /0 "foo"
57 | /1 "bar"
58 | /2 "baz"
59 |
60 | For an object or array, the right-hand column indicates the datatype, and will
61 | be either ``{}`` (object) or ``[]`` (array). For objects, the order of the keys
62 | is preserved in the output.
63 |
64 | The path syntax allows arbitrarily complex data structures::
65 |
66 | $ echo '[{"a": [{"b": {"c": ["foo"]}}]}]' | jsonpipe
67 | / []
68 | /0 {}
69 | /0/a []
70 | /0/a/0 {}
71 | /0/a/0/b {}
72 | /0/a/0/b/c []
73 | /0/a/0/b/c/0 "foo"
74 |
75 |
76 | Caveat: Path Separators
77 | =======================
78 |
79 | Because the path components are separated by ``/`` characters, an object key
80 | like ``"abc/def"`` would result in ambiguous output. jsonpipe will throw
81 | an error if this occurs in your input, so that you can recognize and handle the
82 | issue. To mitigate the problem, you can choose a different path separator::
83 |
84 | $ echo '{"abc/def": 123}' | jsonpipe -s '☃'
85 | ☃ {}
86 | ☃abc/def 123
87 |
88 | The Unicode snowman is chosen here because it's unlikely to occur as part of
89 | the key in most JSON objects, but any character or string (e.g. ``:``, ``::``,
90 | ``~``) will do.
91 |
92 |
93 | jsonunpipe
94 | ==========
95 |
96 | Another useful part of the library is ``jsonunpipe``, which turns jsonpipe
97 | output back into JSON proper::
98 |
99 | $ echo '{"a": 1, "b": 2}' | jsonpipe | jsonunpipe
100 | {"a": 1, "b": 2}
101 |
102 | jsonunpipe also supports incomplete information (such as you might get from
103 | grep), and will assume all previously-undeclared parts of a path to be JSON
104 | objects::
105 |
106 | $ echo "/a/b/c 123" | jsonunpipe
107 | {"a": {"b": {"c": 123}}}
108 |
109 |
110 | Python API
111 | ==========
112 |
113 | Since jsonpipe is written in Python, you can import it and use it without
114 | having to spawn another process::
115 |
116 | >>> from jsonpipe import jsonpipe
117 | >>> for line in jsonpipe({"a": 1, "b": 2}):
118 | ... print line
119 | / {}
120 | /a 1
121 | /b 2
122 |
123 | Note that the ``jsonpipe()`` generator function takes a Python object, not a
124 | JSON string, so the order of dictionary keys may be slightly unpredictable in
125 | the output. You can use ``simplejson.OrderedDict`` to get a fixed ordering::
126 |
127 | >>> from simplejson import OrderedDict
128 | >>> obj = OrderedDict([('a', 1), ('b', 2), ('c', 3)])
129 | >>> obj
130 | OrderedDict([('a', 1), ('b', 2), ('c', 3)])
131 | >>> for line in jsonpipe(obj):
132 | ... print line
133 | / {}
134 | /a 1
135 | /b 2
136 | /c 3
137 |
138 | A more general hint: if you need to parse JSON but maintain ordering for object
139 | keys, use the ``object_pairs_hook`` option on ``simplejson.load(s)``::
140 |
141 | >>> import simplejson
142 | >>> simplejson.loads('{"a": 1, "b": 2, "c": 3}',
143 | ... object_pairs_hook=simplejson.OrderedDict)
144 | OrderedDict([('a', 1), ('b', 2), ('c', 3)])
145 |
146 | Of course, a Python implementation of jsonunpipe also exists::
147 |
148 | >>> from jsonpipe import jsonunpipe
149 | >>> jsonunpipe(['/\t{}', '/a\t123'])
150 | {'a': 123}
151 |
152 | You can pass a ``decoder`` parameter, as in the following example, where the
153 | JSON object returned uses an ordered dictionary::
154 |
155 | >>> jsonunpipe(['/\t{}', '/a\t123', '/b\t456'],
156 | ... decoder=simplejson.JSONDecoder(
157 | ... object_pairs_hook=simplejson.OrderedDict))
158 | OrderedDict([('a', 123), ('b', 456)])
159 |
160 | Installation
161 | ============
162 |
163 | **jsonpipe** is written in Python, so is best installed using ``pip``::
164 |
165 | pip install jsonpipe
166 |
167 | Note that it requires Python v2.5 or later (simplejson only supports 2.5+).
168 |
169 |
170 | (Un)license
171 | ===========
172 |
173 | This is free and unencumbered software released into the public domain.
174 |
175 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
176 | software, either in source code form or as a compiled binary, for any purpose,
177 | commercial or non-commercial, and by any means.
178 |
179 | In jurisdictions that recognize copyright laws, the author or authors of this
180 | software dedicate any and all copyright interest in the software to the public
181 | domain. We make this dedication for the benefit of the public at large and to
182 | the detriment of our heirs and successors. We intend this dedication to be an
183 | overt act of relinquishment in perpetuity of all present and future rights to
184 | this software under copyright law.
185 |
186 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
189 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
190 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
191 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
192 |
193 | For more information, please refer to
194 |
--------------------------------------------------------------------------------
/src/jsonpipe/pipe.py:
--------------------------------------------------------------------------------
1 | import simplejson
2 |
3 |
4 | __all__ = ['jsonpipe', 'jsonunpipe']
5 |
6 |
7 | def jsonpipe(obj, pathsep='/', path=()):
8 |
9 | r"""
10 | Generate a jsonpipe stream for the provided (parsed) JSON object.
11 |
12 | This generator will yield output as UTF-8-encoded bytestrings line-by-line.
13 | These lines will *not* be terminated with line ending characters.
14 |
15 | The provided object can be as complex as you like, but it must consist only
16 | of:
17 |
18 | * Dictionaries (or subclasses of `dict`)
19 | * Lists or tuples (or subclasses of the built-in types)
20 | * Unicode Strings (`unicode`, utf-8 encoded `str`)
21 | * Numbers (`int`, `long`, `float`)
22 | * Booleans (`True`, `False`)
23 | * `None`
24 |
25 | Please note that, where applicable, *all* input must use either native
26 | Unicode strings or UTF-8-encoded bytestrings, and all output will be UTF-8
27 | encoded.
28 |
29 | The simplest case is outputting JSON values (strings, numbers, booleans and
30 | nulls):
31 |
32 | >>> def pipe(obj): # Shim for easier demonstration.
33 | ... print '\n'.join(jsonpipe(obj))
34 | >>> pipe(u"Hello, World!")
35 | / "Hello, World!"
36 | >>> pipe(123)
37 | / 123
38 | >>> pipe(0.25)
39 | / 0.25
40 | >>> pipe(None)
41 | / null
42 | >>> pipe(True)
43 | / true
44 | >>> pipe(False)
45 | / false
46 |
47 | jsonpipe always uses '/' to represent the top-level object. Dictionaries
48 | are displayed as ``{}``, with each key shown as a sub-path:
49 |
50 | >>> pipe({"a": 1, "b": 2})
51 | / {}
52 | /a 1
53 | /b 2
54 |
55 | Lists are treated in much the same way, only the integer indices are used
56 | as the keys, and the top-level list object is shown as ``[]``:
57 |
58 | >>> pipe([1, "foo", 2, "bar"])
59 | / []
60 | /0 1
61 | /1 "foo"
62 | /2 2
63 | /3 "bar"
64 |
65 | Finally, the practical benefit of using hierarchical paths is that the
66 | syntax supports nesting of arbitrarily complex constructs:
67 |
68 | >>> pipe([{"a": [{"b": {"c": ["foo"]}}]}])
69 | / []
70 | /0 {}
71 | /0/a []
72 | /0/a/0 {}
73 | /0/a/0/b {}
74 | /0/a/0/b/c []
75 | /0/a/0/b/c/0 "foo"
76 |
77 | Because the sole separator of path components is a ``/`` character by
78 | default, keys containing this character would result in ambiguous output.
79 | Therefore, if you try to write a dictionary with a key containing the path
80 | separator, :func:`jsonpipe` will raise a :exc:`ValueError`:
81 |
82 | >>> pipe({"a/b": 1})
83 | Traceback (most recent call last):
84 | ...
85 | ValueError: Path separator '/' present in key 'a/b'
86 |
87 | In more complex examples, some output may be written before the exception
88 | is raised. To mitigate this problem, you can provide a custom path
89 | separator:
90 |
91 | >>> print '\n'.join(jsonpipe({"a/b": 1}, pathsep=':'))
92 | : {}
93 | :a/b 1
94 |
95 | The path separator should be a bytestring, and you are advised to use
96 | something you are almost certain will not be present in your dictionary
97 | keys.
98 | """
99 |
100 | def output(string):
101 | return pathsep + pathsep.join(path) + "\t" + string
102 |
103 | if is_value(obj):
104 | yield output(simplejson.dumps(obj))
105 | raise StopIteration # Stop the generator immediately.
106 | elif isinstance(obj, dict):
107 | yield output('{}')
108 | iterator = obj.iteritems()
109 | elif hasattr(obj, '__iter__'):
110 | yield output('[]')
111 | iterator = enumerate(obj)
112 | else:
113 | raise TypeError("Unsupported type for jsonpipe output: %r" %
114 | type(obj))
115 |
116 | for key, value in iterator:
117 | # Check the key for sanity.
118 | key = to_str(key)
119 | if pathsep in key:
120 | # In almost any case this is not what the user wants; having
121 | # the path separator in the key would create ambiguous output
122 | # so we should fail loudly and as quickly as possible.
123 | raise ValueError("Path separator %r present in key %r" %
124 | (pathsep, key))
125 |
126 | for line in jsonpipe(value, pathsep=pathsep, path=path + (key,)):
127 | yield line
128 |
129 |
130 | def jsonunpipe(lines, pathsep='/', discard='',
131 | decoder=simplejson._default_decoder):
132 |
133 | r"""
134 | Parse a stream of jsonpipe output back into a JSON object.
135 |
136 | >>> def unpipe(s): # Shim for easier demonstration.
137 | ... print repr(jsonunpipe(s.strip().splitlines()))
138 |
139 | Works as expected for simple JSON values::
140 |
141 | >>> unpipe('/\t"abc"')
142 | 'abc'
143 | >>> unpipe('/\t123')
144 | 123
145 | >>> unpipe('/\t0.25')
146 | 0.25
147 | >>> unpipe('/\tnull')
148 | None
149 | >>> unpipe('/\ttrue')
150 | True
151 | >>> unpipe('/\tfalse')
152 | False
153 |
154 | And likewise for more complex objects::
155 |
156 | >>> unpipe('''
157 | ... /\t{}
158 | ... /a\t1
159 | ... /b\t2''')
160 | {'a': 1, 'b': 2}
161 |
162 | >>> unpipe('''
163 | ... /\t[]
164 | ... /0\t{}
165 | ... /0/a\t[]
166 | ... /0/a/0\t{}
167 | ... /0/a/0/b\t{}
168 | ... /0/a/0/b/c\t[]
169 | ... /0/a/0/b/c/0\t"foo"''')
170 | [{'a': [{'b': {'c': ['foo']}}]}]
171 |
172 | Any level in the path left unspecified will be assumed to be an object::
173 |
174 | >>> unpipe('''
175 | ... /a/b/c\t123''')
176 | {'a': {'b': {'c': 123}}}
177 | """
178 |
179 | def parse_line(line):
180 | path, json = line.rstrip().split('\t')
181 | return path.split(pathsep)[1:], decoder.decode(json)
182 |
183 | def getitem(obj, index):
184 | if isinstance(obj, (list, tuple)):
185 | return obj[int(index)]
186 | # All non-existent keys are assumed to be an object.
187 | if index not in obj:
188 | obj[index] = decoder.decode('{}')
189 | return obj[index]
190 |
191 | def setitem(obj, index, value):
192 | if isinstance(obj, list):
193 | index = int(index)
194 | if len(obj) == index:
195 | obj.append(value)
196 | return
197 | obj[index] = value
198 |
199 | output = decoder.decode('{}')
200 | for line in lines:
201 | path, obj = parse_line(line)
202 | if path == ['']:
203 | output = obj
204 | continue
205 | setitem(reduce(getitem, path[:-1], output), path[-1], obj)
206 | return output
207 |
208 |
209 | def to_str(obj):
210 |
211 | ur"""
212 | Coerce an object to a bytestring, utf-8-encoding if necessary.
213 |
214 | >>> to_str("Hello World")
215 | 'Hello World'
216 | >>> to_str(u"H\xe9llo")
217 | 'H\xc3\xa9llo'
218 | """
219 |
220 | if isinstance(obj, unicode):
221 | return obj.encode('utf-8')
222 | elif hasattr(obj, '__unicode__'):
223 | return unicode(obj).encode('utf-8')
224 | return str(obj)
225 |
226 |
227 | def is_value(obj):
228 |
229 | """
230 | Determine whether an object is a simple JSON value.
231 |
232 | The phrase 'simple JSON value' here means one of:
233 |
234 | * String (Unicode or UTF-8-encoded bytestring)
235 | * Number (integer or floating-point)
236 | * Boolean
237 | * `None`
238 | """
239 |
240 | return isinstance(obj, (str, unicode, int, long, float, bool, type(None)))
241 |
--------------------------------------------------------------------------------
/distribute_setup.py:
--------------------------------------------------------------------------------
1 | #!python
2 | """Bootstrap distribute installation
3 |
4 | If you want to use setuptools in your package's setup.py, just include this
5 | file in the same directory with it, and add this to the top of your setup.py::
6 |
7 | from distribute_setup import use_setuptools
8 | use_setuptools()
9 |
10 | If you want to require a specific version of setuptools, set a download
11 | mirror, or use an alternate download directory, you can do so by supplying
12 | the appropriate options to ``use_setuptools()``.
13 |
14 | This file can also be run as a script to install or upgrade setuptools.
15 | """
16 | import os
17 | import sys
18 | import time
19 | import fnmatch
20 | import tempfile
21 | import tarfile
22 | from distutils import log
23 |
24 | try:
25 | from site import USER_SITE
26 | except ImportError:
27 | USER_SITE = None
28 |
29 | try:
30 | import subprocess
31 |
32 | def _python_cmd(*args):
33 | args = (sys.executable,) + args
34 | return subprocess.call(args) == 0
35 |
36 | except ImportError:
37 | # will be used for python 2.3
38 | def _python_cmd(*args):
39 | args = (sys.executable,) + args
40 | # quoting arguments if windows
41 | if sys.platform == 'win32':
42 | def quote(arg):
43 | if ' ' in arg:
44 | return '"%s"' % arg
45 | return arg
46 | args = [quote(arg) for arg in args]
47 | return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
48 |
49 | DEFAULT_VERSION = "0.6.14"
50 | DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
51 | SETUPTOOLS_FAKED_VERSION = "0.6c11"
52 |
53 | SETUPTOOLS_PKG_INFO = """\
54 | Metadata-Version: 1.0
55 | Name: setuptools
56 | Version: %s
57 | Summary: xxxx
58 | Home-page: xxx
59 | Author: xxx
60 | Author-email: xxx
61 | License: xxx
62 | Description: xxx
63 | """ % SETUPTOOLS_FAKED_VERSION
64 |
65 |
66 | def _install(tarball):
67 | # extracting the tarball
68 | tmpdir = tempfile.mkdtemp()
69 | log.warn('Extracting in %s', tmpdir)
70 | old_wd = os.getcwd()
71 | try:
72 | os.chdir(tmpdir)
73 | tar = tarfile.open(tarball)
74 | _extractall(tar)
75 | tar.close()
76 |
77 | # going in the directory
78 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
79 | os.chdir(subdir)
80 | log.warn('Now working in %s', subdir)
81 |
82 | # installing
83 | log.warn('Installing Distribute')
84 | if not _python_cmd('setup.py', 'install'):
85 | log.warn('Something went wrong during the installation.')
86 | log.warn('See the error message above.')
87 | finally:
88 | os.chdir(old_wd)
89 |
90 |
91 | def _build_egg(egg, tarball, to_dir):
92 | # extracting the tarball
93 | tmpdir = tempfile.mkdtemp()
94 | log.warn('Extracting in %s', tmpdir)
95 | old_wd = os.getcwd()
96 | try:
97 | os.chdir(tmpdir)
98 | tar = tarfile.open(tarball)
99 | _extractall(tar)
100 | tar.close()
101 |
102 | # going in the directory
103 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
104 | os.chdir(subdir)
105 | log.warn('Now working in %s', subdir)
106 |
107 | # building an egg
108 | log.warn('Building a Distribute egg in %s', to_dir)
109 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
110 |
111 | finally:
112 | os.chdir(old_wd)
113 | # returning the result
114 | log.warn(egg)
115 | if not os.path.exists(egg):
116 | raise IOError('Could not build the egg.')
117 |
118 |
119 | def _do_download(version, download_base, to_dir, download_delay):
120 | egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
121 | % (version, sys.version_info[0], sys.version_info[1]))
122 | if not os.path.exists(egg):
123 | tarball = download_setuptools(version, download_base,
124 | to_dir, download_delay)
125 | _build_egg(egg, tarball, to_dir)
126 | sys.path.insert(0, egg)
127 | import setuptools
128 | setuptools.bootstrap_install_from = egg
129 |
130 |
131 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
132 | to_dir=os.curdir, download_delay=15, no_fake=True):
133 | # making sure we use the absolute path
134 | to_dir = os.path.abspath(to_dir)
135 | was_imported = 'pkg_resources' in sys.modules or \
136 | 'setuptools' in sys.modules
137 | try:
138 | try:
139 | import pkg_resources
140 | if not hasattr(pkg_resources, '_distribute'):
141 | if not no_fake:
142 | _fake_setuptools()
143 | raise ImportError
144 | except ImportError:
145 | return _do_download(version, download_base, to_dir, download_delay)
146 | try:
147 | pkg_resources.require("distribute>="+version)
148 | return
149 | except pkg_resources.VersionConflict:
150 | e = sys.exc_info()[1]
151 | if was_imported:
152 | sys.stderr.write(
153 | "The required version of distribute (>=%s) is not available,\n"
154 | "and can't be installed while this script is running. Please\n"
155 | "install a more recent version first, using\n"
156 | "'easy_install -U distribute'."
157 | "\n\n(Currently using %r)\n" % (version, e.args[0]))
158 | sys.exit(2)
159 | else:
160 | del pkg_resources, sys.modules['pkg_resources'] # reload ok
161 | return _do_download(version, download_base, to_dir,
162 | download_delay)
163 | except pkg_resources.DistributionNotFound:
164 | return _do_download(version, download_base, to_dir,
165 | download_delay)
166 | finally:
167 | if not no_fake:
168 | _create_fake_setuptools_pkg_info(to_dir)
169 |
170 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
171 | to_dir=os.curdir, delay=15):
172 | """Download distribute from a specified location and return its filename
173 |
174 | `version` should be a valid distribute version number that is available
175 | as an egg for download under the `download_base` URL (which should end
176 | with a '/'). `to_dir` is the directory where the egg will be downloaded.
177 | `delay` is the number of seconds to pause before an actual download
178 | attempt.
179 | """
180 | # making sure we use the absolute path
181 | to_dir = os.path.abspath(to_dir)
182 | try:
183 | from urllib.request import urlopen
184 | except ImportError:
185 | from urllib2 import urlopen
186 | tgz_name = "distribute-%s.tar.gz" % version
187 | url = download_base + tgz_name
188 | saveto = os.path.join(to_dir, tgz_name)
189 | src = dst = None
190 | if not os.path.exists(saveto): # Avoid repeated downloads
191 | try:
192 | log.warn("Downloading %s", url)
193 | src = urlopen(url)
194 | # Read/write all in one block, so we don't create a corrupt file
195 | # if the download is interrupted.
196 | data = src.read()
197 | dst = open(saveto, "wb")
198 | dst.write(data)
199 | finally:
200 | if src:
201 | src.close()
202 | if dst:
203 | dst.close()
204 | return os.path.realpath(saveto)
205 |
206 | def _no_sandbox(function):
207 | def __no_sandbox(*args, **kw):
208 | try:
209 | from setuptools.sandbox import DirectorySandbox
210 | if not hasattr(DirectorySandbox, '_old'):
211 | def violation(*args):
212 | pass
213 | DirectorySandbox._old = DirectorySandbox._violation
214 | DirectorySandbox._violation = violation
215 | patched = True
216 | else:
217 | patched = False
218 | except ImportError:
219 | patched = False
220 |
221 | try:
222 | return function(*args, **kw)
223 | finally:
224 | if patched:
225 | DirectorySandbox._violation = DirectorySandbox._old
226 | del DirectorySandbox._old
227 |
228 | return __no_sandbox
229 |
230 | def _patch_file(path, content):
231 | """Will backup the file then patch it"""
232 | existing_content = open(path).read()
233 | if existing_content == content:
234 | # already patched
235 | log.warn('Already patched.')
236 | return False
237 | log.warn('Patching...')
238 | _rename_path(path)
239 | f = open(path, 'w')
240 | try:
241 | f.write(content)
242 | finally:
243 | f.close()
244 | return True
245 |
246 | _patch_file = _no_sandbox(_patch_file)
247 |
248 | def _same_content(path, content):
249 | return open(path).read() == content
250 |
251 | def _rename_path(path):
252 | new_name = path + '.OLD.%s' % time.time()
253 | log.warn('Renaming %s into %s', path, new_name)
254 | os.rename(path, new_name)
255 | return new_name
256 |
257 | def _remove_flat_installation(placeholder):
258 | if not os.path.isdir(placeholder):
259 | log.warn('Unkown installation at %s', placeholder)
260 | return False
261 | found = False
262 | for file in os.listdir(placeholder):
263 | if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
264 | found = True
265 | break
266 | if not found:
267 | log.warn('Could not locate setuptools*.egg-info')
268 | return
269 |
270 | log.warn('Removing elements out of the way...')
271 | pkg_info = os.path.join(placeholder, file)
272 | if os.path.isdir(pkg_info):
273 | patched = _patch_egg_dir(pkg_info)
274 | else:
275 | patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
276 |
277 | if not patched:
278 | log.warn('%s already patched.', pkg_info)
279 | return False
280 | # now let's move the files out of the way
281 | for element in ('setuptools', 'pkg_resources.py', 'site.py'):
282 | element = os.path.join(placeholder, element)
283 | if os.path.exists(element):
284 | _rename_path(element)
285 | else:
286 | log.warn('Could not find the %s element of the '
287 | 'Setuptools distribution', element)
288 | return True
289 |
290 | _remove_flat_installation = _no_sandbox(_remove_flat_installation)
291 |
292 | def _after_install(dist):
293 | log.warn('After install bootstrap.')
294 | placeholder = dist.get_command_obj('install').install_purelib
295 | _create_fake_setuptools_pkg_info(placeholder)
296 |
297 | def _create_fake_setuptools_pkg_info(placeholder):
298 | if not placeholder or not os.path.exists(placeholder):
299 | log.warn('Could not find the install location')
300 | return
301 | pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
302 | setuptools_file = 'setuptools-%s-py%s.egg-info' % \
303 | (SETUPTOOLS_FAKED_VERSION, pyver)
304 | pkg_info = os.path.join(placeholder, setuptools_file)
305 | if os.path.exists(pkg_info):
306 | log.warn('%s already exists', pkg_info)
307 | return
308 |
309 | log.warn('Creating %s', pkg_info)
310 | f = open(pkg_info, 'w')
311 | try:
312 | f.write(SETUPTOOLS_PKG_INFO)
313 | finally:
314 | f.close()
315 |
316 | pth_file = os.path.join(placeholder, 'setuptools.pth')
317 | log.warn('Creating %s', pth_file)
318 | f = open(pth_file, 'w')
319 | try:
320 | f.write(os.path.join(os.curdir, setuptools_file))
321 | finally:
322 | f.close()
323 |
324 | _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
325 |
326 | def _patch_egg_dir(path):
327 | # let's check if it's already patched
328 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
329 | if os.path.exists(pkg_info):
330 | if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
331 | log.warn('%s already patched.', pkg_info)
332 | return False
333 | _rename_path(path)
334 | os.mkdir(path)
335 | os.mkdir(os.path.join(path, 'EGG-INFO'))
336 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
337 | f = open(pkg_info, 'w')
338 | try:
339 | f.write(SETUPTOOLS_PKG_INFO)
340 | finally:
341 | f.close()
342 | return True
343 |
344 | _patch_egg_dir = _no_sandbox(_patch_egg_dir)
345 |
346 | def _before_install():
347 | log.warn('Before install bootstrap.')
348 | _fake_setuptools()
349 |
350 |
351 | def _under_prefix(location):
352 | if 'install' not in sys.argv:
353 | return True
354 | args = sys.argv[sys.argv.index('install')+1:]
355 | for index, arg in enumerate(args):
356 | for option in ('--root', '--prefix'):
357 | if arg.startswith('%s=' % option):
358 | top_dir = arg.split('root=')[-1]
359 | return location.startswith(top_dir)
360 | elif arg == option:
361 | if len(args) > index:
362 | top_dir = args[index+1]
363 | return location.startswith(top_dir)
364 | if arg == '--user' and USER_SITE is not None:
365 | return location.startswith(USER_SITE)
366 | return True
367 |
368 |
369 | def _fake_setuptools():
370 | log.warn('Scanning installed packages')
371 | try:
372 | import pkg_resources
373 | except ImportError:
374 | # we're cool
375 | log.warn('Setuptools or Distribute does not seem to be installed.')
376 | return
377 | ws = pkg_resources.working_set
378 | try:
379 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
380 | replacement=False))
381 | except TypeError:
382 | # old distribute API
383 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
384 |
385 | if setuptools_dist is None:
386 | log.warn('No setuptools distribution found')
387 | return
388 | # detecting if it was already faked
389 | setuptools_location = setuptools_dist.location
390 | log.warn('Setuptools installation detected at %s', setuptools_location)
391 |
392 | # if --root or --preix was provided, and if
393 | # setuptools is not located in them, we don't patch it
394 | if not _under_prefix(setuptools_location):
395 | log.warn('Not patching, --root or --prefix is installing Distribute'
396 | ' in another location')
397 | return
398 |
399 | # let's see if its an egg
400 | if not setuptools_location.endswith('.egg'):
401 | log.warn('Non-egg installation')
402 | res = _remove_flat_installation(setuptools_location)
403 | if not res:
404 | return
405 | else:
406 | log.warn('Egg installation')
407 | pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
408 | if (os.path.exists(pkg_info) and
409 | _same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
410 | log.warn('Already patched.')
411 | return
412 | log.warn('Patching...')
413 | # let's create a fake egg replacing setuptools one
414 | res = _patch_egg_dir(setuptools_location)
415 | if not res:
416 | return
417 | log.warn('Patched done.')
418 | _relaunch()
419 |
420 |
421 | def _relaunch():
422 | log.warn('Relaunching...')
423 | # we have to relaunch the process
424 | # pip marker to avoid a relaunch bug
425 | if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
426 | sys.argv[0] = 'setup.py'
427 | args = [sys.executable] + sys.argv
428 | sys.exit(subprocess.call(args))
429 |
430 |
431 | def _extractall(self, path=".", members=None):
432 | """Extract all members from the archive to the current working
433 | directory and set owner, modification time and permissions on
434 | directories afterwards. `path' specifies a different directory
435 | to extract to. `members' is optional and must be a subset of the
436 | list returned by getmembers().
437 | """
438 | import copy
439 | import operator
440 | from tarfile import ExtractError
441 | directories = []
442 |
443 | if members is None:
444 | members = self
445 |
446 | for tarinfo in members:
447 | if tarinfo.isdir():
448 | # Extract directories with a safe mode.
449 | directories.append(tarinfo)
450 | tarinfo = copy.copy(tarinfo)
451 | tarinfo.mode = 448 # decimal for oct 0700
452 | self.extract(tarinfo, path)
453 |
454 | # Reverse sort directories.
455 | if sys.version_info < (2, 4):
456 | def sorter(dir1, dir2):
457 | return cmp(dir1.name, dir2.name)
458 | directories.sort(sorter)
459 | directories.reverse()
460 | else:
461 | directories.sort(key=operator.attrgetter('name'), reverse=True)
462 |
463 | # Set correct owner, mtime and filemode on directories.
464 | for tarinfo in directories:
465 | dirpath = os.path.join(path, tarinfo.name)
466 | try:
467 | self.chown(tarinfo, dirpath)
468 | self.utime(tarinfo, dirpath)
469 | self.chmod(tarinfo, dirpath)
470 | except ExtractError:
471 | e = sys.exc_info()[1]
472 | if self.errorlevel > 1:
473 | raise
474 | else:
475 | self._dbg(1, "tarfile: %s" % e)
476 |
477 |
478 | def main(argv, version=DEFAULT_VERSION):
479 | """Install or upgrade setuptools and EasyInstall"""
480 | tarball = download_setuptools()
481 | _install(tarball)
482 |
483 |
484 | if __name__ == '__main__':
485 | main(sys.argv[1:])
486 |
--------------------------------------------------------------------------------
/example.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "contributors": null,
4 | "coordinates": null,
5 | "created_at": "Fri Apr 01 22:05:53 +0000 2011",
6 | "favorited": false,
7 | "geo": null,
8 | "id": 53941178919424000,
9 | "id_str": "53941178919424000",
10 | "in_reply_to_screen_name": null,
11 | "in_reply_to_status_id": null,
12 | "in_reply_to_status_id_str": null,
13 | "in_reply_to_user_id": null,
14 | "in_reply_to_user_id_str": null,
15 | "place": null,
16 | "retweet_count": 0,
17 | "retweeted": false,
18 | "source": "web",
19 | "text": "\u00e9 muito tarde pra voltar atras, pra te dar o que eu nao te dei...",
20 | "truncated": false,
21 | "user": {
22 | "contributors_enabled": false,
23 | "created_at": "Fri Jan 08 22:58:29 +0000 2010",
24 | "default_profile": false,
25 | "default_profile_image": false,
26 | "description": "Viver bem \u00e9 a melhor vingan\u00e7a!",
27 | "favourites_count": 0,
28 | "follow_request_sent": null,
29 | "followers_count": 109,
30 | "following": null,
31 | "friends_count": 103,
32 | "geo_enabled": false,
33 | "id": 103110250,
34 | "id_str": "103110250",
35 | "is_translator": false,
36 | "lang": "en",
37 | "listed_count": 3,
38 | "location": "Gravata\u00ed, RS",
39 | "name": "Milene Magnus",
40 | "notifications": null,
41 | "profile_background_color": "000000",
42 | "profile_background_image_url": "http://a2.twimg.com/profile_background_images/65470560/OgAAAOjJ_MppsYQZ5ZIFPzLf7QjT-5wgOmTemhq9qvahqFT9MRQm4559-uuhom6sKdKrrzRp-kCwdNM7xwZrzrB0OBUAm1T1UHgn-ESU4-sjIlzODnV77RH7AD4K.jpg",
43 | "profile_background_tile": true,
44 | "profile_image_url": "http://a3.twimg.com/profile_images/1284102622/OgAAAOl0wS1BtFRhzD_kOjFEXmCvi3AT66iEZDjGyDv93mMy8LsW29pEfoiwLVjd1iH1dLmNlYlXgkQRNvHQuWVCWisAm1T1UJ46Oi3Md00Fk5-wj9Qgqst-VXja_normal.jpg",
45 | "profile_link_color": "340573",
46 | "profile_sidebar_border_color": "d91a77",
47 | "profile_sidebar_fill_color": "f24389",
48 | "profile_text_color": "000000",
49 | "profile_use_background_image": false,
50 | "protected": false,
51 | "screen_name": "milenemagnus_",
52 | "show_all_inline_media": false,
53 | "statuses_count": 4478,
54 | "time_zone": "Greenland",
55 | "url": null,
56 | "utc_offset": -10800,
57 | "verified": false
58 | }
59 | },
60 | {
61 | "contributors": null,
62 | "coordinates": null,
63 | "created_at": "Fri Apr 01 22:05:53 +0000 2011",
64 | "favorited": false,
65 | "geo": null,
66 | "id": 53941178504192000,
67 | "id_str": "53941178504192000",
68 | "in_reply_to_screen_name": null,
69 | "in_reply_to_status_id": null,
70 | "in_reply_to_status_id_str": null,
71 | "in_reply_to_user_id": null,
72 | "in_reply_to_user_id_str": null,
73 | "place": null,
74 | "retweet_count": 0,
75 | "retweeted": false,
76 | "source": "web",
77 | "text": "De los errores se aprende, no se lamenta.",
78 | "truncated": false,
79 | "user": {
80 | "contributors_enabled": false,
81 | "created_at": "Wed Nov 10 05:07:00 +0000 2010",
82 | "default_profile": false,
83 | "default_profile_image": false,
84 | "description": "T'estimo Bar\u00e7a. Viajar. Conocer el mundo, disfrutar cada segundo de la vida. English too",
85 | "favourites_count": 0,
86 | "follow_request_sent": null,
87 | "followers_count": 62,
88 | "following": null,
89 | "friends_count": 623,
90 | "geo_enabled": false,
91 | "id": 213948055,
92 | "id_str": "213948055",
93 | "is_translator": false,
94 | "lang": "en",
95 | "listed_count": 0,
96 | "location": "",
97 | "name": "Andres",
98 | "notifications": null,
99 | "profile_background_color": "022330",
100 | "profile_background_image_url": "http://a2.twimg.com/profile_background_images/197871070/andrespirate2.jpg",
101 | "profile_background_tile": false,
102 | "profile_image_url": "http://a1.twimg.com/profile_images/1164443389/andrespirate2_normal.jpg",
103 | "profile_link_color": "0084B4",
104 | "profile_sidebar_border_color": "a8c7f7",
105 | "profile_sidebar_fill_color": "C0DFEC",
106 | "profile_text_color": "333333",
107 | "profile_use_background_image": true,
108 | "protected": false,
109 | "screen_name": "andrewsin7",
110 | "show_all_inline_media": false,
111 | "statuses_count": 513,
112 | "time_zone": "Eastern Time (US & Canada)",
113 | "url": null,
114 | "utc_offset": -18000,
115 | "verified": false
116 | }
117 | },
118 | {
119 | "contributors": null,
120 | "coordinates": null,
121 | "created_at": "Fri Apr 01 22:05:53 +0000 2011",
122 | "favorited": false,
123 | "geo": null,
124 | "id": 53941177673728000,
125 | "id_str": "53941177673728000",
126 | "in_reply_to_screen_name": null,
127 | "in_reply_to_status_id": null,
128 | "in_reply_to_status_id_str": null,
129 | "in_reply_to_user_id": null,
130 | "in_reply_to_user_id_str": null,
131 | "place": null,
132 | "retweet_count": 0,
133 | "retweeted": false,
134 | "source": "web",
135 | "text": "a #TaianeMello ta querendo me deixar maluco, so pode !!!",
136 | "truncated": false,
137 | "user": {
138 | "contributors_enabled": false,
139 | "created_at": "Sat Oct 16 00:44:59 +0000 2010",
140 | "default_profile": false,
141 | "default_profile_image": false,
142 | "description": "Estudante do MV1, tricolor ilustre ;p, jogador de futmesa e peladeiro do Petr\u00f4, quem me conhece, sabe exatamente como sou... ;D",
143 | "favourites_count": 0,
144 | "follow_request_sent": null,
145 | "followers_count": 37,
146 | "following": null,
147 | "friends_count": 67,
148 | "geo_enabled": false,
149 | "id": 203317177,
150 | "id_str": "203317177",
151 | "is_translator": false,
152 | "lang": "en",
153 | "listed_count": 1,
154 | "location": "Petropolis",
155 | "name": "Bernardo Kochem",
156 | "notifications": null,
157 | "profile_background_color": "1A1B1F",
158 | "profile_background_image_url": "http://a3.twimg.com/a/1300479984/images/themes/theme9/bg.gif",
159 | "profile_background_tile": false,
160 | "profile_image_url": "http://a3.twimg.com/profile_images/1279442404/S5030048_normal.JPG",
161 | "profile_link_color": "2FC2EF",
162 | "profile_sidebar_border_color": "181A1E",
163 | "profile_sidebar_fill_color": "252429",
164 | "profile_text_color": "666666",
165 | "profile_use_background_image": true,
166 | "protected": false,
167 | "screen_name": "bekochem",
168 | "show_all_inline_media": false,
169 | "statuses_count": 198,
170 | "time_zone": "Brasilia",
171 | "url": null,
172 | "utc_offset": -10800,
173 | "verified": false
174 | }
175 | },
176 | {
177 | "contributors": null,
178 | "coordinates": null,
179 | "created_at": "Fri Apr 01 22:05:53 +0000 2011",
180 | "favorited": false,
181 | "geo": null,
182 | "id": 53941176822272000,
183 | "id_str": "53941176822272000",
184 | "in_reply_to_screen_name": null,
185 | "in_reply_to_status_id": null,
186 | "in_reply_to_status_id_str": null,
187 | "in_reply_to_user_id": null,
188 | "in_reply_to_user_id_str": null,
189 | "place": null,
190 | "retweet_count": 0,
191 | "retweeted": false,
192 | "source": "Seesmic Desktop",
193 | "text": "/@TelegraphNews: Afghanistan UN murders: the worst possible incident for the US, Nato and the UN http://tgr.ph/gy5odP",
194 | "truncated": false,
195 | "user": {
196 | "contributors_enabled": false,
197 | "created_at": "Tue Sep 22 18:16:32 +0000 2009",
198 | "default_profile": true,
199 | "default_profile_image": false,
200 | "description": "Bringing you fast-paced, accurate news from across the country and around the world. 11 yr Air Force member.",
201 | "favourites_count": 0,
202 | "follow_request_sent": null,
203 | "followers_count": 5462,
204 | "following": null,
205 | "friends_count": 3547,
206 | "geo_enabled": false,
207 | "id": 76405315,
208 | "id_str": "76405315",
209 | "is_translator": false,
210 | "lang": "en",
211 | "listed_count": 500,
212 | "location": "Baton Rouge, LA",
213 | "name": "Chris",
214 | "notifications": null,
215 | "profile_background_color": "C0DEED",
216 | "profile_background_image_url": "http://a3.twimg.com/a/1301438647/images/themes/theme1/bg.png",
217 | "profile_background_tile": false,
218 | "profile_image_url": "http://a2.twimg.com/profile_images/760054127/twitterProfilePhoto_normal.jpg",
219 | "profile_link_color": "0084B4",
220 | "profile_sidebar_border_color": "C0DEED",
221 | "profile_sidebar_fill_color": "DDEEF6",
222 | "profile_text_color": "333333",
223 | "profile_use_background_image": true,
224 | "protected": false,
225 | "screen_name": "TheNewsBlotter",
226 | "show_all_inline_media": false,
227 | "statuses_count": 169209,
228 | "time_zone": "Central Time (US & Canada)",
229 | "url": "http://thenewsblotter.blogspot.com/",
230 | "utc_offset": -21600,
231 | "verified": false
232 | }
233 | },
234 | {
235 | "contributors": null,
236 | "coordinates": null,
237 | "created_at": "Fri Apr 01 22:05:52 +0000 2011",
238 | "favorited": false,
239 | "geo": null,
240 | "id": 53941174725120000,
241 | "id_str": "53941174725120000",
242 | "in_reply_to_screen_name": null,
243 | "in_reply_to_status_id": null,
244 | "in_reply_to_status_id_str": null,
245 | "in_reply_to_user_id": null,
246 | "in_reply_to_user_id_str": null,
247 | "place": null,
248 | "retweet_count": 0,
249 | "retweeted": false,
250 | "source": "\u00dcberSocial",
251 | "text": "Smt Even More Vex",
252 | "truncated": false,
253 | "user": {
254 | "contributors_enabled": false,
255 | "created_at": "Thu Nov 05 05:18:49 +0000 2009",
256 | "default_profile": false,
257 | "default_profile_image": false,
258 | "description": "$$ / \u2665 / Feisty / 15 / Mixxed\r\n\r\n#HH^ & #YME ^ Its A #YUMMM Tingg ! :P",
259 | "favourites_count": 171,
260 | "follow_request_sent": null,
261 | "followers_count": 3938,
262 | "following": null,
263 | "friends_count": 3508,
264 | "geo_enabled": true,
265 | "id": 87621800,
266 | "id_str": "87621800",
267 | "is_translator": false,
268 | "lang": "en",
269 | "listed_count": 377,
270 | "location": "Bittin @OGYUMBOY_IZZY Neck :-*",
271 | "name": "\u2603RedROBBiNYUMMM",
272 | "notifications": null,
273 | "profile_background_color": "080101",
274 | "profile_background_image_url": "http://a2.twimg.com/profile_background_images/222919284/101214-203703.jpg",
275 | "profile_background_tile": true,
276 | "profile_image_url": "http://a1.twimg.com/profile_images/1269458607/Picture_002_1__phixr_normal.jpg",
277 | "profile_link_color": "000000",
278 | "profile_sidebar_border_color": "f02ba8",
279 | "profile_sidebar_fill_color": "ffffff",
280 | "profile_text_color": "f527ad",
281 | "profile_use_background_image": true,
282 | "protected": false,
283 | "screen_name": "16SinsAPRIL19th",
284 | "show_all_inline_media": true,
285 | "statuses_count": 123712,
286 | "time_zone": "Hawaii",
287 | "url": "http://plixi.com/RedRobbinYUMM",
288 | "utc_offset": -36000,
289 | "verified": false
290 | }
291 | },
292 | {
293 | "contributors": null,
294 | "coordinates": null,
295 | "created_at": "Fri Apr 01 22:05:52 +0000 2011",
296 | "favorited": false,
297 | "geo": null,
298 | "id": 53941171906560000,
299 | "id_str": "53941171906560000",
300 | "in_reply_to_screen_name": null,
301 | "in_reply_to_status_id": null,
302 | "in_reply_to_status_id_str": null,
303 | "in_reply_to_user_id": null,
304 | "in_reply_to_user_id_str": null,
305 | "place": null,
306 | "retweet_count": 0,
307 | "retweeted": false,
308 | "source": "web",
309 | "text": "earphones in, @justinbieber on full blast, tub of ben and jerrys, block out the world. perfect",
310 | "truncated": false,
311 | "user": {
312 | "contributors_enabled": false,
313 | "created_at": "Sun Dec 19 22:10:31 +0000 2010",
314 | "default_profile": false,
315 | "default_profile_image": false,
316 | "description": "be yourself and #dreambig .. 28/03/11 PATTIE RT ME! 23/3/11 #myworldtour @justinbieber, best night ever! family and friends mean the world\u2665.",
317 | "favourites_count": 0,
318 | "follow_request_sent": null,
319 | "followers_count": 253,
320 | "following": null,
321 | "friends_count": 396,
322 | "geo_enabled": false,
323 | "id": 228496430,
324 | "id_str": "228496430",
325 | "is_translator": false,
326 | "lang": "en",
327 | "listed_count": 1,
328 | "location": "england",
329 | "name": "ellisJB",
330 | "notifications": null,
331 | "profile_background_color": "8dcfe3",
332 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/223151550/twitter.jpg",
333 | "profile_background_tile": true,
334 | "profile_image_url": "http://a3.twimg.com/profile_images/1290422336/JUSTIN_BIEBER2_normal.jpg",
335 | "profile_link_color": "0c00b3",
336 | "profile_sidebar_border_color": "030405",
337 | "profile_sidebar_fill_color": "14e0e0",
338 | "profile_text_color": "c918c9",
339 | "profile_use_background_image": true,
340 | "protected": false,
341 | "screen_name": "ellisbetx",
342 | "show_all_inline_media": false,
343 | "statuses_count": 537,
344 | "time_zone": null,
345 | "url": null,
346 | "utc_offset": null,
347 | "verified": false
348 | }
349 | },
350 | {
351 | "contributors": null,
352 | "coordinates": {
353 | "coordinates": [
354 | 3.851304,
355 | 7.341991
356 | ],
357 | "type": "Point"
358 | },
359 | "created_at": "Fri Apr 01 22:05:51 +0000 2011",
360 | "favorited": false,
361 | "geo": {
362 | "coordinates": [
363 | 7.341991,
364 | 3.851304
365 | ],
366 | "type": "Point"
367 | },
368 | "id": 53941170006528000,
369 | "id_str": "53941170006528000",
370 | "in_reply_to_screen_name": null,
371 | "in_reply_to_status_id": null,
372 | "in_reply_to_status_id_str": null,
373 | "in_reply_to_user_id": null,
374 | "in_reply_to_user_id_str": null,
375 | "place": null,
376 | "retweet_count": 0,
377 | "retweeted": false,
378 | "source": "\u00dcberSocial",
379 | "text": "E nevr kill yu..onpe RT @Adelajjj: Lwkm RT @koonley: Hehehe..e don pain am *tongue out* RT @Adelajjj: Mtscheeew...I ... http://tmi.me/8mBld",
380 | "truncated": false,
381 | "user": {
382 | "contributors_enabled": false,
383 | "created_at": "Sun May 02 11:02:11 +0000 2010",
384 | "default_profile": false,
385 | "default_profile_image": false,
386 | "description": "Ask and yu shall know...",
387 | "favourites_count": 1,
388 | "follow_request_sent": null,
389 | "followers_count": 41,
390 | "following": null,
391 | "friends_count": 112,
392 | "geo_enabled": true,
393 | "id": 139362670,
394 | "id_str": "139362670",
395 | "is_translator": false,
396 | "lang": "en",
397 | "listed_count": 0,
398 | "location": "\u00dcT: 7.341991,3.851304",
399 | "name": "Kunle Raji",
400 | "notifications": null,
401 | "profile_background_color": "8B542B",
402 | "profile_background_image_url": "http://a3.twimg.com/profile_background_images/125323899/051.jpg",
403 | "profile_background_tile": true,
404 | "profile_image_url": "http://a0.twimg.com/profile_images/1288807458/278767158_normal.jpg",
405 | "profile_link_color": "9D582E",
406 | "profile_sidebar_border_color": "D9B17E",
407 | "profile_sidebar_fill_color": "EADEAA",
408 | "profile_text_color": "333333",
409 | "profile_use_background_image": true,
410 | "protected": false,
411 | "screen_name": "koonley",
412 | "show_all_inline_media": false,
413 | "statuses_count": 594,
414 | "time_zone": "Pacific Time (US & Canada)",
415 | "url": null,
416 | "utc_offset": -28800,
417 | "verified": false
418 | }
419 | },
420 | {
421 | "contributors": null,
422 | "coordinates": null,
423 | "created_at": "Fri Apr 01 22:05:51 +0000 2011",
424 | "favorited": false,
425 | "geo": null,
426 | "id": 53941169373184000,
427 | "id_str": "53941169373184000",
428 | "in_reply_to_screen_name": null,
429 | "in_reply_to_status_id": null,
430 | "in_reply_to_status_id_str": null,
431 | "in_reply_to_user_id": null,
432 | "in_reply_to_user_id_str": null,
433 | "place": null,
434 | "retweet_count": 0,
435 | "retweeted": false,
436 | "source": "web",
437 | "text": "eu sou completamente apaixonada pela voz e pelo cantor do panic at the disco *o*",
438 | "truncated": false,
439 | "user": {
440 | "contributors_enabled": false,
441 | "created_at": "Mon Jul 27 15:33:37 +0000 2009",
442 | "default_profile": false,
443 | "default_profile_image": false,
444 | "description": "Tayna Pinheiro Bernardino , Amazonense, 14 anos aquariana, viciada em twitter,chocolate,musica,tumblr s2",
445 | "favourites_count": 371,
446 | "follow_request_sent": null,
447 | "followers_count": 2012,
448 | "following": null,
449 | "friends_count": 1714,
450 | "geo_enabled": true,
451 | "id": 60626790,
452 | "id_str": "60626790",
453 | "is_translator": false,
454 | "lang": "en",
455 | "listed_count": 231,
456 | "location": "Manaus - Am",
457 | "name": "tayna bernardino",
458 | "notifications": null,
459 | "profile_background_color": "09020f",
460 | "profile_background_image_url": "http://a3.twimg.com/profile_background_images/223296260/000000_dream-of-love.gif",
461 | "profile_background_tile": true,
462 | "profile_image_url": "http://a3.twimg.com/profile_images/1265953164/17012011670_normal.jpg",
463 | "profile_link_color": "f0719b",
464 | "profile_sidebar_border_color": "105559",
465 | "profile_sidebar_fill_color": "f0e8ed",
466 | "profile_text_color": "28d8f7",
467 | "profile_use_background_image": true,
468 | "protected": false,
469 | "screen_name": "taynabernardino",
470 | "show_all_inline_media": false,
471 | "statuses_count": 57319,
472 | "time_zone": "Brasilia",
473 | "url": "http://taynabernardino.tumblr.com/",
474 | "utc_offset": -10800,
475 | "verified": false
476 | }
477 | },
478 | {
479 | "contributors": null,
480 | "coordinates": null,
481 | "created_at": "Fri Apr 01 22:05:51 +0000 2011",
482 | "favorited": false,
483 | "geo": null,
484 | "id": 53941168848896000,
485 | "id_str": "53941168848896000",
486 | "in_reply_to_screen_name": null,
487 | "in_reply_to_status_id": null,
488 | "in_reply_to_status_id_str": null,
489 | "in_reply_to_user_id": null,
490 | "in_reply_to_user_id_str": null,
491 | "place": null,
492 | "retweet_count": 0,
493 | "retweeted": false,
494 | "source": "web",
495 | "text": "today is friday",
496 | "truncated": false,
497 | "user": {
498 | "contributors_enabled": false,
499 | "created_at": "Mon Jun 21 00:44:29 +0000 2010",
500 | "default_profile": false,
501 | "default_profile_image": true,
502 | "description": null,
503 | "favourites_count": 0,
504 | "follow_request_sent": null,
505 | "followers_count": 14,
506 | "following": null,
507 | "friends_count": 22,
508 | "geo_enabled": false,
509 | "id": 157823751,
510 | "id_str": "157823751",
511 | "is_translator": false,
512 | "lang": "en",
513 | "listed_count": 0,
514 | "location": null,
515 | "name": "Jordana",
516 | "notifications": null,
517 | "profile_background_color": "FF6699",
518 | "profile_background_image_url": "http://a3.twimg.com/a/1301071706/images/themes/theme11/bg.gif",
519 | "profile_background_tile": true,
520 | "profile_image_url": "http://a2.twimg.com/sticky/default_profile_images/default_profile_1_normal.png",
521 | "profile_link_color": "B40B43",
522 | "profile_sidebar_border_color": "CC3366",
523 | "profile_sidebar_fill_color": "E5507E",
524 | "profile_text_color": "362720",
525 | "profile_use_background_image": true,
526 | "protected": false,
527 | "screen_name": "_johlopes",
528 | "show_all_inline_media": false,
529 | "statuses_count": 173,
530 | "time_zone": null,
531 | "url": null,
532 | "utc_offset": null,
533 | "verified": false
534 | }
535 | },
536 | {
537 | "contributors": null,
538 | "coordinates": null,
539 | "created_at": "Fri Apr 01 22:05:50 +0000 2011",
540 | "favorited": false,
541 | "geo": null,
542 | "id": 53941166554624000,
543 | "id_str": "53941166554624000",
544 | "in_reply_to_screen_name": null,
545 | "in_reply_to_status_id": null,
546 | "in_reply_to_status_id_str": null,
547 | "in_reply_to_user_id": null,
548 | "in_reply_to_user_id_str": null,
549 | "place": null,
550 | "retweet_count": 0,
551 | "retweeted": false,
552 | "source": "web",
553 | "text": "Meu quarto est\u00e1 daquele jeito...todo bagun\u00e7ado...rsrsrsrs",
554 | "truncated": false,
555 | "user": {
556 | "contributors_enabled": false,
557 | "created_at": "Sat Aug 22 01:21:12 +0000 2009",
558 | "default_profile": false,
559 | "default_profile_image": false,
560 | "description": "Menina; Mulher; Pedagoga; S\u00e3o Paulina; Pagodeira; Apaixonada; Diferente e engra\u00e7ada!",
561 | "favourites_count": 2,
562 | "follow_request_sent": null,
563 | "followers_count": 267,
564 | "following": null,
565 | "friends_count": 460,
566 | "geo_enabled": true,
567 | "id": 67776575,
568 | "id_str": "67776575",
569 | "is_translator": false,
570 | "lang": "en",
571 | "listed_count": 14,
572 | "location": "S\u00e3o Paulo",
573 | "name": "Luisa",
574 | "notifications": null,
575 | "profile_background_color": "c773d4",
576 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/223646472/cora__o_quebrado.jpg",
577 | "profile_background_tile": true,
578 | "profile_image_url": "http://a2.twimg.com/profile_images/1289430825/047_normal.JPG",
579 | "profile_link_color": "f01de2",
580 | "profile_sidebar_border_color": "6058c9",
581 | "profile_sidebar_fill_color": "",
582 | "profile_text_color": "ff4ced",
583 | "profile_use_background_image": true,
584 | "protected": false,
585 | "screen_name": "Negah_Luhh",
586 | "show_all_inline_media": false,
587 | "statuses_count": 15080,
588 | "time_zone": "Brasilia",
589 | "url": "http://www.meadiciona.com/lulibispo",
590 | "utc_offset": -10800,
591 | "verified": false
592 | }
593 | },
594 | {
595 | "contributors": null,
596 | "coordinates": null,
597 | "created_at": "Fri Apr 01 22:05:50 +0000 2011",
598 | "favorited": false,
599 | "geo": null,
600 | "id": 53941165178880000,
601 | "id_str": "53941165178880000",
602 | "in_reply_to_screen_name": null,
603 | "in_reply_to_status_id": null,
604 | "in_reply_to_status_id_str": null,
605 | "in_reply_to_user_id": null,
606 | "in_reply_to_user_id_str": null,
607 | "place": null,
608 | "retweet_count": 0,
609 | "retweeted": false,
610 | "source": "Tinychat Connector",
611 | "text": "Hey check out this vid chat room with 40 people in it - http://tinychat.com/justanteenager [http://tinychat.com]",
612 | "truncated": false,
613 | "user": {
614 | "contributors_enabled": false,
615 | "created_at": "Sun Jun 20 18:06:05 +0000 2010",
616 | "default_profile": true,
617 | "default_profile_image": true,
618 | "description": null,
619 | "favourites_count": 0,
620 | "follow_request_sent": null,
621 | "followers_count": 2,
622 | "following": null,
623 | "friends_count": 29,
624 | "geo_enabled": false,
625 | "id": 157731697,
626 | "id_str": "157731697",
627 | "is_translator": false,
628 | "lang": "en",
629 | "listed_count": 0,
630 | "location": null,
631 | "name": "funguy",
632 | "notifications": null,
633 | "profile_background_color": "C0DEED",
634 | "profile_background_image_url": "http://a3.twimg.com/a/1301438647/images/themes/theme1/bg.png",
635 | "profile_background_tile": false,
636 | "profile_image_url": "http://a1.twimg.com/sticky/default_profile_images/default_profile_4_normal.png",
637 | "profile_link_color": "0084B4",
638 | "profile_sidebar_border_color": "C0DEED",
639 | "profile_sidebar_fill_color": "DDEEF6",
640 | "profile_text_color": "333333",
641 | "profile_use_background_image": true,
642 | "protected": false,
643 | "screen_name": "betageta3",
644 | "show_all_inline_media": false,
645 | "statuses_count": 785,
646 | "time_zone": null,
647 | "url": null,
648 | "utc_offset": null,
649 | "verified": false
650 | }
651 | },
652 | {
653 | "contributors": null,
654 | "coordinates": null,
655 | "created_at": "Fri Apr 01 22:05:50 +0000 2011",
656 | "favorited": false,
657 | "geo": null,
658 | "id": 53941163824128000,
659 | "id_str": "53941163824128000",
660 | "in_reply_to_screen_name": null,
661 | "in_reply_to_status_id": null,
662 | "in_reply_to_status_id_str": null,
663 | "in_reply_to_user_id": null,
664 | "in_reply_to_user_id_str": null,
665 | "place": null,
666 | "retweet_count": 0,
667 | "retweeted": false,
668 | "source": "Twitter for BlackBerry\u00ae",
669 | "text": "\"@ajc: Speaking of jobs ~>RT @ajcads: PCI nurses in demand @ DeKalb Medical. Unit opening soon. $4K bonus. Apply now http://bit.ly/gtlDJv\"",
670 | "truncated": false,
671 | "user": {
672 | "contributors_enabled": false,
673 | "created_at": "Mon Nov 01 02:01:12 +0000 2010",
674 | "default_profile": false,
675 | "default_profile_image": false,
676 | "description": "I AM what I WILL to be...Fabulous, A Legend, A Mogul!I AM a Daughter, Sister, Friend, Educator, Entrepreneur, Advocate, Motivated Mommy on the Move to the TOP! ",
677 | "favourites_count": 0,
678 | "follow_request_sent": null,
679 | "followers_count": 131,
680 | "following": null,
681 | "friends_count": 191,
682 | "geo_enabled": false,
683 | "id": 210680272,
684 | "id_str": "210680272",
685 | "is_translator": false,
686 | "lang": "en",
687 | "listed_count": 4,
688 | "location": "Atlanta...NetWORKing Globally ",
689 | "name": "Melissa Waller",
690 | "notifications": null,
691 | "profile_background_color": "A6DF40",
692 | "profile_background_image_url": "http://a3.twimg.com/profile_background_images/168952582/xca551120f1d7a81c4e220859fe5d0f6.png",
693 | "profile_background_tile": true,
694 | "profile_image_url": "http://a1.twimg.com/profile_images/1259137345/FacebookHomescreenImage_normal.jpg",
695 | "profile_link_color": "FFFFFF",
696 | "profile_sidebar_border_color": "FF7E00",
697 | "profile_sidebar_fill_color": "51C7E6",
698 | "profile_text_color": "FA2676",
699 | "profile_use_background_image": true,
700 | "protected": false,
701 | "screen_name": "mommiepreneur",
702 | "show_all_inline_media": false,
703 | "statuses_count": 1196,
704 | "time_zone": "Quito",
705 | "url": null,
706 | "utc_offset": -18000,
707 | "verified": false
708 | }
709 | },
710 | {
711 | "contributors": null,
712 | "coordinates": null,
713 | "created_at": "Fri Apr 01 22:05:49 +0000 2011",
714 | "favorited": false,
715 | "geo": null,
716 | "id": 53941162142208000,
717 | "id_str": "53941162142208000",
718 | "in_reply_to_screen_name": null,
719 | "in_reply_to_status_id": null,
720 | "in_reply_to_status_id_str": null,
721 | "in_reply_to_user_id": null,
722 | "in_reply_to_user_id_str": null,
723 | "place": null,
724 | "retweet_count": 0,
725 | "retweeted": false,
726 | "source": "web",
727 | "text": "s\u00f3 que o nome dele \u00e9 Phillipe KKKKKKKKKKK a\u00ed ele tava bebendo \u00e1gua, a\u00ed minha amg esqueceu e chamou ele de L\u00e9o. Ficamos rindo 53457454 horas!",
728 | "truncated": false,
729 | "user": {
730 | "contributors_enabled": false,
731 | "created_at": "Mon Jan 31 20:42:31 +0000 2011",
732 | "default_profile": false,
733 | "default_profile_image": false,
734 | "description": "te amarei de Janeiro a Janeiro, at\u00e9 o mundo acabar, @whoiscarlos \u2665 Atualizado por: @jufact. Desde: 10/02/2011 =]",
735 | "favourites_count": 23,
736 | "follow_request_sent": null,
737 | "followers_count": 289,
738 | "following": null,
739 | "friends_count": 171,
740 | "geo_enabled": false,
741 | "id": 245495210,
742 | "id_str": "245495210",
743 | "is_translator": false,
744 | "lang": "en",
745 | "listed_count": 8,
746 | "location": "Quarto do Carlos :9 #rawr",
747 | "name": "twitf\u00e3 Carlos",
748 | "notifications": null,
749 | "profile_background_color": "e8e8e8",
750 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/213555594/carlosx.png",
751 | "profile_background_tile": false,
752 | "profile_image_url": "http://a3.twimg.com/profile_images/1288424435/coelho_Carlos_normal.jpg",
753 | "profile_link_color": "84d7f0",
754 | "profile_sidebar_border_color": "f7f7f7",
755 | "profile_sidebar_fill_color": "",
756 | "profile_text_color": "100112",
757 | "profile_use_background_image": true,
758 | "protected": false,
759 | "screen_name": "ManiacasCarlos",
760 | "show_all_inline_media": false,
761 | "statuses_count": 6526,
762 | "time_zone": "Greenland",
763 | "url": "http://twitpic.com/photos/ManiacasCarlos",
764 | "utc_offset": -10800,
765 | "verified": false
766 | }
767 | },
768 | {
769 | "contributors": null,
770 | "coordinates": null,
771 | "created_at": "Fri Apr 01 22:05:49 +0000 2011",
772 | "favorited": false,
773 | "geo": null,
774 | "id": 53941161617920000,
775 | "id_str": "53941161617920000",
776 | "in_reply_to_screen_name": null,
777 | "in_reply_to_status_id": null,
778 | "in_reply_to_status_id_str": null,
779 | "in_reply_to_user_id": null,
780 | "in_reply_to_user_id_str": null,
781 | "place": null,
782 | "retweet_count": 0,
783 | "retweeted": false,
784 | "source": "twicca",
785 | "text": "\u304a\u306f\u3088\u3046\u3054\u3056\u3044\u307e\u3059",
786 | "truncated": false,
787 | "user": {
788 | "contributors_enabled": false,
789 | "created_at": "Tue Oct 06 12:15:55 +0000 2009",
790 | "default_profile": false,
791 | "default_profile_image": false,
792 | "description": "\u666e\u6bb5\u306f\u4e00\u822c\u4eba\u306e\u76ae\u3092\u88ab\u308a\u3064\u3064\u3082\u4e8c\u6b21\u5143\u304c\u306a\u3044\u3068\u751f\u304d\u3089\u308c\u306a\u3044\u30c0\u30e1\u306a\u793e\u4f1a\u4eba\u3002\u57fa\u672c\u7684\u306b\u5fc3\u306e\u5e95\u304b\u3089\u30f2\u30bf\u305d\u3057\u3066\u8150\u3067\u3059\u3002GODEATER\u3068DDFF\u306b\u840c\u3048\u308b\u65e5\u3005\u3002Drrr\u3068\u304bAPH\u3082\u5927\u597d\u304d\u3067\u3059\u3002\u30aa\u30d5\u3067\u306fBASARA\u3067\u6d3b\u52d5\u4e2d\u3001\u3067\u30823\u306f\u3084\u3063\u3066\u307e\u305b\u3093\u3002\u30b5\u30a4\u30c8\u3067\u306f\u7537\u4e3b\u5922\u5c0f\u8aac\u3068\u304b\u66f8\u3044\u3066\u307e\u3059\u3088\u3002\u30d5\u30a9\u30ed\u30fb\u30ea\u30e0\u304a\u6c17\u8efd\u306b\u30c9\u30be\u30fc\u3002",
793 | "favourites_count": 10,
794 | "follow_request_sent": null,
795 | "followers_count": 54,
796 | "following": null,
797 | "friends_count": 42,
798 | "geo_enabled": false,
799 | "id": 80286573,
800 | "id_str": "80286573",
801 | "is_translator": false,
802 | "lang": "ja",
803 | "listed_count": 4,
804 | "location": "\u307f\u3084\u304e\u3051\u3093\u306e\u771f\u3093\u4e2d\u3078\u3093",
805 | "name": "\u3055\u304b\u304d",
806 | "notifications": null,
807 | "profile_background_color": "131516",
808 | "profile_background_image_url": "http://a0.twimg.com/profile_background_images/62047302/dot_11.gif",
809 | "profile_background_tile": true,
810 | "profile_image_url": "http://a2.twimg.com/profile_images/1289192557/profile_normal.png",
811 | "profile_link_color": "009999",
812 | "profile_sidebar_border_color": "eeeeee",
813 | "profile_sidebar_fill_color": "efefef",
814 | "profile_text_color": "333333",
815 | "profile_use_background_image": true,
816 | "protected": false,
817 | "screen_name": "sa_kaki",
818 | "show_all_inline_media": false,
819 | "statuses_count": 9636,
820 | "time_zone": "Tokyo",
821 | "url": "http://mega36.sakura.ne.jp/",
822 | "utc_offset": 32400,
823 | "verified": false
824 | }
825 | },
826 | {
827 | "contributors": null,
828 | "coordinates": null,
829 | "created_at": "Fri Apr 01 22:05:49 +0000 2011",
830 | "favorited": false,
831 | "geo": null,
832 | "id": 53941160678400000,
833 | "id_str": "53941160678400000",
834 | "in_reply_to_screen_name": null,
835 | "in_reply_to_status_id": null,
836 | "in_reply_to_status_id_str": null,
837 | "in_reply_to_user_id": null,
838 | "in_reply_to_user_id_str": null,
839 | "place": null,
840 | "retweet_count": 0,
841 | "retweeted": false,
842 | "source": "web",
843 | "text": "nossa, quero ir pra algum lugar amanha, n\u00e3o aguento ter q ficar em casa com a minha tia e a minha v\u00f3 juntas",
844 | "truncated": false,
845 | "user": {
846 | "contributors_enabled": false,
847 | "created_at": "Wed Jan 06 19:51:33 +0000 2010",
848 | "default_profile": false,
849 | "default_profile_image": false,
850 | "description": "eu gosto de texugos ,guaxinins e suricatos .e voc\u00ea ? :3",
851 | "favourites_count": 2,
852 | "follow_request_sent": null,
853 | "followers_count": 199,
854 | "following": null,
855 | "friends_count": 191,
856 | "geo_enabled": true,
857 | "id": 102462023,
858 | "id_str": "102462023",
859 | "is_translator": false,
860 | "lang": "en",
861 | "listed_count": 2,
862 | "location": "",
863 | "name": "Natalia xD",
864 | "notifications": null,
865 | "profile_background_color": "000000",
866 | "profile_background_image_url": "http://a3.twimg.com/profile_background_images/190709748/knuckle.png",
867 | "profile_background_tile": true,
868 | "profile_image_url": "http://a0.twimg.com/profile_images/1241787503/Foto0376_normal.jpg",
869 | "profile_link_color": "000000",
870 | "profile_sidebar_border_color": "ffffff",
871 | "profile_sidebar_fill_color": "ffffff",
872 | "profile_text_color": "000000",
873 | "profile_use_background_image": true,
874 | "protected": false,
875 | "screen_name": "mantegavoadora",
876 | "show_all_inline_media": false,
877 | "statuses_count": 1527,
878 | "time_zone": "Greenland",
879 | "url": "http://www.formspring.me/Naahty",
880 | "utc_offset": -10800,
881 | "verified": false
882 | }
883 | },
884 | {
885 | "contributors": null,
886 | "coordinates": null,
887 | "created_at": "Fri Apr 01 22:05:49 +0000 2011",
888 | "favorited": false,
889 | "geo": null,
890 | "id": 53941159411712000,
891 | "id_str": "53941159411712000",
892 | "in_reply_to_screen_name": null,
893 | "in_reply_to_status_id": null,
894 | "in_reply_to_status_id_str": null,
895 | "in_reply_to_user_id": null,
896 | "in_reply_to_user_id_str": null,
897 | "place": null,
898 | "retweet_count": 0,
899 | "retweeted": false,
900 | "source": "Google",
901 | "text": "Des petits Ours de l'Himalaya http://goo.gl/fb/Oyjv0",
902 | "truncated": false,
903 | "user": {
904 | "contributors_enabled": false,
905 | "created_at": "Sat Jun 26 21:28:16 +0000 2010",
906 | "default_profile": false,
907 | "default_profile_image": false,
908 | "description": "",
909 | "favourites_count": 0,
910 | "follow_request_sent": null,
911 | "followers_count": 78,
912 | "following": null,
913 | "friends_count": 210,
914 | "geo_enabled": false,
915 | "id": 159981295,
916 | "id_str": "159981295",
917 | "is_translator": false,
918 | "lang": "fr",
919 | "listed_count": 0,
920 | "location": "",
921 | "name": "hapatchan",
922 | "notifications": null,
923 | "profile_background_color": "9AE4E8",
924 | "profile_background_image_url": "http://a0.twimg.com/a/1300401069/images/themes/theme16/bg.gif",
925 | "profile_background_tile": false,
926 | "profile_image_url": "http://a1.twimg.com/profile_images/1027264665/bo3ou_normal.jpg",
927 | "profile_link_color": "0084B4",
928 | "profile_sidebar_border_color": "BDDCAD",
929 | "profile_sidebar_fill_color": "DDFFCC",
930 | "profile_text_color": "333333",
931 | "profile_use_background_image": true,
932 | "protected": false,
933 | "screen_name": "hapatchan",
934 | "show_all_inline_media": false,
935 | "statuses_count": 603,
936 | "time_zone": "Greenland",
937 | "url": "http://www.hapatchan.blogspot.com/",
938 | "utc_offset": -10800,
939 | "verified": false
940 | }
941 | },
942 | {
943 | "contributors": null,
944 | "coordinates": null,
945 | "created_at": "Fri Apr 01 22:05:49 +0000 2011",
946 | "favorited": false,
947 | "geo": null,
948 | "id": 53941159105536000,
949 | "id_str": "53941159105536000",
950 | "in_reply_to_screen_name": null,
951 | "in_reply_to_status_id": null,
952 | "in_reply_to_status_id_str": null,
953 | "in_reply_to_user_id": null,
954 | "in_reply_to_user_id_str": null,
955 | "place": null,
956 | "retweet_count": 0,
957 | "retweeted": false,
958 | "source": "web",
959 | "text": "85 \u2013 \u00c0s vezes deixo de me aproximar de pessoas, por medo de perd\u00ea-las. (no sentido da morte), chega a ser paran\u00f3ia. #100factsaboutme",
960 | "truncated": false,
961 | "user": {
962 | "contributors_enabled": false,
963 | "created_at": "Thu Jan 21 23:23:45 +0000 2010",
964 | "default_profile": false,
965 | "default_profile_image": false,
966 | "description": "16 anos. Ciumento, exagerado, curioso, atrapalhado, medroso. Apaixonado por Atua\u00e7\u00e3o e Psicologia. N\u00e3o mudarei, tentando te impressionar. I Belong to Jesus! \u2665",
967 | "favourites_count": 5,
968 | "follow_request_sent": null,
969 | "followers_count": 426,
970 | "following": null,
971 | "friends_count": 96,
972 | "geo_enabled": true,
973 | "id": 107240071,
974 | "id_str": "107240071",
975 | "is_translator": false,
976 | "lang": "es",
977 | "listed_count": 124,
978 | "location": "Catanduva - S\u00e3o Paulo",
979 | "name": "Erik William Lopes",
980 | "notifications": null,
981 | "profile_background_color": "ffffff",
982 | "profile_background_image_url": "http://a1.twimg.com/profile_background_images/221771443/bg.png",
983 | "profile_background_tile": true,
984 | "profile_image_url": "http://a2.twimg.com/profile_images/1199593805/CIMG0150_normal.JPG",
985 | "profile_link_color": "78093b",
986 | "profile_sidebar_border_color": "0d0000",
987 | "profile_sidebar_fill_color": "DDEEF6",
988 | "profile_text_color": "000000",
989 | "profile_use_background_image": true,
990 | "protected": false,
991 | "screen_name": "_ErikLopes",
992 | "show_all_inline_media": false,
993 | "statuses_count": 26762,
994 | "time_zone": "Greenland",
995 | "url": "http://meadiciona.com/erikwiil",
996 | "utc_offset": -10800,
997 | "verified": false
998 | }
999 | },
1000 | {
1001 | "contributors": null,
1002 | "coordinates": null,
1003 | "created_at": "Fri Apr 01 22:05:48 +0000 2011",
1004 | "favorited": false,
1005 | "geo": null,
1006 | "id": 53941158166016000,
1007 | "id_str": "53941158166016000",
1008 | "in_reply_to_screen_name": null,
1009 | "in_reply_to_status_id": null,
1010 | "in_reply_to_status_id_str": null,
1011 | "in_reply_to_user_id": null,
1012 | "in_reply_to_user_id_str": null,
1013 | "place": null,
1014 | "retweet_count": 0,
1015 | "retweeted": false,
1016 | "source": "twitterfeed",
1017 | "text": "Home Depot Tries Crowdsourced Philanthropy on Facebook http://bit.ly/i5Meip #news #socialmedia",
1018 | "truncated": false,
1019 | "user": {
1020 | "contributors_enabled": false,
1021 | "created_at": "Sun Sep 20 05:11:03 +0000 2009",
1022 | "default_profile": true,
1023 | "default_profile_image": false,
1024 | "description": "Your real-time source for social media jobs in San Francisco and social media news.",
1025 | "favourites_count": 0,
1026 | "follow_request_sent": null,
1027 | "followers_count": 1211,
1028 | "following": null,
1029 | "friends_count": 0,
1030 | "geo_enabled": false,
1031 | "id": 75725191,
1032 | "id_str": "75725191",
1033 | "is_translator": false,
1034 | "lang": "en",
1035 | "listed_count": 20,
1036 | "location": "San Francisco, CA",
1037 | "name": "JobShoots",
1038 | "notifications": null,
1039 | "profile_background_color": "C0DEED",
1040 | "profile_background_image_url": "http://a3.twimg.com/a/1300991299/images/themes/theme1/bg.png",
1041 | "profile_background_tile": false,
1042 | "profile_image_url": "http://a1.twimg.com/profile_images/425265269/10431_156500280258_156499820258_4034514_6686001_n_normal.jpg",
1043 | "profile_link_color": "0084B4",
1044 | "profile_sidebar_border_color": "C0DEED",
1045 | "profile_sidebar_fill_color": "DDEEF6",
1046 | "profile_text_color": "333333",
1047 | "profile_use_background_image": true,
1048 | "protected": false,
1049 | "screen_name": "sfsmjobshoot",
1050 | "show_all_inline_media": false,
1051 | "statuses_count": 52111,
1052 | "time_zone": "Pacific Time (US & Canada)",
1053 | "url": "http://www.jobshoots.com",
1054 | "utc_offset": -28800,
1055 | "verified": false
1056 | }
1057 | },
1058 | {
1059 | "contributors": null,
1060 | "coordinates": null,
1061 | "created_at": "Fri Apr 01 22:05:48 +0000 2011",
1062 | "favorited": false,
1063 | "geo": null,
1064 | "id": 53941157838848000,
1065 | "id_str": "53941157838848000",
1066 | "in_reply_to_screen_name": null,
1067 | "in_reply_to_status_id": null,
1068 | "in_reply_to_status_id_str": null,
1069 | "in_reply_to_user_id": null,
1070 | "in_reply_to_user_id_str": null,
1071 | "place": null,
1072 | "retweet_count": 0,
1073 | "retweeted": false,
1074 | "source": "jigtwi",
1075 | "text": "\u306a\u3093\u304b\u6674\u308c\u3068\u308b\u3002\u3067\u3082\u5929\u6c17\u4e88\u5831\u306f\u96ea\u3063\u3066\u3069\u3046\u3044\u3046\u3053\u3068\u30fc",
1076 | "truncated": false,
1077 | "user": {
1078 | "contributors_enabled": false,
1079 | "created_at": "Mon Jul 19 06:48:54 +0000 2010",
1080 | "default_profile": false,
1081 | "default_profile_image": false,
1082 | "description": "\u30c1\u30ad\u30f3\u30cf\u30fc\u30c8\u306a\u8150\u5973\u5b50\u3067\u3059\u3002\u65e5\u5e38post\u304c\u591a\u3081\u3002\u9375\u306f\u30ea\u30a2\u53cb\u5bfe\u7b56(\u7b11)\u306a\u306e\u3067\u6c17\u8efd\u306b\u30d5\u30a9\u30ed\u30fc\u3069\u3046\u305e\uff01(\u796d\u308a\u6642\u306f\u5916\u3057\u3066\u307e\u3059)\u3055\u3088\u306a\u3089\u306f\u30d6\u30ed\u30c3\u30af\u3067\u304a\u9858\u3044\u3057\u307e\u3059\u3002\r\n\r\n\u64ec\u4eba\u5316/DFF/\uff76\uff70\uff86\uff73\uff9e\uff67\uff99/APH/LD1/\u5546\u696dBL/\u6771\u65b9/\u30b3\u30b9/\u30e9\u30c6/\u304a\u7d75\u304b\u304d\u7b49",
1083 | "favourites_count": 54,
1084 | "follow_request_sent": null,
1085 | "followers_count": 162,
1086 | "following": null,
1087 | "friends_count": 163,
1088 | "geo_enabled": false,
1089 | "id": 168402715,
1090 | "id_str": "168402715",
1091 | "is_translator": false,
1092 | "lang": "ja",
1093 | "listed_count": 3,
1094 | "location": "\u5317\u306e\u56fd\u306e\u8150\u6d77\u304b\u3089",
1095 | "name": "\u305f\u305d",
1096 | "notifications": null,
1097 | "profile_background_color": "8B542B",
1098 | "profile_background_image_url": "http://a2.twimg.com/a/1301438647/images/themes/theme8/bg.gif",
1099 | "profile_background_tile": false,
1100 | "profile_image_url": "http://a2.twimg.com/profile_images/1287631780/_________normal.png",
1101 | "profile_link_color": "9D582E",
1102 | "profile_sidebar_border_color": "D9B17E",
1103 | "profile_sidebar_fill_color": "EADEAA",
1104 | "profile_text_color": "333333",
1105 | "profile_use_background_image": true,
1106 | "protected": false,
1107 | "screen_name": "taso_red",
1108 | "show_all_inline_media": true,
1109 | "statuses_count": 19816,
1110 | "time_zone": "Tokyo",
1111 | "url": null,
1112 | "utc_offset": 32400,
1113 | "verified": false
1114 | }
1115 | },
1116 | {
1117 | "contributors": null,
1118 | "coordinates": null,
1119 | "created_at": "Fri Apr 01 22:05:48 +0000 2011",
1120 | "favorited": false,
1121 | "geo": null,
1122 | "id": 53941156375040000,
1123 | "id_str": "53941156375040000",
1124 | "in_reply_to_screen_name": null,
1125 | "in_reply_to_status_id": null,
1126 | "in_reply_to_status_id_str": null,
1127 | "in_reply_to_user_id": null,
1128 | "in_reply_to_user_id_str": null,
1129 | "place": null,
1130 | "retweet_count": 0,
1131 | "retweeted": false,
1132 | "source": "Echofon",
1133 | "text": "Lol RT @JesusDidIt3: I should have stayed HOME!!! I ain't got time for GIRLS!!!",
1134 | "truncated": false,
1135 | "user": {
1136 | "contributors_enabled": false,
1137 | "created_at": "Tue Jan 05 07:16:45 +0000 2010",
1138 | "default_profile": false,
1139 | "default_profile_image": false,
1140 | "description": "whoooosssseeeyou?\u2122 \r\n",
1141 | "favourites_count": 146,
1142 | "follow_request_sent": null,
1143 | "followers_count": 1198,
1144 | "following": null,
1145 | "friends_count": 564,
1146 | "geo_enabled": false,
1147 | "id": 101988812,
1148 | "id_str": "101988812",
1149 | "is_translator": false,
1150 | "lang": "en",
1151 | "listed_count": 78,
1152 | "location": "Atlanta, GA",
1153 | "name": "Ranesha Finley",
1154 | "notifications": null,
1155 | "profile_background_color": "50586b",
1156 | "profile_background_image_url": "http://a2.twimg.com/profile_background_images/226370379/200269_10150180898970774_775445773_8467827_174615_n.jpg",
1157 | "profile_background_tile": true,
1158 | "profile_image_url": "http://a3.twimg.com/profile_images/1289622843/ranesha89_normal.jpg",
1159 | "profile_link_color": "f2e608",
1160 | "profile_sidebar_border_color": "a35b71",
1161 | "profile_sidebar_fill_color": "1f1aa3",
1162 | "profile_text_color": "605a7a",
1163 | "profile_use_background_image": true,
1164 | "protected": false,
1165 | "screen_name": "ranesha89",
1166 | "show_all_inline_media": false,
1167 | "statuses_count": 13785,
1168 | "time_zone": "Eastern Time (US & Canada)",
1169 | "url": null,
1170 | "utc_offset": -18000,
1171 | "verified": false
1172 | }
1173 | }
1174 | ]
1175 |
--------------------------------------------------------------------------------