Location search (#212)
* Add %location search option Search for posts for a given location ID using %[location id] as the query * Document %location search * Make pylint happy * Use correct paths for location results * Fix —help output Add description of location argument and fix output error for short help. * Add unit tests for location download * Add extra unit test for locations
This commit is contained in:
parent
1ab9e44104
commit
be5d02ef3b
@ -85,6 +85,11 @@ Instaloader supports the following targets:
|
||||
- ``"#hashtag"``
|
||||
Posts with a certain **hashtag** (the quotes are usually necessary),
|
||||
|
||||
- ``%location id``
|
||||
Posts tagged with a given location; the location ID is the numerical ID
|
||||
Instagram labels a location with (e.g.
|
||||
\https://www.instagram.com/explore/locations/**362629379**/plymouth-naval-memorial/).
|
||||
|
||||
- ``:stories``
|
||||
The currently-visible **stories** of your followees (requires
|
||||
:option:`--login`),
|
||||
@ -126,7 +131,7 @@ Filename Specification
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
For each target, Instaloader creates a directory named after the target,
|
||||
i.e. ``profile``, ``#hashtag``, ``:feed``, etc. and therein saves the
|
||||
i.e. ``profile``, ``#hashtag``, ``%location id``, ``:feed``, etc. and therein saves the
|
||||
posts in files named after the post's timestamp.
|
||||
|
||||
:option:`--dirname-pattern` allows to configure the directory name of each
|
||||
|
@ -8,7 +8,7 @@ Instaloader is invoked with::
|
||||
$ instaloader [options] target [target ...]
|
||||
|
||||
where ``target`` is a ``profile``, a ``"#hashtag"``, ``@profile`` (all profiles
|
||||
that *profile* is following), or if logged in ``:feed`` (pictures from your
|
||||
that *profile* is following), ``%location ID``, or if logged in ``:feed`` (pictures from your
|
||||
feed), ``:stories`` (stories of your followees) or ``:saved`` (collection of
|
||||
posts marked as saved).
|
||||
|
||||
@ -171,7 +171,7 @@ Which Posts to Download
|
||||
.. option:: --count COUNT, -c
|
||||
|
||||
Do not attempt to download more than COUNT posts. Applies only to
|
||||
``#hashtag`` and ``:feed``.
|
||||
``#hashtag``, ``%location id``, and ``:feed``.
|
||||
|
||||
|
||||
Login (Download Private Profiles)
|
||||
|
@ -43,7 +43,7 @@ See :ref:`install` for more options on how to install Instaloader.
|
||||
|
||||
instaloader [--comments] [--geotags] [--stories] [--highlights] [--tagged]
|
||||
[--login YOUR-USERNAME] [--fast-update]
|
||||
profile | "#hashtag" |
|
||||
profile | "#hashtag" | %location_id |
|
||||
:stories | :feed | :saved
|
||||
|
||||
See :ref:`download-pictures-from-instagram` for a detailed introduction on how
|
||||
|
@ -20,7 +20,7 @@ def usage_string():
|
||||
return """
|
||||
{0} [--comments] [--geotags] [--stories] [--highlights] [--tagged]
|
||||
{2:{1}} [--login YOUR-USERNAME] [--fast-update]
|
||||
{2:{1}} profile | "#hashtag" | :stories | :feed | :saved
|
||||
{2:{1}} profile | "#hashtag" | %%location_id | :stories | :feed | :saved
|
||||
{0} --help""".format(argv0, len(argv0), '')
|
||||
|
||||
|
||||
@ -130,6 +130,9 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
||||
post_filter=post_filter)
|
||||
elif target[0] == '-':
|
||||
instaloader.download_post(Post.from_shortcode(instaloader.context, target[1:]), target)
|
||||
elif target[0] == "%":
|
||||
instaloader.download_location(location=target[1:], max_count=max_count, fast_update=fast_update,
|
||||
post_filter=post_filter)
|
||||
elif target == ":feed":
|
||||
instaloader.download_feed_posts(fast_update=fast_update, max_count=max_count,
|
||||
post_filter=post_filter)
|
||||
@ -208,6 +211,7 @@ def main():
|
||||
help="Download all followees of profile. Requires --login. "
|
||||
"Consider using :feed rather than @yourself.")
|
||||
g_targets.add_argument('_hashtag', nargs='*', metavar='"#hashtag"', help="Download #hashtag.")
|
||||
g_targets.add_argument('_location', nargs='*', metavar='%location_id', help="Download %%location_id.")
|
||||
g_targets.add_argument('_feed', nargs='*', metavar=":feed",
|
||||
help="Download pictures from your feed. Requires --login.")
|
||||
g_targets.add_argument('_stories', nargs='*', metavar=":stories",
|
||||
|
@ -647,6 +647,55 @@ class Instaloader:
|
||||
if fast_update and not downloaded:
|
||||
break
|
||||
|
||||
def get_location_posts(self, location: str) -> Iterator[Post]:
|
||||
"""Get Posts which are listed by Instagram for a given Location.
|
||||
|
||||
:return: Iterator over Posts of a location's posts
|
||||
"""
|
||||
has_next_page = True
|
||||
end_cursor = None
|
||||
while has_next_page:
|
||||
if end_cursor:
|
||||
params = {'__a': 1, 'max_id': end_cursor}
|
||||
else:
|
||||
params = {'__a': 1}
|
||||
location_data = self.context.get_json('explore/locations/{0}/'.format(location),
|
||||
params)['graphql']['location']['edge_location_to_media']
|
||||
yield from (Post(self.context, edge['node']) for edge in location_data['edges'])
|
||||
has_next_page = location_data['page_info']['has_next_page']
|
||||
end_cursor = location_data['page_info']['end_cursor']
|
||||
|
||||
def download_location(self, location: str,
|
||||
max_count: Optional[int] = None,
|
||||
post_filter: Optional[Callable[[Post], bool]] = None,
|
||||
fast_update: bool = False) -> None:
|
||||
"""Download pictures of one location.
|
||||
|
||||
To download the last 30 pictures with location 362629379, do::
|
||||
|
||||
loader = Instaloader()
|
||||
loader.download_location(362629379, max_count=30)
|
||||
|
||||
:param location: Location to download, as Instagram numerical ID
|
||||
:param max_count: Maximum count of pictures to download
|
||||
:param post_filter: function(post), which returns True if given picture should be downloaded
|
||||
:param fast_update: If true, abort when first already-downloaded picture is encountered
|
||||
"""
|
||||
self.context.log("Retrieving pictures for location {}...".format(location))
|
||||
count = 1
|
||||
for post in self.get_location_posts(location):
|
||||
if max_count is not None and count > max_count:
|
||||
break
|
||||
self.context.log('[{0:3d}] %{1} '.format(count, location), end='', flush=True)
|
||||
if post_filter is not None and not post_filter(post):
|
||||
self.context.log('<skipped>')
|
||||
continue
|
||||
count += 1
|
||||
with self.context.error_catcher('Download location {}'.format(location)):
|
||||
downloaded = self.download_post(post, target='%' + location)
|
||||
if fast_update and not downloaded:
|
||||
break
|
||||
|
||||
@_requires_login
|
||||
def get_explore_posts(self) -> Iterator[Post]:
|
||||
"""Get Posts which are worthy of exploring suggested by Instagram.
|
||||
|
@ -12,6 +12,7 @@ PROFILE_WITH_HIGHLIGHTS = 325732271
|
||||
PUBLIC_PROFILE = "selenagomez"
|
||||
PUBLIC_PROFILE_ID = 460563723
|
||||
HASHTAG = "kitten"
|
||||
LOCATION = "362629379"
|
||||
OWN_USERNAME = "aandergr"
|
||||
NORMAL_MAX_COUNT = 2
|
||||
PAGING_MAX_COUNT = 15
|
||||
@ -69,6 +70,15 @@ class TestInstaloaderAnonymously(unittest.TestCase):
|
||||
if count == PAGING_MAX_COUNT:
|
||||
break
|
||||
|
||||
def test_location_download(self):
|
||||
self.L.download_location(LOCATION, NORMAL_MAX_COUNT)
|
||||
|
||||
def test_location_paging(self):
|
||||
for count, post in enumerate(self.L.get_location_posts(LOCATION)):
|
||||
print(post)
|
||||
if count == PAGING_MAX_COUNT:
|
||||
break
|
||||
|
||||
def test_get_id_by_username(self):
|
||||
self.assertEqual(PUBLIC_PROFILE_ID,
|
||||
instaloader.Profile.from_username(self.L.context, PUBLIC_PROFILE).userid)
|
||||
|
Loading…
Reference in New Issue
Block a user