Update as-module.rst and docstrings
This commit is contained in:
parent
b9ac40899d
commit
2c50972e08
@ -10,6 +10,9 @@ Python Module :mod:`instaloader`
|
|||||||
|
|
||||||
.. highlight:: python
|
.. highlight:: python
|
||||||
|
|
||||||
|
.. contents::
|
||||||
|
:backlinks: none
|
||||||
|
|
||||||
Instaloader exposes its internally used methods as a Python module, making it a
|
Instaloader exposes its internally used methods as a Python module, making it a
|
||||||
**powerful and easy-to-use Python API for Instagram**, allowing to further
|
**powerful and easy-to-use Python API for Instagram**, allowing to further
|
||||||
customize obtaining media and metadata.
|
customize obtaining media and metadata.
|
||||||
@ -42,34 +45,8 @@ Besides :func:`Instaloader.get_hashtag_posts`, there is
|
|||||||
Also, :class:`Post` instances can be created with :func:`Post.from_shortcode`
|
Also, :class:`Post` instances can be created with :func:`Post.from_shortcode`
|
||||||
and :func:`Post.from_mediaid`.
|
and :func:`Post.from_mediaid`.
|
||||||
|
|
||||||
Further, information about profiles can be easily obtained. For example, you may
|
|
||||||
print a list of all followees and followers of a profile with::
|
|
||||||
|
|
||||||
# Print followees
|
|
||||||
print(PROFILE + " follows these profiles:")
|
|
||||||
for f in L.get_followees(PROFILE):
|
|
||||||
print("\t%s\t%s" % (f['username'], f['full_name']))
|
|
||||||
|
|
||||||
# Print followers
|
|
||||||
print("Followers of " + PROFILE + ":")
|
|
||||||
for f in L.get_followers(PROFILE):
|
|
||||||
print("\t%s\t%s" % (f['username'], f['full_name']))
|
|
||||||
|
|
||||||
Then, you may download all pictures of all followees with::
|
|
||||||
|
|
||||||
for f in L.get_followees(PROFILE):
|
|
||||||
L.download_profile(f['username'])
|
|
||||||
|
|
||||||
Each Instagram profile has its own unique ID which stays unmodified even if a
|
|
||||||
user changes his/her username. To get said ID, given the profile's name, you may
|
|
||||||
call::
|
|
||||||
|
|
||||||
L.get_id_by_username(PROFILE_NAME)
|
|
||||||
|
|
||||||
A reference of the many methods provided by the :mod:`instaloader` module is
|
A reference of the many methods provided by the :mod:`instaloader` module is
|
||||||
provided in the remainder of this document. Feel free to direct any issue or
|
provided in the remainder of this document.
|
||||||
contribution to
|
|
||||||
`Instaloader on Github <https://github.com/instaloader/instaloader>`__.
|
|
||||||
|
|
||||||
``Instaloader`` (Main Class)
|
``Instaloader`` (Main Class)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -77,24 +54,34 @@ contribution to
|
|||||||
.. autoclass:: Instaloader
|
.. autoclass:: Instaloader
|
||||||
:no-show-inheritance:
|
:no-show-inheritance:
|
||||||
|
|
||||||
``Profile`` Class
|
Instagram Structures
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
.. autoclass:: Profile
|
.. autofunction:: load_structure_from_file
|
||||||
:no-show-inheritance:
|
|
||||||
|
|
||||||
``Post`` Class
|
.. autofunction:: save_structure_to_file
|
||||||
^^^^^^^^^^^^^^
|
|
||||||
|
``Post``
|
||||||
|
""""""""
|
||||||
|
|
||||||
|
.. autofunction:: mediaid_to_shortcode
|
||||||
|
|
||||||
|
.. autofunction:: shortcode_to_mediaid
|
||||||
|
|
||||||
.. autoclass:: Post
|
.. autoclass:: Post
|
||||||
:no-show-inheritance:
|
:no-show-inheritance:
|
||||||
|
|
||||||
Miscellaneous Functions
|
``StoryItem``
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
"""""""""""""
|
||||||
|
|
||||||
.. autofunction:: shortcode_to_mediaid
|
.. autoclass:: StoryItem
|
||||||
|
:no-show-inheritance:
|
||||||
|
|
||||||
.. autofunction:: mediaid_to_shortcode
|
``Profile``
|
||||||
|
"""""""""""
|
||||||
|
|
||||||
|
.. autoclass:: Profile
|
||||||
|
:no-show-inheritance:
|
||||||
|
|
||||||
Exceptions
|
Exceptions
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^
|
||||||
@ -102,6 +89,8 @@ Exceptions
|
|||||||
.. autoexception:: InstaloaderException
|
.. autoexception:: InstaloaderException
|
||||||
:no-show-inheritance:
|
:no-show-inheritance:
|
||||||
|
|
||||||
|
.. autoexception:: QueryReturnedBadRequestException
|
||||||
|
|
||||||
.. autoexception:: QueryReturnedNotFoundException
|
.. autoexception:: QueryReturnedNotFoundException
|
||||||
|
|
||||||
.. autoexception:: QueryReturnedForbiddenException
|
.. autoexception:: QueryReturnedForbiddenException
|
||||||
@ -123,3 +112,9 @@ Exceptions
|
|||||||
.. autoexception:: ConnectionException
|
.. autoexception:: ConnectionException
|
||||||
|
|
||||||
.. autoexception:: TooManyRequestsException
|
.. autoexception:: TooManyRequestsException
|
||||||
|
|
||||||
|
``InstaloaderContext`` (Low-level functions)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. autoclass:: InstaloaderContext
|
||||||
|
:no-show-inheritance:
|
||||||
|
@ -369,13 +369,5 @@ current_release_date = subprocess.check_output(["git", "log", "-1", "--tags", "-
|
|||||||
html_context = {'current_release': current_release, 'current_release_date': current_release_date}
|
html_context = {'current_release': current_release, 'current_release_date': current_release_date}
|
||||||
|
|
||||||
|
|
||||||
def skip(app, what, name, obj, skip, options):
|
|
||||||
# Ensure constructors are documented
|
|
||||||
if name == "__init__":
|
|
||||||
return False
|
|
||||||
return skip
|
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
app.connect('autodoc-skip-member', skip)
|
|
||||||
app.add_stylesheet("style.css")
|
app.add_stylesheet("style.css")
|
||||||
|
@ -14,5 +14,6 @@ else:
|
|||||||
|
|
||||||
from .exceptions import *
|
from .exceptions import *
|
||||||
from .instaloader import Instaloader
|
from .instaloader import Instaloader
|
||||||
|
from .instaloadercontext import InstaloaderContext
|
||||||
from .structures import (Post, Profile, Story, StoryItem, load_structure_from_file, mediaid_to_shortcode,
|
from .structures import (Post, Profile, Story, StoryItem, load_structure_from_file, mediaid_to_shortcode,
|
||||||
save_structure_to_file, shortcode_to_mediaid)
|
save_structure_to_file, shortcode_to_mediaid)
|
||||||
|
@ -71,6 +71,36 @@ class _PostPathFormatter(_ArbitraryItemFormatter):
|
|||||||
|
|
||||||
|
|
||||||
class Instaloader:
|
class Instaloader:
|
||||||
|
"""Instaloader Class.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
L = Instaloader()
|
||||||
|
|
||||||
|
# Optionally, login or load session
|
||||||
|
L.login(USER, PASSWORD) # (login)
|
||||||
|
L.interactive_login(USER) # (ask password on terminal)
|
||||||
|
L.load_session_from_file(USER) # (load session created w/
|
||||||
|
# `instaloader -l USERNAME`)
|
||||||
|
|
||||||
|
:mod:`instaloader` provides the :class:`Post` structure, which represents a
|
||||||
|
picture, video or sidecar (set of multiple pictures/videos) posted in a user's
|
||||||
|
profile. :class:`Instaloader` provides methods to iterate over Posts from a
|
||||||
|
certain source::
|
||||||
|
|
||||||
|
for post in L.get_hashtag_posts('cat'):
|
||||||
|
# post is an instance of Post
|
||||||
|
L.download_post(post, target='#cat')
|
||||||
|
|
||||||
|
Besides :func:`Instaloader.get_hashtag_posts`, there is
|
||||||
|
:func:`Instaloader.get_feed_posts`, :func:`Profile.get_posts` and
|
||||||
|
:func:`Profile.get_saved_posts`.
|
||||||
|
Also, :class:`Post` instances can be created with :func:`Post.from_shortcode`
|
||||||
|
and :func:`Post.from_mediaid`.
|
||||||
|
|
||||||
|
Also, this class provides methods :meth:`Instaloader.download_profile`,
|
||||||
|
:meth:`Instaloader.download_hashtag` and many more to download targets.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
sleep: bool = True, quiet: bool = False,
|
sleep: bool = True, quiet: bool = False,
|
||||||
@ -119,6 +149,7 @@ class Instaloader:
|
|||||||
new_loader.close()
|
new_loader.close()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
"""Close associated session objects and repeat error log."""
|
||||||
self.context.close()
|
self.context.close()
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
@ -176,7 +207,7 @@ class Instaloader:
|
|||||||
self.context.log('comments', end=' ', flush=True)
|
self.context.log('comments', end=' ', flush=True)
|
||||||
|
|
||||||
def save_caption(self, filename: str, mtime: datetime, caption: str) -> None:
|
def save_caption(self, filename: str, mtime: datetime, caption: str) -> None:
|
||||||
"""Updates picture caption"""
|
"""Updates picture caption / Post metadata info"""
|
||||||
filename += '.txt'
|
filename += '.txt'
|
||||||
caption += '\n'
|
caption += '\n'
|
||||||
pcaption = caption.replace('\n', ' ').strip()
|
pcaption = caption.replace('\n', ' ').strip()
|
||||||
@ -214,6 +245,7 @@ class Instaloader:
|
|||||||
os.utime(filename, (datetime.now().timestamp(), mtime.timestamp()))
|
os.utime(filename, (datetime.now().timestamp(), mtime.timestamp()))
|
||||||
|
|
||||||
def save_location(self, filename: str, location_json: Dict[str, str], mtime: datetime) -> None:
|
def save_location(self, filename: str, location_json: Dict[str, str], mtime: datetime) -> None:
|
||||||
|
"""Save post location name and Google Maps link."""
|
||||||
filename += '_location.txt'
|
filename += '_location.txt'
|
||||||
location_string = (location_json["name"] + "\n" +
|
location_string = (location_json["name"] + "\n" +
|
||||||
"https://maps.google.com/maps?q={0},{1}&ll={0},{1}\n".format(location_json["lat"],
|
"https://maps.google.com/maps?q={0},{1}&ll={0},{1}\n".format(location_json["lat"],
|
||||||
@ -250,7 +282,10 @@ class Instaloader:
|
|||||||
|
|
||||||
@_requires_login
|
@_requires_login
|
||||||
def save_session_to_file(self, filename: Optional[str] = None) -> None:
|
def save_session_to_file(self, filename: Optional[str] = None) -> None:
|
||||||
"""Saves internally stored :class:`requests.Session` object."""
|
"""Saves internally stored :class:`requests.Session` object.
|
||||||
|
|
||||||
|
:param filename: Filename, or None to use default filename.
|
||||||
|
"""
|
||||||
if filename is None:
|
if filename is None:
|
||||||
filename = get_default_session_filename(self.context.username)
|
filename = get_default_session_filename(self.context.username)
|
||||||
dirname = os.path.dirname(filename)
|
dirname = os.path.dirname(filename)
|
||||||
@ -437,7 +472,10 @@ class Instaloader:
|
|||||||
|
|
||||||
@_requires_login
|
@_requires_login
|
||||||
def get_feed_posts(self) -> Iterator[Post]:
|
def get_feed_posts(self) -> Iterator[Post]:
|
||||||
"""Get Posts of the user's feed."""
|
"""Get Posts of the user's feed.
|
||||||
|
|
||||||
|
:return: Iterator over Posts of the user's feed.
|
||||||
|
"""
|
||||||
|
|
||||||
data = self.context.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})["data"]
|
data = self.context.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})["data"]
|
||||||
|
|
||||||
@ -514,7 +552,10 @@ class Instaloader:
|
|||||||
|
|
||||||
@_requires_login
|
@_requires_login
|
||||||
def get_explore_posts(self) -> Iterator[Post]:
|
def get_explore_posts(self) -> Iterator[Post]:
|
||||||
"""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.
|
||||||
|
"""
|
||||||
data = self.context.get_json('explore/', {})
|
data = self.context.get_json('explore/', {})
|
||||||
yield from (Post(self.context, node)
|
yield from (Post(self.context, node)
|
||||||
for node in self.context.graphql_node_list("df0dcc250c2b18d9fd27c5581ef33c7c",
|
for node in self.context.graphql_node_list("df0dcc250c2b18d9fd27c5581ef33c7c",
|
||||||
|
@ -139,9 +139,11 @@ class InstaloaderContext:
|
|||||||
return session
|
return session
|
||||||
|
|
||||||
def save_session_to_file(self, sessionfile):
|
def save_session_to_file(self, sessionfile):
|
||||||
|
"""Not meant to be used directly, use :meth:`Instaloader.save_session_to_file`."""
|
||||||
pickle.dump(requests.utils.dict_from_cookiejar(self._session.cookies), sessionfile)
|
pickle.dump(requests.utils.dict_from_cookiejar(self._session.cookies), sessionfile)
|
||||||
|
|
||||||
def load_session_from_file(self, username, sessionfile):
|
def load_session_from_file(self, username, sessionfile):
|
||||||
|
"""Not meant to be used directly, use :meth:`Instaloader.load_session_to_file`."""
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
session.cookies = requests.utils.cookiejar_from_dict(pickle.load(sessionfile))
|
session.cookies = requests.utils.cookiejar_from_dict(pickle.load(sessionfile))
|
||||||
session.headers.update(self._default_http_header())
|
session.headers.update(self._default_http_header())
|
||||||
@ -150,10 +152,12 @@ class InstaloaderContext:
|
|||||||
self.username = username
|
self.username = username
|
||||||
|
|
||||||
def test_login(self) -> Optional[str]:
|
def test_login(self) -> Optional[str]:
|
||||||
|
"""Not meant to be used directly, use :meth:`Instaloader.test_login`."""
|
||||||
data = self.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})
|
data = self.graphql_query("d6f4427fbe92d846298cf93df0b937d3", {})
|
||||||
return data["data"]["user"]["username"] if data["data"]["user"] is not None else None
|
return data["data"]["user"]["username"] if data["data"]["user"] is not None else None
|
||||||
|
|
||||||
def login(self, user, passwd):
|
def login(self, user, passwd):
|
||||||
|
"""Not meant to be used directly, use :meth:`Instaloader.login`."""
|
||||||
session = requests.Session()
|
session = requests.Session()
|
||||||
session.cookies.update({'sessionid': '', 'mid': '', 'ig_pr': '1',
|
session.cookies.update({'sessionid': '', 'mid': '', 'ig_pr': '1',
|
||||||
'ig_vw': '1920', 'csrftoken': '',
|
'ig_vw': '1920', 'csrftoken': '',
|
||||||
|
@ -28,23 +28,26 @@ class Post:
|
|||||||
Structure containing information about an Instagram post.
|
Structure containing information about an Instagram post.
|
||||||
|
|
||||||
Created by methods :meth:`Profile.get_posts`, :meth:`Instaloader.get_hashtag_posts`,
|
Created by methods :meth:`Profile.get_posts`, :meth:`Instaloader.get_hashtag_posts`,
|
||||||
:meth:`Instaloader.get_feed_posts` and :meth:`Profile.get_saved_posts`.
|
:meth:`Instaloader.get_feed_posts` and :meth:`Profile.get_saved_posts`, which return iterators of Posts::
|
||||||
|
|
||||||
|
L = Instaloader()
|
||||||
|
for post in L.get_hashtag_posts(HASHTAG):
|
||||||
|
L.download_post(post, target='#'+HASHTAG)
|
||||||
|
|
||||||
|
Might also be created with::
|
||||||
|
|
||||||
|
post = Post.from_shortcode(L.context, SHORTCODE)
|
||||||
|
|
||||||
This class unifies access to the properties associated with a post. It implements == and is
|
This class unifies access to the properties associated with a post. It implements == and is
|
||||||
hashable.
|
hashable.
|
||||||
|
|
||||||
The properties defined here are accessible by the filter expressions specified with the :option:`--only-if`
|
:param context: :attr:`Instaloader.context` used for additional queries if neccessary..
|
||||||
parameter and exported into JSON files with :option:`--metadata-json`.
|
:param node: Node structure, as returned by Instagram.
|
||||||
|
:param owner_profile: The Profile of the owner, if already known at creation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, context: InstaloaderContext, node: Dict[str, Any],
|
def __init__(self, context: InstaloaderContext, node: Dict[str, Any],
|
||||||
owner_profile: Optional['Profile'] = None):
|
owner_profile: Optional['Profile'] = None):
|
||||||
"""Create a Post instance from a node structure as returned by Instagram.
|
|
||||||
|
|
||||||
:param context: :class:`InstaloaderContext` instance used for additional queries if neccessary.
|
|
||||||
:param node: Node structure, as returned by Instagram.
|
|
||||||
:param owner_profile: The Profile of the owner, if already known at creation.
|
|
||||||
"""
|
|
||||||
|
|
||||||
assert 'shortcode' in node or 'code' in node
|
assert 'shortcode' in node or 'code' in node
|
||||||
|
|
||||||
self._context = context
|
self._context = context
|
||||||
@ -296,7 +299,7 @@ class Post:
|
|||||||
self._rhx_gis)
|
self._rhx_gis)
|
||||||
|
|
||||||
def get_location(self) -> Optional[Dict[str, str]]:
|
def get_location(self) -> Optional[Dict[str, str]]:
|
||||||
"""If the Post has a location, returns a dictionary with fields 'lat' and 'lng'."""
|
"""If the Post has a location, returns a dictionary with fields 'lat' and 'lng' and 'name'."""
|
||||||
loc_dict = self._field("location")
|
loc_dict = self._field("location")
|
||||||
if loc_dict is not None:
|
if loc_dict is not None:
|
||||||
location_json = self._context.get_json("explore/locations/{0}/".format(loc_dict["id"]),
|
location_json = self._context.get_json("explore/locations/{0}/".format(loc_dict["id"]),
|
||||||
@ -311,7 +314,25 @@ class Profile:
|
|||||||
Provides methods for accessing profile properties, as well as :meth:`Profile.get_posts` and for own profile
|
Provides methods for accessing profile properties, as well as :meth:`Profile.get_posts` and for own profile
|
||||||
:meth:`Profile.get_saved_posts`.
|
:meth:`Profile.get_saved_posts`.
|
||||||
|
|
||||||
This class implements == and is hashable.
|
Get instances with :meth:`Post.owner_profile`, :meth:`StoryItem.owner_profile`, :meth:`Profile.get_followees`,
|
||||||
|
:meth:`Profile.get_followers` or::
|
||||||
|
|
||||||
|
L = Instaloader()
|
||||||
|
profile = Profile.from_username(L.context, USERNAME)
|
||||||
|
|
||||||
|
Provides :meth:`Profile.get_posts` and for own profile :meth:`Profile.get_saved_posts` to iterate over associated
|
||||||
|
:class:`Post` objects::
|
||||||
|
|
||||||
|
for post in profile.get_posts():
|
||||||
|
L.download_post(post, target=profile.username)
|
||||||
|
|
||||||
|
:meth:`Profile.get_followees` and :meth:`Profile.get_followers`::
|
||||||
|
|
||||||
|
print("{} follows these profiles:".format(profile.username))
|
||||||
|
for followee in profile.get_followees():
|
||||||
|
print(followee.username)
|
||||||
|
|
||||||
|
Also, this class implements == and is hashable.
|
||||||
"""
|
"""
|
||||||
def __init__(self, context: InstaloaderContext, node: Dict[str, Any]):
|
def __init__(self, context: InstaloaderContext, node: Dict[str, Any]):
|
||||||
assert 'username' in node
|
assert 'username' in node
|
||||||
@ -321,6 +342,12 @@ class Profile:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_username(cls, context: InstaloaderContext, username: str):
|
def from_username(cls, context: InstaloaderContext, username: str):
|
||||||
|
"""Create a Profile instance from a given username, raise exception if it does not exist.
|
||||||
|
|
||||||
|
:param context: :attr:`Instaloader.context`
|
||||||
|
:param username: Username
|
||||||
|
:raises: :class:`ProfileNotExistsException`
|
||||||
|
"""
|
||||||
# pylint:disable=protected-access
|
# pylint:disable=protected-access
|
||||||
profile = cls(context, {'username': username.lower()})
|
profile = cls(context, {'username': username.lower()})
|
||||||
profile._obtain_metadata() # to raise ProfileNotExistException now in case username is invalid
|
profile._obtain_metadata() # to raise ProfileNotExistException now in case username is invalid
|
||||||
@ -328,6 +355,13 @@ class Profile:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_id(cls, context: InstaloaderContext, profile_id: int):
|
def from_id(cls, context: InstaloaderContext, profile_id: int):
|
||||||
|
"""If logged in, create a Profile instance from a given userid. If possible, use :meth:`Profile.from_username`
|
||||||
|
or constructor directly rather than this method, since does many requests.
|
||||||
|
|
||||||
|
:param context: :attr:`Instaloader.context`
|
||||||
|
:param profile_id: userid
|
||||||
|
:raises: :class:`ProfileNotExistsException`, :class:`LoginRequiredException`, :class:`ProfileHasNoPicsException`
|
||||||
|
"""
|
||||||
if not context.is_logged_in:
|
if not context.is_logged_in:
|
||||||
raise LoginRequiredException("--login=USERNAME required to obtain profile metadata from its ID number.")
|
raise LoginRequiredException("--login=USERNAME required to obtain profile metadata from its ID number.")
|
||||||
data = context.graphql_query("472f257a40c653c64c666ce877d59d2b",
|
data = context.graphql_query("472f257a40c653c64c666ce877d59d2b",
|
||||||
@ -377,10 +411,12 @@ class Profile:
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def userid(self) -> int:
|
def userid(self) -> int:
|
||||||
|
"""User ID"""
|
||||||
return int(self._metadata('id'))
|
return int(self._metadata('id'))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def username(self) -> str:
|
def username(self) -> str:
|
||||||
|
"""Profile Name"""
|
||||||
return self._metadata('username').lower()
|
return self._metadata('username').lower()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
@ -478,8 +514,7 @@ class Profile:
|
|||||||
def get_followers(self) -> Iterator['Profile']:
|
def get_followers(self) -> Iterator['Profile']:
|
||||||
"""
|
"""
|
||||||
Retrieve list of followers of given profile.
|
Retrieve list of followers of given profile.
|
||||||
To use this, one needs to be logged in and private profiles has to be followed,
|
To use this, one needs to be logged in and private profiles has to be followed.
|
||||||
otherwise this returns an empty list.
|
|
||||||
"""
|
"""
|
||||||
if not self._context.is_logged_in:
|
if not self._context.is_logged_in:
|
||||||
raise LoginRequiredException("--login required to get a profile's followers.")
|
raise LoginRequiredException("--login required to get a profile's followers.")
|
||||||
@ -494,8 +529,7 @@ class Profile:
|
|||||||
def get_followees(self) -> Iterator['Profile']:
|
def get_followees(self) -> Iterator['Profile']:
|
||||||
"""
|
"""
|
||||||
Retrieve list of followees (followings) of given profile.
|
Retrieve list of followees (followings) of given profile.
|
||||||
To use this, one needs to be logged in and private profiles has to be followed,
|
To use this, one needs to be logged in and private profiles has to be followed.
|
||||||
otherwise this returns an empty list.
|
|
||||||
"""
|
"""
|
||||||
if not self._context.is_logged_in:
|
if not self._context.is_logged_in:
|
||||||
raise LoginRequiredException("--login required to get a profile's followees.")
|
raise LoginRequiredException("--login required to get a profile's followees.")
|
||||||
@ -712,7 +746,16 @@ class Story:
|
|||||||
JsonExportable = Union[Post, Profile, StoryItem]
|
JsonExportable = Union[Post, Profile, StoryItem]
|
||||||
|
|
||||||
|
|
||||||
def save_structure_to_file(structure: JsonExportable, filename: str):
|
def save_structure_to_file(structure: JsonExportable, filename: str) -> None:
|
||||||
|
"""Saves a :class:`Post`, :class:`Profile` or :class:`StoryItem` to a '.json' or '.json.xz' file such that it can
|
||||||
|
later be loaded by :func:`load_structure_from_file`.
|
||||||
|
|
||||||
|
If the specified filename ends in '.xz', the file will be LZMA compressed. Otherwise, a pretty-printed JSON file
|
||||||
|
will be created.
|
||||||
|
|
||||||
|
:param structure: :class:`Post`, :class:`Profile` or :class:`StoryItem`
|
||||||
|
:param filename: Filename, ends in '.json' or '.json.xz'
|
||||||
|
"""
|
||||||
json_structure = {'node': structure.get_node(),
|
json_structure = {'node': structure.get_node(),
|
||||||
'instaloader': {'version': __version__, 'node_type': structure.__class__.__name__}}
|
'instaloader': {'version': __version__, 'node_type': structure.__class__.__name__}}
|
||||||
compress = filename.endswith('.xz')
|
compress = filename.endswith('.xz')
|
||||||
@ -725,6 +768,12 @@ def save_structure_to_file(structure: JsonExportable, filename: str):
|
|||||||
|
|
||||||
|
|
||||||
def load_structure_from_file(context: InstaloaderContext, filename: str) -> JsonExportable:
|
def load_structure_from_file(context: InstaloaderContext, filename: str) -> JsonExportable:
|
||||||
|
"""Loads a :class:`Post`, :class:`Profile` or :class:`StoryItem` from a '.json' or '.json.xz' file that
|
||||||
|
has been saved by :func:`save_structure_from_file`.
|
||||||
|
|
||||||
|
:param context: :attr:`Instaloader.context` linked to the new object, used for additional queries if neccessary.
|
||||||
|
:param filename: Filename, ends in '.json' or '.json.xz'
|
||||||
|
"""
|
||||||
compressed = filename.endswith('.xz')
|
compressed = filename.endswith('.xz')
|
||||||
if compressed:
|
if compressed:
|
||||||
fp = lzma.open(filename, 'rt')
|
fp = lzma.open(filename, 'rt')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user