mirror of
https://github.com/instaloader/instaloader.git
synced 2026-10-16 21:52:17 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13ebcb782e | ||
|
|
50faad0a04 | ||
|
|
98c2847afd | ||
|
|
be8546812a | ||
|
|
1d506b5f95 | ||
|
|
1036fe45b5 | ||
|
|
bbcebb25eb | ||
|
|
8408183b79 |
@@ -1,103 +0,0 @@
|
||||
# instaloader
|
||||
|
||||
Tool to download pictures (or videos) and captions from Instagram, from a given set
|
||||
of profiles, from your feed or from all followees of a given profile.
|
||||
|
||||
## Usage
|
||||
|
||||
instaloader is written in Python, thus ensure having
|
||||
[Python](https://www.python.org/) (at least version 3.3) installed.
|
||||
|
||||
If you intend to use this tool under Windows, it is highly recommended to first install
|
||||
[win-unicode-console](https://pypi.python.org/pypi/win_unicode_console).
|
||||
|
||||
After having [downloaded instaloader](https://github.com/Thammus/instaloader/releases), unzip it and
|
||||
invoke bundled `setup.py` (requiring [setuptools](https://pypi.python.org/pypi/setuptools)) to
|
||||
install it:
|
||||
```
|
||||
./setup.py install [--user]
|
||||
```
|
||||
(pass `--user` to install it for your user only instead of globally)
|
||||
instaloader requires [python3-requests](https://pypi.python.org/pypi/requests/), which will be
|
||||
installed automatically by setup.py, if not already installed.
|
||||
|
||||
Now, to download a set of profiles, do
|
||||
```
|
||||
instaloader profile [profile ...]
|
||||
```
|
||||
where `profile` is the name of a profile you want to download. Instead of only one profile, you may
|
||||
also specify a list of profiles.
|
||||
|
||||
To later update your local copy of that profile, you may run
|
||||
```
|
||||
instaloader --fast-update profile [profile ...]
|
||||
```
|
||||
When `--fast-update` is given, instaloder terminates when arriving at the first already-downloaded
|
||||
picture.
|
||||
|
||||
Instaloader can also be used to **download private profiles**. To do so, invoke it with
|
||||
```
|
||||
instaloader --login=your_username profile [profile ...]
|
||||
```
|
||||
When invoked like this, it also **stores the session cookies** in a file in `/tmp`, which will be
|
||||
reused later when `--login` is given. So you can download private profiles **non-interactively**
|
||||
when you already have a valid session cookies file.
|
||||
|
||||
If you want to **download all followees of a given profile**, call
|
||||
```
|
||||
instaloader --login=your_username @profile
|
||||
```
|
||||
|
||||
To **download all the pictures which you have liked**, call
|
||||
```
|
||||
instaloader --login=your_username :feed-liked
|
||||
```
|
||||
|
||||
The `--quiet` option makes it also **suitable as a cron job**.
|
||||
|
||||
To get a list of other helpful flags, run `instaloader --help`.
|
||||
|
||||
## Usage as library
|
||||
|
||||
You may also use parts of instaloader as library to do other interesting things.
|
||||
|
||||
For example, to get a list of all followers of a profile as well as their follower count, do
|
||||
```python
|
||||
import instaloader
|
||||
|
||||
# login
|
||||
session = instaloader.get_logged_in_session(USERNAME)
|
||||
|
||||
# get followees
|
||||
followees = instaloader.get_followees(PROFILE, session)
|
||||
for f in followees:
|
||||
print("%i\t%s\t%s" % (f['follower_count'], f['username'], f['full_name']))
|
||||
```
|
||||
|
||||
Then, you may download all pictures of all followees with
|
||||
```python
|
||||
for f in followees:
|
||||
try:
|
||||
instaloader.download(f['username'], session)
|
||||
except instaloader.NonfatalException:
|
||||
pass
|
||||
```
|
||||
|
||||
You could also download your last 20 liked pics with
|
||||
```python
|
||||
instaloader.download_feed_pics(session, max_count=20, fast_update=True,
|
||||
filter_func=lambda node: not node["likes"]["viewer_has_liked"])
|
||||
```
|
||||
|
||||
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
|
||||
```python
|
||||
instaloader.get_id_by_username(PROFILE_NAME)
|
||||
```
|
||||
|
||||
`get_followees()` also returns unique IDs for all loaded followees. To get the current username of a
|
||||
profile, given this unique ID
|
||||
`get_username_by_id()` can be used. For example:
|
||||
```python
|
||||
instaloader.get_username_by_id(session, followees[0]['id'])
|
||||
```
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
Instaloader
|
||||
===========
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
instaloader is written in Python, thus ensure having
|
||||
`Python <https://www.python.org/>`__ (at least version 3.3) installed.
|
||||
|
||||
If you intend to use this tool under Windows, it is recommended
|
||||
to install
|
||||
`win-unicode-console <https://pypi.python.org/pypi/win_unicode_console>`__.
|
||||
|
||||
If you have `pip <https://pypi.python.org/pypi/pip>`__ installed, you
|
||||
may install instaloader using
|
||||
|
||||
::
|
||||
|
||||
pip3 install instaloader
|
||||
|
||||
Alternatively, you may `download instaloader from
|
||||
github <https://github.com/Thammus/instaloader/releases>`__, unzip it
|
||||
and invoke bundled ``setup.py`` (requiring
|
||||
`setuptools <https://pypi.python.org/pypi/setuptools>`__) to install it:
|
||||
|
||||
::
|
||||
|
||||
python3 setup.py install [--user]
|
||||
|
||||
(pass ``--user`` to install it for your user only instead of globally)
|
||||
|
||||
instaloader requires
|
||||
`requests <https://pypi.python.org/pypi/requests>`__, which
|
||||
will be installed automatically by setup.py, if not already installed.
|
||||
|
||||
How to automatically download pictures from Instagram
|
||||
-----------------------------------------------------
|
||||
|
||||
To download a set of profiles, do
|
||||
|
||||
::
|
||||
|
||||
instaloader profile [profile ...]
|
||||
|
||||
where ``profile`` is the name of a profile you want to download. Instead
|
||||
of only one profile, you may also specify a list of profiles.
|
||||
|
||||
To later update your local copy of that profile, you may run
|
||||
|
||||
::
|
||||
|
||||
instaloader --fast-update profile [profile ...]
|
||||
|
||||
When ``--fast-update`` is given, instaloder terminates when arriving at
|
||||
the first already-downloaded picture.
|
||||
|
||||
Instaloader can also be used to **download private profiles**. To do so,
|
||||
invoke it with
|
||||
|
||||
::
|
||||
|
||||
instaloader --login=your_username profile [profile ...]
|
||||
|
||||
When invoked like this, it also **stores the session cookies** in a file
|
||||
in ``/tmp``, which will be reused later when ``--login`` is given. So
|
||||
you can download private profiles **non-interactively** when you already
|
||||
have a valid session cookie file.
|
||||
|
||||
If you want to **download all followees of a given profile**, call
|
||||
|
||||
::
|
||||
|
||||
instaloader --login=your_username @profile
|
||||
|
||||
To **download all the pictures from your feed which you have liked**, call
|
||||
|
||||
::
|
||||
|
||||
instaloader --login=your_username :feed-liked
|
||||
|
||||
The ``--quiet`` option makes it also **suitable as a cron job**.
|
||||
|
||||
To get a list of other helpful flags, run ``instaloader --help``.
|
||||
|
||||
Usage as library
|
||||
----------------
|
||||
|
||||
You may also use parts of instaloader as library to do other interesting
|
||||
things.
|
||||
|
||||
For example, to get a list of all followers of a profile as well as
|
||||
their follower count, do
|
||||
|
||||
.. code:: python
|
||||
|
||||
import instaloader
|
||||
|
||||
# login
|
||||
session = instaloader.get_logged_in_session(USERNAME)
|
||||
|
||||
# get followees
|
||||
followees = instaloader.get_followees(PROFILE, session)
|
||||
for f in followees:
|
||||
print("%i\t%s\t%s" % (f['follower_count'], f['username'], f['full_name']))
|
||||
|
||||
Then, you may download all pictures of all followees with
|
||||
|
||||
.. code:: python
|
||||
|
||||
for f in followees:
|
||||
try:
|
||||
instaloader.download(f['username'], session)
|
||||
except instaloader.NonfatalException:
|
||||
pass
|
||||
|
||||
You could also download your last 20 liked pics with
|
||||
|
||||
.. code:: python
|
||||
|
||||
instaloader.download_feed_pics(session, max_count=20, fast_update=True,
|
||||
filter_func=lambda node: not node["likes"]["viewer_has_liked"])
|
||||
|
||||
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
|
||||
|
||||
.. code:: python
|
||||
|
||||
instaloader.get_id_by_username(PROFILE_NAME)
|
||||
|
||||
``get_followees()`` also returns unique IDs for all loaded followees. To
|
||||
get the current username of a profile, given this unique ID
|
||||
``get_username_by_id()`` can be used. For example:
|
||||
|
||||
.. code:: python
|
||||
|
||||
instaloader.get_username_by_id(session, followees[0]['id'])
|
||||
+46
-18
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
"""Tool to download pictures (or videos) and captions from Instagram, from a given set
|
||||
of profiles, from your feed or from all followees of a given profile."""
|
||||
of profiles (even if private), from your feed or from all followees of a given profile."""
|
||||
|
||||
import re, json, datetime, shutil, os, time, random, sys, pickle, getpass, tempfile
|
||||
from argparse import ArgumentParser
|
||||
@@ -60,7 +60,7 @@ def _log(*msg, sep='', end='\n', flush=False, quiet=False):
|
||||
|
||||
def get_json(name, session, max_id=0, sleep=True):
|
||||
"""Return JSON of a profile"""
|
||||
resp = session.get('http://www.instagram.com/'+name, \
|
||||
resp = session.get('http://www.instagram.com/'+name,
|
||||
params={'max_id': max_id})
|
||||
if sleep:
|
||||
time.sleep(4 * random.random() + 1)
|
||||
@@ -206,7 +206,7 @@ def save_caption(name, date_epoch, caption, shorter_output=False, quiet=False):
|
||||
return None
|
||||
else:
|
||||
def get_filename(index):
|
||||
return filename if index==0 else (filename[:-4] + '_old_' + \
|
||||
return filename if index==0 else (filename[:-4] + '_old_' +
|
||||
(str(0) if index<10 else str()) + str(index) + filename[-4:])
|
||||
i = 0
|
||||
while os.path.isfile(get_filename(i)):
|
||||
@@ -229,6 +229,17 @@ def save_caption(name, date_epoch, caption, shorter_output=False, quiet=False):
|
||||
os.utime(filename, (datetime.datetime.now().timestamp(), date_epoch))
|
||||
|
||||
|
||||
def save_location(name, location_json, date_epoch):
|
||||
filename = name.lower() + '/' + _epoch_to_string(date_epoch) + '_location.txt'
|
||||
location_string = location_json["name"]+"\n" + \
|
||||
"https://maps.google.com/maps?q={0},{1}&ll={0},{1}\n" \
|
||||
.format(location_json["lat"], location_json["lng"])
|
||||
os.makedirs(name.lower(), exist_ok=True)
|
||||
with open(filename, 'wb') as text_file:
|
||||
shutil.copyfileobj(BytesIO(location_string.encode()), text_file)
|
||||
os.utime(filename, (datetime.datetime.now().timestamp(), date_epoch))
|
||||
|
||||
|
||||
def download_profilepic(name, url, quiet=False):
|
||||
"""Downloads and saves profile pic with given url."""
|
||||
date_object = datetime.datetime.strptime(requests.head(url).headers["Last-Modified"], \
|
||||
@@ -414,8 +425,18 @@ def get_feed_json(session, end_cursor=None, sleep=True):
|
||||
return json.loads(resp.text)
|
||||
|
||||
|
||||
def get_location(node, session, sleep=True):
|
||||
pic_json = get_json("p/" + node["code"], session, sleep=sleep)
|
||||
if pic_json["entry_data"]["PostPage"][0]["media"]["location"] is not None:
|
||||
location_json = get_json("explore/locations/" +
|
||||
pic_json["entry_data"]["PostPage"][0]["media"]["location"]["id"],
|
||||
session, sleep=sleep)
|
||||
return location_json["entry_data"]["LocationsPage"][0]["location"]
|
||||
|
||||
|
||||
def download_node(node, session, name,
|
||||
download_videos=True, sleep=True, shorter_output=False, quiet=False):
|
||||
download_videos=True, geotags=False,
|
||||
sleep=True, shorter_output=False, quiet=False):
|
||||
"""
|
||||
Download everything associated with one instagram node, i.e. picture, caption and video.
|
||||
|
||||
@@ -440,12 +461,17 @@ def download_node(node, session, name,
|
||||
download_pic(name,
|
||||
video_data['entry_data']['PostPage'][0]['media']['video_url'],
|
||||
node["date"], 'mp4', quiet=quiet)
|
||||
if geotags:
|
||||
location = get_location(node, session, sleep)
|
||||
if location:
|
||||
save_location(name, location, node["date"])
|
||||
_log(quiet=quiet)
|
||||
return downloaded
|
||||
|
||||
|
||||
def download_feed_pics(session, max_count=None, fast_update=False, filter_func=None,
|
||||
download_videos=True, shorter_output=False, sleep=True, quiet=False):
|
||||
download_videos=True, geotags=False,
|
||||
shorter_output=False, sleep=True, quiet=False):
|
||||
"""
|
||||
Download pictures from the user's feed.
|
||||
|
||||
@@ -475,8 +501,8 @@ def download_feed_pics(session, max_count=None, fast_update=False, filter_func=N
|
||||
_log("[%3i] %s " % (count, name), end="", flush=True, quiet=quiet)
|
||||
count += 1
|
||||
downloaded = download_node(node, session, name,
|
||||
download_videos=download_videos, sleep=sleep,
|
||||
shorter_output=shorter_output, quiet=quiet)
|
||||
download_videos=download_videos, geotags=geotags,
|
||||
sleep=sleep, shorter_output=shorter_output, quiet=quiet)
|
||||
if fast_update and not downloaded:
|
||||
return
|
||||
data = get_feed_json(session, end_cursor=data["feed"]["media"]["page_info"]["end_cursor"],
|
||||
@@ -520,10 +546,10 @@ def check_id(profile, session, json_data, quiet=False):
|
||||
raise ProfileNotExistsException("Profile {0} does not exist.".format(profile))
|
||||
|
||||
|
||||
def download(name, session, profile_pic_only=False, download_videos=True,
|
||||
def download(name, session, profile_pic_only=False, download_videos=True, geotags=False,
|
||||
fast_update=False, shorter_output=False, sleep=True, quiet=False):
|
||||
"""Download one profile"""
|
||||
# pylint:disable=too-many-branches
|
||||
# pylint:disable=too-many-branches,too-many-locals
|
||||
# Get profile main page json
|
||||
data = get_json(name, session, sleep=sleep)
|
||||
# check if profile does exist or name has changed since last download
|
||||
@@ -567,8 +593,8 @@ def download(name, session, profile_pic_only=False, download_videos=True,
|
||||
_log("[%3i/%3i] " % (count, totalcount), end="", flush=True, quiet=quiet)
|
||||
count += 1
|
||||
downloaded = download_node(node, session, name,
|
||||
download_videos=download_videos, sleep=sleep,
|
||||
shorter_output=shorter_output, quiet=quiet)
|
||||
download_videos=download_videos, geotags=geotags,
|
||||
sleep=sleep, shorter_output=shorter_output, quiet=quiet)
|
||||
if fast_update and not downloaded:
|
||||
return
|
||||
data = get_json(name, session, max_id=get_last_id(data), sleep=sleep)
|
||||
@@ -589,7 +615,7 @@ def get_logged_in_session(username, password=None, quiet=False):
|
||||
password = None
|
||||
|
||||
def download_profiles(profilelist, username=None, password=None, sessionfile=None,
|
||||
profile_pic_only=False, download_videos=True, fast_update=False,
|
||||
profile_pic_only=False, download_videos=True, geotags=False, fast_update=False,
|
||||
sleep=True, shorter_output=False, quiet=False):
|
||||
"""Download set of profiles and handle sessions"""
|
||||
# pylint:disable=too-many-branches,too-many-locals
|
||||
@@ -614,14 +640,14 @@ def download_profiles(profilelist, username=None, password=None, sessionfile=Non
|
||||
elif pentry == ":feed-all" and username is not None:
|
||||
_log("Retrieving pictures from your feed...", quiet=quiet)
|
||||
download_feed_pics(session, fast_update=fast_update,
|
||||
download_videos=download_videos, shorter_output=shorter_output,
|
||||
sleep=sleep, quiet=quiet)
|
||||
download_videos=download_videos, geotags=geotags,
|
||||
shorter_output=shorter_output, sleep=sleep, quiet=quiet)
|
||||
elif pentry == ":feed-liked" and username is not None:
|
||||
_log("Retrieving pictures you liked from your feed...", quiet=quiet)
|
||||
download_feed_pics(session, fast_update=fast_update,
|
||||
filter_func=lambda node: not node["likes"]["viewer_has_liked"],
|
||||
download_videos=download_videos, shorter_output=shorter_output,
|
||||
sleep=sleep, quiet=quiet)
|
||||
download_videos=download_videos, geotags=geotags,
|
||||
shorter_output=shorter_output, sleep=sleep, quiet=quiet)
|
||||
else:
|
||||
targets.add(pentry)
|
||||
if len(targets) == 0:
|
||||
@@ -632,7 +658,7 @@ def download_profiles(profilelist, username=None, password=None, sessionfile=Non
|
||||
for target in targets:
|
||||
try:
|
||||
download(target, session, profile_pic_only, download_videos,
|
||||
fast_update, shorter_output, sleep, quiet)
|
||||
geotags, fast_update, shorter_output, sleep, quiet)
|
||||
except NonfatalException as err:
|
||||
failedtargets.append(target)
|
||||
print(err, file=sys.stderr)
|
||||
@@ -670,6 +696,8 @@ def main():
|
||||
help='Only download profile picture')
|
||||
parser.add_argument('-V', '--skip-videos', action='store_true',
|
||||
help='Do not download videos')
|
||||
parser.add_argument('-G', '--geotags', action='store_true',
|
||||
help='Store geotags when available')
|
||||
parser.add_argument('-F', '--fast-update', action='store_true',
|
||||
help='Abort at encounter of first already-downloaded picture')
|
||||
parser.add_argument('-S', '--no-sleep', action='store_true',
|
||||
@@ -682,7 +710,7 @@ def main():
|
||||
args = parser.parse_args()
|
||||
try:
|
||||
download_profiles(args.profile, args.login, args.password, args.sessionfile,
|
||||
args.profile_pic_only, not args.skip_videos, args.fast_update,
|
||||
args.profile_pic_only, not args.skip_videos, args.geotags, args.fast_update,
|
||||
not args.no_sleep, args.shorter_output, args.quiet)
|
||||
except InstaloaderException as err:
|
||||
raise SystemExit("Fatal error: %s" % err)
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
from setuptools import setup
|
||||
|
||||
if sys.version_info < (3, 3):
|
||||
sys.exit('Instaloader requires Python >= 3.3.')
|
||||
|
||||
setup(
|
||||
name='instaloader',
|
||||
version='1.1',
|
||||
version='1.2',
|
||||
py_modules=['instaloader'],
|
||||
url='https://github.com/Thammus/instaloader',
|
||||
license='MIT',
|
||||
author='Alexander Graf, André Koch-Kramer',
|
||||
author_email='mail@agraf.me, koch-kramer@web.de',
|
||||
description='Tool to download pictures (or videos) and captions from Instagram, from a given '
|
||||
'set of profiles, from your feed or from all followees of a given profile.',
|
||||
description='Tool to download Instagram pictures (or videos) and captions from a given set of '
|
||||
'profiles (even if private), from your feed or from all followees of a given '
|
||||
'profile.',
|
||||
long_description=open('README.rst').read(),
|
||||
install_requires=['requests>=2.4'],
|
||||
python_requires='>=3.3',
|
||||
entry_points={'console_scripts': ['instaloader=instaloader:main']},
|
||||
@@ -20,12 +26,15 @@ setup(
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Console',
|
||||
'Intended Audience :: End Users/Desktop',
|
||||
'License :: OSI Approved :: MIT License',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6'
|
||||
'Topic :: Internet'
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: 3 :: Only',
|
||||
'Topic :: Internet',
|
||||
'Topic :: Multimedia :: Graphics'
|
||||
]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user