Merge branch 'master' into v4-dev

This commit is contained in:
Alexander Graf 2018-03-22 16:10:35 +01:00
commit a492f0a6d1
4 changed files with 47 additions and 15 deletions

View File

@ -1,7 +1,7 @@
.. meta:: .. meta::
:description: :description:
Instaloader can also be used as a powerful and easy-to-use Documentation of Instaloader module, a powerful and easy-to-use
Python API for Instagram, allowing to download media and metadata. Python library to download Instagram media and metadata.
Python Module :mod:`instaloader` Python Module :mod:`instaloader`
-------------------------------- --------------------------------

View File

@ -1,3 +1,10 @@
.. meta::
:description:
How to download pictures from Instagram. Description of basic
usage of Instaloader, free tool to download photos from public
and private profiles, hashtags, stories, feeds, saved media, and
their metadata, comments and captions.
Download Pictures from Instagram Download Pictures from Instagram
--------------------------------- ---------------------------------

View File

@ -1,8 +1,9 @@
.. meta:: .. meta::
:description: :description:
Command line tool to download pictures (and videos) from Instagram. Free command line tool to download photos from Instagram.
Instaloader downloads public and private profiles, hashtags, user stories, Scrapes public and private profiles, hashtags, stories, feeds,
feeds, saved media, comments, geotags, captions and other metadata of each post. saved media, and their metadata, comments and captions.
Written in Python.
Instaloader Instaloader
=========== ===========

View File

@ -7,6 +7,7 @@ import getpass
import json import json
import os import os
import pickle import pickle
import platform
import random import random
import re import re
import shutil import shutil
@ -30,8 +31,14 @@ import requests.utils
import urllib3 import urllib3
__version__ = '3.3.1' __version__ = '3.3.3'
# NOTE: duplicated in README.rst and docs/index.rst
USAGE_STRING = """
{0} [--comments] [--geotags] [--stories]
[--login YOUR-USERNAME] [--fast-update]
profile | "#hashtag" | :stories | :feed | :saved
{0} --help""".format(sys.argv[0])
try: try:
# pylint:disable=wrong-import-position # pylint:disable=wrong-import-position
@ -128,10 +135,10 @@ def mediaid_to_shortcode(mediaid: int) -> str:
return b64encode(mediaid.to_bytes(9, 'big'), b'-_').decode().replace('A', ' ').lstrip().replace(' ','A') return b64encode(mediaid.to_bytes(9, 'big'), b'-_').decode().replace('A', ' ').lstrip().replace(' ','A')
def format_string_contains_key(format_string: str, key: str) -> bool: def format_string_contains_key(format_string: '_PathPattern', key: str) -> bool:
# pylint:disable=unused-variable # pylint:disable=unused-variable
for literal_text, field_name, format_spec, conversion in string.Formatter().parse(format_string): for literal_text, field_name, format_spec, conversion in string.Formatter().parse(format_string):
if field_name == key or field_name.startswith(key + '.'): if field_name and (field_name == key or field_name.startswith(key + '.')):
return True return True
return False return False
@ -606,6 +613,14 @@ def _requires_login(func: Callable) -> Callable:
return call return call
class _PathPattern(str):
"""Class overriding :meth:`str.format` for character substitution in paths for Windows, see issue #84."""
def format(self, *args: Any, **kwargs: Any) -> str:
ret = super().format(*args, **kwargs)
return ret.replace(':', '\ua789') if platform.system() == 'Windows' else ret
class Instaloader: class Instaloader:
GRAPHQL_PAGE_LENGTH = 200 GRAPHQL_PAGE_LENGTH = 200
@ -629,18 +644,19 @@ class Instaloader:
self.username = None self.username = None
self.sleep = sleep self.sleep = sleep
self.quiet = quiet self.quiet = quiet
self.dirname_pattern = dirname_pattern if dirname_pattern is not None else '{target}' self.dirname_pattern = _PathPattern(dirname_pattern if dirname_pattern is not None else '{target}')
if filename_pattern is not None: if filename_pattern is not None:
filename_pattern = re.sub(r"({(?:post\.)?date)([:}])", r"\1_utc\2", filename_pattern) filename_pattern = re.sub(r"({(?:post\.)?date)([:}])", r"\1_utc\2", filename_pattern)
self.filename_pattern_old = filename_pattern.replace('{date_utc}', '{date_utc:%Y-%m-%d_%H-%M-%S}') self.filename_pattern_old = filename_pattern.replace('{date_utc}', '{date_utc:%Y-%m-%d_%H-%M-%S}')
self.filename_pattern_old = re.sub(r"(?i)({(?:post\.)?date_utc:[^}]*?)_UTC", self.filename_pattern_old = _PathPattern(re.sub(r"(?i)({(?:post\.)?date_utc:[^}]*?)_UTC",
r"\1", self.filename_pattern_old) r"\1", self.filename_pattern_old))
filename_pattern = re.sub(r"(?i)({(date_utc|post\.date_utc):(?![^}]*UTC[^}]*).*?)}", filename_pattern = re.sub(r"(?i)({(date_utc|post\.date_utc):(?![^}]*UTC[^}]*).*?)}",
r"\1_UTC}", filename_pattern) r"\1_UTC}", filename_pattern)
self.filename_pattern = filename_pattern.replace('{date_utc}', '{date_utc:%Y-%m-%d_%H-%M-%S_UTC}') self.filename_pattern = _PathPattern(filename_pattern.replace('{date_utc}',
'{date_utc:%Y-%m-%d_%H-%M-%S_UTC}'))
else: else:
self.filename_pattern = '{date_utc:%Y-%m-%d_%H-%M-%S_UTC}' self.filename_pattern = _PathPattern('{date_utc:%Y-%m-%d_%H-%M-%S_UTC}')
self.filename_pattern_old = '{date_utc:%Y-%m-%d_%H-%M-%S}' self.filename_pattern_old = _PathPattern('{date_utc:%Y-%m-%d_%H-%M-%S}')
self.download_videos = download_videos self.download_videos = download_videos
self.download_video_thumbnails = download_video_thumbnails self.download_video_thumbnails = download_video_thumbnails
self.download_geotags = download_geotags self.download_geotags = download_geotags
@ -1651,6 +1667,14 @@ class Instaloader:
# Save session if it is useful # Save session if it is useful
if self.is_logged_in: if self.is_logged_in:
self.save_session_to_file(sessionfile) self.save_session_to_file(sessionfile)
# User might be confused if Instaloader does nothing
if not targetlist:
if self.is_logged_in:
# Instaloader did at least save a session file
self._log("No targets were specified, thus nothing has been downloaded.")
else:
# Instloader did not do anything
self._log("usage:"+USAGE_STRING)
if self.error_log: if self.error_log:
print("\nErrors occured:", file=sys.stderr) print("\nErrors occured:", file=sys.stderr)
for err in self.error_log: for err in self.error_log:
@ -1658,7 +1682,7 @@ class Instaloader:
def main(): def main():
parser = ArgumentParser(description=__doc__, add_help=False, parser = ArgumentParser(description=__doc__, add_help=False, usage=USAGE_STRING,
epilog="Report issues at https://github.com/instaloader/instaloader/issues. " epilog="Report issues at https://github.com/instaloader/instaloader/issues. "
"The complete documentation can be found at " "The complete documentation can be found at "
"https://instaloader.github.io/.") "https://instaloader.github.io/.")