Merge branch 'master' into v4-dev
This commit is contained in:
commit
a492f0a6d1
@ -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`
|
||||||
--------------------------------
|
--------------------------------
|
||||||
|
@ -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
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
@ -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
|
||||||
===========
|
===========
|
||||||
|
@ -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/.")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user