--no-posts; Deprecate --{profile-pic,stories}-only

This commit is contained in:
Alexander Graf 2018-08-20 15:23:11 +02:00
parent 0f0ac13d72
commit e388a1c966
3 changed files with 115 additions and 51 deletions

View File

@ -95,8 +95,15 @@ What to Download of each Profile
.. option:: --profile-pic-only, -P
.. deprecated:: 4.1
Use :option:`--no-posts`.
Only download profile picture.
.. option:: --no-posts
Do not download regular posts.
.. option:: --no-profile-pic
Do not download profile picture.
@ -112,6 +119,9 @@ What to Download of each Profile
.. option:: --stories-only
.. deprecated:: 4.1
Use :option:`--stories` :option:`--no-posts`.
Rather than downloading regular posts of each specified profile, only
download stories. Requires :option:`--login`. Does not imply
:option:`--no-profile-pic`.
@ -121,10 +131,6 @@ What to Download of each Profile
If possible, use ``:stories`` target rather than :option:`--stories-only`
with all your followees. ``:stories`` uses fewer API requests.
.. option:: --tagged-only
Download only post where each profile is tagged, not their regular posts.
Which Posts to Download
^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -58,12 +58,11 @@ def filterstr_to_filterfunc(filter_str: str, item_type: type):
def _main(instaloader: Instaloader, targetlist: List[str],
username: Optional[str] = None, password: Optional[str] = None,
sessionfile: Optional[str] = None, max_count: Optional[int] = None,
profile_pic: bool = True, profile_pic_only: bool = False,
sessionfile: Optional[str] = None,
download_profile_pic: bool = True,
download_posts=True, download_stories: bool = False, download_tagged: bool = False,
fast_update: bool = False,
stories: bool = False, stories_only: bool = False,
tagged: bool = False, tagged_only: bool = False,
post_filter_str: Optional[str] = None,
max_count: Optional[int] = None, post_filter_str: Optional[str] = None,
storyitem_filter_str: Optional[str] = None) -> None:
"""Download set of profiles, hashtags etc. and handle logging in and session files if desired."""
# Parse and generate filter function
@ -89,10 +88,6 @@ def _main(instaloader: Instaloader, targetlist: List[str],
else:
instaloader.interactive_login(username)
instaloader.context.log("Logged in as %s." % username)
# Determine what to download
download_profile_pic = profile_pic or profile_pic_only
download_profile_posts = not (stories_only or profile_pic_only)
download_profile_stories = stories or stories_only
# Try block for KeyboardInterrupt (save session on ^C)
profiles = set()
anonymous_retry_profiles = set()
@ -147,7 +142,7 @@ def _main(instaloader: Instaloader, targetlist: List[str],
try:
profile = instaloader.check_profile_id(target)
if instaloader.context.is_logged_in and profile.has_blocked_viewer:
if download_profile_pic or (download_profile_posts and not profile.is_private):
if download_profile_pic or ((download_posts or download_tagged) and not profile.is_private):
raise ProfileNotExistsException("{} blocked you; But we download her anonymously."
.format(target))
else:
@ -157,39 +152,30 @@ def _main(instaloader: Instaloader, targetlist: List[str],
except ProfileNotExistsException as err:
# Not only our profile.has_blocked_viewer condition raises ProfileNotExistsException,
# check_profile_id() also does, since access to blocked profile may be responded with 404.
if instaloader.context.is_logged_in and (download_profile_pic or download_profile_posts):
if instaloader.context.is_logged_in and (download_profile_pic or download_posts or
download_tagged):
instaloader.context.log(err)
instaloader.context.log("Trying again anonymously, helps in case you are just blocked.")
with instaloader.anonymous_copy() as anonymous_loader:
with instaloader.context.error_catcher():
anonymous_retry_profiles.add(anonymous_loader.check_profile_id(target))
instaloader.context.log("Looks good.")
instaloader.context.error("Warning: {} will be downloaded anonymously (\"{}\")."
.format(target, err))
else:
raise
if len(profiles) > 1:
instaloader.context.log("Downloading {} profiles: {}".format(len(profiles),
' '.join([p.username for p in profiles])))
if download_profile_pic or download_profile_posts:
# Iterate through profiles list and download them
for target in profiles:
with instaloader.context.error_catcher(target):
instaloader.download_profile(target, download_profile_pic, not download_profile_posts,
fast_update, download_tagged=tagged,
download_tagged_only=tagged_only, post_filter=post_filter)
if anonymous_retry_profiles:
instaloader.context.log("Downloading anonymously: {}"
.format(' '.join([p.username for p in anonymous_retry_profiles])))
with instaloader.anonymous_copy() as anonymous_loader:
for target in anonymous_retry_profiles:
with instaloader.context.error_catcher(target):
anonymous_loader.download_profile(target, download_profile_pic, not download_profile_posts,
fast_update, download_tagged=tagged,
download_tagged_only=tagged_only, post_filter=post_filter)
if download_profile_stories and profiles:
with instaloader.context.error_catcher("Download stories"):
instaloader.context.log("Downloading stories")
instaloader.download_stories(userids=list(profiles), fast_update=fast_update,
filename_target=None, storyitem_filter=storyitem_filter)
instaloader.download_profiles(profiles,
download_profile_pic, download_posts, download_tagged, download_stories,
fast_update, post_filter, storyitem_filter)
if anonymous_retry_profiles:
instaloader.context.log("Downloading anonymously: {}"
.format(' '.join([p.username for p in anonymous_retry_profiles])))
with instaloader.anonymous_copy() as anonymous_loader:
anonymous_loader.download_profiles(anonymous_retry_profiles,
download_profile_pic, download_posts, download_tagged,
fast_update=fast_update, post_filter=post_filter)
except KeyboardInterrupt:
print("\nInterrupted by user.", file=sys.stderr)
# Save session if it is useful
@ -240,7 +226,9 @@ def main():
g_prof = parser.add_argument_group("What to Download of each Profile")
g_prof.add_argument('-P', '--profile-pic-only', action='store_true',
help='Only download profile picture.')
help=SUPPRESS)
g_prof.add_argument('--no-posts', action='store_true',
help="Do not download regular posts.")
g_prof.add_argument('--no-profile-pic', action='store_true',
help='Do not download profile picture.')
g_post.add_argument('--no-pictures', action='store_true',
@ -274,12 +262,9 @@ def main():
g_prof.add_argument('-s', '--stories', action='store_true',
help='Also download stories of each profile that is downloaded. Requires --login.')
g_prof.add_argument('--stories-only', action='store_true',
help='Rather than downloading regular posts of each specified profile, only download '
'stories. Requires --login. Does not imply --no-profile-pic.')
help=SUPPRESS)
g_prof.add_argument('--tagged', action='store_true',
help='Also download posts where each profile is tagged.')
g_prof.add_argument('--tagged-only', action='store_true',
help='Download only post where each profile is tagged, not their regular posts.')
g_cond = parser.add_argument_group("Which Posts to Download")
@ -369,6 +354,11 @@ def main():
if args.no_pictures and args.fast_update:
raise SystemExit('--no-pictures and --fast-update cannot be used together.')
# Determine what to download
download_profile_pic = not args.no_profile_pic or args.profile_pic_only
download_posts = not (args.no_posts or args.stories_only or args.profile_pic_only)
download_stories = args.stories or args.stories_only
loader = Instaloader(sleep=not args.no_sleep, quiet=args.quiet, user_agent=args.user_agent,
dirname_pattern=args.dirname_pattern, filename_pattern=args.filename_pattern,
download_pictures=not args.no_pictures,
@ -385,14 +375,12 @@ def main():
username=args.login.lower() if args.login is not None else None,
password=args.password,
sessionfile=args.sessionfile,
max_count=int(args.count) if args.count is not None else None,
profile_pic=not args.no_profile_pic,
profile_pic_only=args.profile_pic_only,
download_profile_pic=download_profile_pic,
download_posts=download_posts,
download_stories=download_stories,
download_tagged=args.tagged,
fast_update=args.fast_update,
stories=args.stories,
stories_only=args.stories_only,
tagged=args.tagged,
tagged_only=args.tagged_only,
max_count=int(args.count) if args.count is not None else None,
post_filter_str=args.post_filter,
storyitem_filter_str=args.storyitem_filter)
loader.close()

View File

@ -11,7 +11,7 @@ from contextlib import contextmanager, suppress
from datetime import datetime, timezone
from functools import wraps
from io import BytesIO
from typing import Any, Callable, Iterator, List, Optional, Union
from typing import Any, Callable, Iterator, List, Optional, Set, Union
from .exceptions import *
from .instaloadercontext import InstaloaderContext
@ -707,6 +707,72 @@ class Instaloader:
return profile
raise ProfileNotExistsException("Profile {0} does not exist.".format(profile_name))
def download_profiles(self, profiles: Set[Profile],
profile_pic: bool = True, posts: bool = True, tagged: bool = False, stories: bool = False,
fast_update: bool = False,
post_filter: Optional[Callable[[Post], bool]] = None,
storyitem_filter: Optional[Callable[[Post], bool]] = None):
"""High-level method to download set of profiles.
:param profiles: Set of profiles to download.
:param profile_pic: not :option:`--no-profile-pic`.
:param posts: not :option:`--no-posts`.
:param tagged: :option:`--tagged`.
:param stories: :option:`--stories`.
:param fast_update: :option:`--fast-update`.
:param post_filter: :option:`--post-filter`.
:param storyitem_filter: :option:`--post-filter`."""
for profile in profiles:
with self.context.error_catcher(profile.username):
profile_name = profile.username
# Save metadata as JSON if desired.
if self.save_metadata:
json_filename = '{0}/{1}_{2}'.format(self.dirname_pattern.format(profile=profile_name,
target=profile_name),
profile_name, profile.userid)
self.save_metadata_json(json_filename, profile)
# Download profile picture
if profile_pic:
with self.context.error_catcher('Download profile picture of {}'.format(profile_name)):
self.download_profilepic(profile)
# Catch some errors
if profile.is_private:
if not self.context.is_logged_in:
raise LoginRequiredException("--login=USERNAME required.")
if not profile.followed_by_viewer and self.context.username != profile.username:
raise PrivateProfileNotFollowedException("Private but not followed.")
# Download tagged, if requested
if tagged:
with self.context.error_catcher('Download tagged of {}'.format(profile_name)):
self.download_tagged(profile, fast_update=fast_update, post_filter=post_filter)
# Iterate over pictures and download them
if posts:
self.context.log("Retrieving posts from profile {}.".format(profile_name))
totalcount = profile.mediacount
count = 1
for post in profile.get_posts():
self.context.log("[%3i/%3i] " % (count, totalcount), end="", flush=True)
count += 1
if post_filter is not None and not post_filter(post):
self.context.log('<skipped>')
continue
with self.context.error_catcher("Download {} of {}".format(post, profile_name)):
downloaded = self.download_post(post, target=profile_name)
if fast_update and not downloaded:
break
if stories and profiles:
with self.context.error_catcher("Download stories"):
self.context.log("Downloading stories")
self.download_stories(userids=list(profiles), fast_update=fast_update, filename_target=None,
storyitem_filter=storyitem_filter)
def download_profile(self, profile_name: Union[str, Profile],
profile_pic: bool = True, profile_pic_only: bool = False,
fast_update: bool = False,
@ -714,7 +780,11 @@ class Instaloader:
download_tagged: bool = False, download_tagged_only: bool = False,
post_filter: Optional[Callable[[Post], bool]] = None,
storyitem_filter: Optional[Callable[[StoryItem], bool]] = None) -> None:
"""Download one profile"""
"""Download one profile
.. deprecated:: 4.1
Use :meth:`Instaloader.download_profiles`.
"""
# Get profile main page json
# check if profile does exist or name has changed since last download