parent
75a729781d
commit
0aa1ec7c76
@ -89,6 +89,7 @@ Instaloader supports the following targets:
|
|||||||
Posts tagged with a given location; the location ID is the numerical ID
|
Posts tagged with a given location; the location ID is the numerical ID
|
||||||
Instagram labels a location with (e.g.
|
Instagram labels a location with (e.g.
|
||||||
\https://www.instagram.com/explore/locations/**362629379**/plymouth-naval-memorial/).
|
\https://www.instagram.com/explore/locations/**362629379**/plymouth-naval-memorial/).
|
||||||
|
Requires :option:`--login`.
|
||||||
|
|
||||||
.. versionadded:: 4.2
|
.. versionadded:: 4.2
|
||||||
|
|
||||||
@ -122,7 +123,7 @@ downloads the pictures and videos and their captions. You can specify
|
|||||||
|
|
||||||
- :option:`--geotags`
|
- :option:`--geotags`
|
||||||
**download geotags** of each post and save them as
|
**download geotags** of each post and save them as
|
||||||
Google Maps link,
|
Google Maps link (requires :option:`--login`),
|
||||||
|
|
||||||
For a reference of all supported command line options, see
|
For a reference of all supported command line options, see
|
||||||
:ref:`command-line-options`.
|
:ref:`command-line-options`.
|
||||||
|
@ -61,8 +61,7 @@ What to Download of each Post
|
|||||||
|
|
||||||
**Download geotags** when available. Geotags are stored as a text file with
|
**Download geotags** when available. Geotags are stored as a text file with
|
||||||
the location's name and a Google Maps link. This requires an additional
|
the location's name and a Google Maps link. This requires an additional
|
||||||
request to the Instagram server for each picture, which is why it is disabled
|
request to the Instagram server for each picture. Requires :option:`--login`.
|
||||||
by default.
|
|
||||||
|
|
||||||
.. option:: --comments, -C
|
.. option:: --comments, -C
|
||||||
|
|
||||||
|
@ -98,6 +98,9 @@ def _main(instaloader: Instaloader, targetlist: List[str],
|
|||||||
else:
|
else:
|
||||||
instaloader.interactive_login(username)
|
instaloader.interactive_login(username)
|
||||||
instaloader.context.log("Logged in as %s." % username)
|
instaloader.context.log("Logged in as %s." % username)
|
||||||
|
# since 4.2.9 login is required for geotags
|
||||||
|
if instaloader.download_geotags and not instaloader.context.is_logged_in:
|
||||||
|
instaloader.context.error("Warning: Use --login to download geotags of posts.")
|
||||||
# Try block for KeyboardInterrupt (save session on ^C)
|
# Try block for KeyboardInterrupt (save session on ^C)
|
||||||
profiles = set()
|
profiles = set()
|
||||||
anonymous_retry_profiles = set()
|
anonymous_retry_profiles = set()
|
||||||
@ -223,7 +226,8 @@ def main():
|
|||||||
help="Download all followees of profile. Requires --login. "
|
help="Download all followees of profile. Requires --login. "
|
||||||
"Consider using :feed rather than @yourself.")
|
"Consider using :feed rather than @yourself.")
|
||||||
g_targets.add_argument('_hashtag', nargs='*', metavar='"#hashtag"', help="Download #hashtag.")
|
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('_location', nargs='*', metavar='%location_id',
|
||||||
|
help="Download %%location_id. Requires --login.")
|
||||||
g_targets.add_argument('_feed', nargs='*', metavar=":feed",
|
g_targets.add_argument('_feed', nargs='*', metavar=":feed",
|
||||||
help="Download pictures from your feed. Requires --login.")
|
help="Download pictures from your feed. Requires --login.")
|
||||||
g_targets.add_argument('_stories', nargs='*', metavar=":stories",
|
g_targets.add_argument('_stories', nargs='*', metavar=":stories",
|
||||||
@ -258,7 +262,7 @@ def main():
|
|||||||
help='Download geotags when available. Geotags are stored as a '
|
help='Download geotags when available. Geotags are stored as a '
|
||||||
'text file with the location\'s name and a Google Maps link. '
|
'text file with the location\'s name and a Google Maps link. '
|
||||||
'This requires an additional request to the Instagram '
|
'This requires an additional request to the Instagram '
|
||||||
'server for each picture, which is why it is disabled by default.')
|
'server for each picture. Requires --login.')
|
||||||
g_post.add_argument('-C', '--comments', action='store_true',
|
g_post.add_argument('-C', '--comments', action='store_true',
|
||||||
help='Download and update comments for each post. '
|
help='Download and update comments for each post. '
|
||||||
'This requires an additional request to the Instagram '
|
'This requires an additional request to the Instagram '
|
||||||
|
@ -47,8 +47,6 @@ def _requires_login(func: Callable) -> Callable:
|
|||||||
if not instaloader.context.is_logged_in:
|
if not instaloader.context.is_logged_in:
|
||||||
raise LoginRequiredException("--login=USERNAME required.")
|
raise LoginRequiredException("--login=USERNAME required.")
|
||||||
return func(instaloader, *args, **kwargs)
|
return func(instaloader, *args, **kwargs)
|
||||||
docstring_text = ":raises LoginRequiredException: If called without being logged in.\n"
|
|
||||||
call.__doc__ = call.__doc__ + docstring_text if call.__doc__ is not None else docstring_text
|
|
||||||
return call
|
return call
|
||||||
|
|
||||||
|
|
||||||
@ -386,6 +384,7 @@ class Instaloader:
|
|||||||
"""Saves internally stored :class:`requests.Session` object.
|
"""Saves internally stored :class:`requests.Session` object.
|
||||||
|
|
||||||
:param filename: Filename, or None to use default filename.
|
:param filename: Filename, or None to use default filename.
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
if filename is None:
|
if filename is None:
|
||||||
assert self.context.username is not None
|
assert self.context.username is not None
|
||||||
@ -510,6 +509,7 @@ class Instaloader:
|
|||||||
To use this, one needs to be logged in
|
To use this, one needs to be logged in
|
||||||
|
|
||||||
:param userids: List of user IDs to be processed in terms of downloading their stories, or None.
|
:param userids: List of user IDs to be processed in terms of downloading their stories, or None.
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not userids:
|
if not userids:
|
||||||
@ -546,6 +546,7 @@ class Instaloader:
|
|||||||
:param filename_target: Replacement for {target} in dirname_pattern and filename_pattern
|
:param filename_target: Replacement for {target} in dirname_pattern and filename_pattern
|
||||||
or None if profile name should be used instead
|
or None if profile name should be used instead
|
||||||
:param storyitem_filter: function(storyitem), which returns True if given StoryItem should be downloaded
|
:param storyitem_filter: function(storyitem), which returns True if given StoryItem should be downloaded
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not userids:
|
if not userids:
|
||||||
@ -605,6 +606,7 @@ class Instaloader:
|
|||||||
.. versionadded:: 4.1
|
.. versionadded:: 4.1
|
||||||
|
|
||||||
:param user: ID or Profile of the user whose highlights should get fetched.
|
:param user: ID or Profile of the user whose highlights should get fetched.
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
userid = user if isinstance(user, int) else user.userid
|
userid = user if isinstance(user, int) else user.userid
|
||||||
@ -634,6 +636,7 @@ class Instaloader:
|
|||||||
:param filename_target: Replacement for {target} in dirname_pattern and filename_pattern
|
:param filename_target: Replacement for {target} in dirname_pattern and filename_pattern
|
||||||
or None if profile name and the highlights' titles should be used instead
|
or None if profile name and the highlights' titles should be used instead
|
||||||
:param storyitem_filter: function(storyitem), which returns True if given StoryItem should be downloaded
|
:param storyitem_filter: function(storyitem), which returns True if given StoryItem should be downloaded
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
for user_highlight in self.get_highlights(user):
|
for user_highlight in self.get_highlights(user):
|
||||||
name = user_highlight.owner_username
|
name = user_highlight.owner_username
|
||||||
@ -659,6 +662,7 @@ class Instaloader:
|
|||||||
"""Get Posts of the user's feed.
|
"""Get Posts of the user's feed.
|
||||||
|
|
||||||
:return: Iterator over Posts of the user's feed.
|
:return: Iterator over Posts of the user's feed.
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
data = self.context.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})["data"]
|
data = self.context.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})["data"]
|
||||||
@ -692,6 +696,7 @@ class Instaloader:
|
|||||||
:param max_count: Maximum count of pictures to download
|
:param max_count: Maximum count of pictures to download
|
||||||
:param fast_update: If true, abort when first already-downloaded picture is encountered
|
:param fast_update: If true, abort when first already-downloaded picture is encountered
|
||||||
:param post_filter: function(post), which returns True if given picture should be downloaded
|
:param post_filter: function(post), which returns True if given picture should be downloaded
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
self.context.log("Retrieving pictures from your feed...")
|
self.context.log("Retrieving pictures from your feed...")
|
||||||
count = 1
|
count = 1
|
||||||
@ -717,6 +722,7 @@ class Instaloader:
|
|||||||
:param max_count: Maximum count of pictures to download
|
:param max_count: Maximum count of pictures to download
|
||||||
:param fast_update: If true, abort when first already-downloaded picture is encountered
|
:param fast_update: If true, abort when first already-downloaded picture is encountered
|
||||||
:param post_filter: function(post), which returns True if given picture should be downloaded
|
:param post_filter: function(post), which returns True if given picture should be downloaded
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
self.context.log("Retrieving saved posts...")
|
self.context.log("Retrieving saved posts...")
|
||||||
count = 1
|
count = 1
|
||||||
@ -734,12 +740,17 @@ class Instaloader:
|
|||||||
if fast_update and not downloaded:
|
if fast_update and not downloaded:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
@_requires_login
|
||||||
def get_location_posts(self, location: str) -> Iterator[Post]:
|
def get_location_posts(self, location: str) -> Iterator[Post]:
|
||||||
"""Get Posts which are listed by Instagram for a given Location.
|
"""Get Posts which are listed by Instagram for a given Location.
|
||||||
|
|
||||||
:return: Iterator over Posts of a location's posts
|
:return: Iterator over Posts of a location's posts
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
|
|
||||||
.. versionadded:: 4.2
|
.. versionadded:: 4.2
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2.9
|
||||||
|
Require being logged in (as required by Instagram)
|
||||||
"""
|
"""
|
||||||
has_next_page = True
|
has_next_page = True
|
||||||
end_cursor = None
|
end_cursor = None
|
||||||
@ -754,6 +765,7 @@ class Instaloader:
|
|||||||
has_next_page = location_data['page_info']['has_next_page']
|
has_next_page = location_data['page_info']['has_next_page']
|
||||||
end_cursor = location_data['page_info']['end_cursor']
|
end_cursor = location_data['page_info']['end_cursor']
|
||||||
|
|
||||||
|
@_requires_login
|
||||||
def download_location(self, location: str,
|
def download_location(self, location: str,
|
||||||
max_count: Optional[int] = None,
|
max_count: Optional[int] = None,
|
||||||
post_filter: Optional[Callable[[Post], bool]] = None,
|
post_filter: Optional[Callable[[Post], bool]] = None,
|
||||||
@ -769,8 +781,12 @@ class Instaloader:
|
|||||||
:param max_count: Maximum count of pictures to download
|
:param max_count: Maximum count of pictures to download
|
||||||
:param post_filter: function(post), which returns True if given picture should be downloaded
|
: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
|
:param fast_update: If true, abort when first already-downloaded picture is encountered
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
|
|
||||||
.. versionadded:: 4.2
|
.. versionadded:: 4.2
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2.9
|
||||||
|
Require being logged in (as required by Instagram)
|
||||||
"""
|
"""
|
||||||
self.context.log("Retrieving pictures for location {}...".format(location))
|
self.context.log("Retrieving pictures for location {}...".format(location))
|
||||||
count = 1
|
count = 1
|
||||||
@ -792,6 +808,7 @@ class Instaloader:
|
|||||||
"""Get Posts which are worthy of exploring suggested by Instagram.
|
"""Get Posts which are worthy of exploring suggested by Instagram.
|
||||||
|
|
||||||
:return: Iterator over Posts of the user's suggested posts.
|
:return: Iterator over Posts of the user's suggested posts.
|
||||||
|
:raises LoginRequiredException: If called without being logged in.
|
||||||
"""
|
"""
|
||||||
data = self.context.get_json('explore/', {})
|
data = self.context.get_json('explore/', {})
|
||||||
yield from (Post(self.context, node)
|
yield from (Post(self.context, node)
|
||||||
|
@ -408,10 +408,17 @@ class Post:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def location(self) -> Optional[PostLocation]:
|
def location(self) -> Optional[PostLocation]:
|
||||||
"""If the Post has a location, returns PostLocation namedtuple with fields 'id', 'lat' and 'lng' and 'name'."""
|
"""
|
||||||
|
If the Post has a location, returns PostLocation namedtuple with fields 'id', 'lat' and 'lng' and 'name'.
|
||||||
|
|
||||||
|
.. versionchanged:: 4.2.9
|
||||||
|
Require being logged in (as required by Instagram), return None if not logged-in.
|
||||||
|
"""
|
||||||
loc = self._field("location")
|
loc = self._field("location")
|
||||||
if self._location or not loc:
|
if self._location or not loc:
|
||||||
return self._location
|
return self._location
|
||||||
|
if not self._context.is_logged_in:
|
||||||
|
return None
|
||||||
location_id = int(loc['id'])
|
location_id = int(loc['id'])
|
||||||
if any(k not in loc for k in ('name', 'slug', 'has_public_page', 'lat', 'lng')):
|
if any(k not in loc for k in ('name', 'slug', 'has_public_page', 'lat', 'lng')):
|
||||||
loc = self._context.get_json("explore/locations/{0}/".format(location_id),
|
loc = self._context.get_json("explore/locations/{0}/".format(location_id),
|
||||||
|
@ -77,15 +77,6 @@ class TestInstaloaderAnonymously(unittest.TestCase):
|
|||||||
if count == PAGING_MAX_COUNT:
|
if count == PAGING_MAX_COUNT:
|
||||||
break
|
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):
|
def test_get_id_by_username(self):
|
||||||
self.assertEqual(PUBLIC_PROFILE_ID,
|
self.assertEqual(PUBLIC_PROFILE_ID,
|
||||||
instaloader.Profile.from_username(self.L.context, PUBLIC_PROFILE).userid)
|
instaloader.Profile.from_username(self.L.context, PUBLIC_PROFILE).userid)
|
||||||
@ -193,6 +184,15 @@ class TestInstaloaderLoggedIn(TestInstaloaderAnonymously):
|
|||||||
if count == PAGING_MAX_COUNT:
|
if count == PAGING_MAX_COUNT:
|
||||||
break
|
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
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user