Skip to content

Commit

Permalink
Merge pull request #3 from niuware/development
Browse files Browse the repository at this point in the history
Add 'Timeline' endpoint download resource feature
  • Loading branch information
niuware authored Oct 8, 2019
2 parents ae2d1fd + 61e052c commit fcdba4e
Show file tree
Hide file tree
Showing 9 changed files with 169 additions and 140 deletions.
102 changes: 48 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# Instpector

A simple Instagram's web API library written in Python. No selenium or webdriver required.
A simple Instagram's web API library written in Python. Supports login with two-factor authentication enabled. No selenium or webdriver required.

Supports login with two-factor authentication enabled.

# Installation
## Installation

```
pip install instpector
Expand All @@ -16,35 +14,22 @@ pip install instpector
from instpector import Instpector, endpoints

instpector = Instpector()

# Login into Instagram's web
instpector.login("my_username", "my_password")

# Get the profile of any user, for example 'some_username'
profile = endpoints.factory.create("profile", instpector)
followers = endpoints.factory.create("followers", instpector)

insta_profile = profile.of_user("some_username")

print(insta_profile)
# id, followers_count, following_count, is_private, ...

# Iterate all followers of 'some_username'
followers = endpoints.factory.create("followers", instpector)

# Loop through all followers
for follower in followers.of_user(insta_profile.id):
print(follower)
# id, username, full_name, ...
print(follower.username)

# Logout
instpector.logout()
```

## Examples

Check out more examples [here](https://github.com/niuware/instpector/tree/master/examples).

## Using 2FA
For login in using two-factor authentication, generate your 2fa key on Instagram's app and provide the code when logging in with `instpector`. The following example uses `pytop` to demonstrate the usage:
For login in using two-factor authentication, generate your 2fa key once on Instagram's app and provide the code when logging in with `instpector`. The following example uses `pytop` to demonstrate the usage:

```python
from pyotp import TOTP
Expand All @@ -57,7 +42,11 @@ totp = TOTP("my_2fa_key") # Input without spaces
instpector.login("my_username", "my_password", totp.now())
```

# Available endpoints
# Examples

Check out more examples [here](https://github.com/niuware/instpector/tree/master/examples).

# Endpoints

- Followers
- Following
Expand All @@ -72,19 +61,19 @@ More to come

## Classes

### Instpector
`Instpector`

|Method|Details|
|---|---|
|login(user, password, two_factor_code=None)|Login to an Instagram account. If your account is 2FA protected check the [provided example](https://github.com/niuware/instpector/blob/master/examples/two_factor_auth.py).|
|login(user, password, two_factor_code=None)|Login to an Instagram account. If your account is 2FA protected provide the 2FA code as in the [provided example](https://github.com/niuware/instpector/blob/master/examples/two_factor_auth.py).|
|logout()|Logouts from an Instagram account|
|session()|Returns the current session used by `instpector`|

### EndpointFactory
`EndpointFactory`

|Method|Details|
|---|---|
|create(endpoint_name, instpector_instance)|Creates and returns an endpoint instance based on the provided name. Available names are: `"followers"`, `"following"`, `"profile"`, `"timeline"`, `"story_reel"` and `"story"`|
|create(endpoint_name, instpector_instance)|Creates and returns an endpoint instance based on the provided name. Available endpoint names are: `"followers"`, `"following"`, `"profile"`, `"timeline"`, `"story_reel"` and `"story"`|

## Endpoints

Expand All @@ -94,7 +83,7 @@ Gets the profile of any public or friend user account.

|Method|Details|
|---|---|
|of_user(username)|Returns a `TProfile` instance of the provided username.|
|of_user(username)|Returns a `TProfile` instance for the provided username.|

### Followers

Expand All @@ -119,6 +108,7 @@ Endpoint for accessing the timeline of any public or friend account.
|Method|Details|
|---|---|
|of_user(user_id)|Returns a generator of `TTimelinePost` instances with all timeline posts. Note the method receives a user id and not a username. To get the user id use the `Profile` endpoint.|
|download(timeline_post, only_image=False, low_quality=False)|Downloads and save the available resources (image and video) for the provided `TTimelinePost`. The file name convention is `ownerid_resourceid.extension` and saved in the execution directory. If `low_quality` is `True` the resource will be the downloaded with the lowest size available (only for image). If `only_image` is `True` a video file resource won't be downloaded.|

### StoryReel

Expand All @@ -127,64 +117,68 @@ Endpoint for accessing the story reel (stories) of any public or friend user acc
|Method|Details|
|---|---|
|of_user(user_id)|Returns a generator of `TStoryReelItem` instances with all stories. Note the method receives a user id and not a username. To get the user id use the `Profile` endpoint.|
|download(story_item, low_quality=False)|Downloads and save the available resources (image and video) for the provided `TStoryReelItem`. The files are named using the story's Instagram Id and saved in the execution directory. If `low_quality` is `True` the resource will be the downloaded with the least size available.|
|download(story_item, only_image=False, low_quality=False)|Downloads and save the available resources (image and video) for the provided `TStoryReelItem`. The file name convention is `ownerid_resourceid.extension` and saved in the execution directory. If `low_quality` is `True` the resource will be the downloaded with the lowest size available. If `only_image` is `True` a video file resource won't be downloaded.|

### Story

Endpoint for accessing the story details of a story reel item. This endpoint is only available for stories posted by the current logged in user.

|Method|Details|
|---|---|
|viewers_for(story_id)|Returns a generator of `TStoryViewer` instances with all viewers for the provided story id.|
|viewers_for(story_id)|Returns a generator of `TStoryViewer` instances with all viewers of the provided story id.|

## Types

### TUser

|Field|Type|Details|
|---|---|---|
|id|string|The Instagram Id of the user|
|username|string|The user's name|
|full_name|string|The full name of the user|
|is_private|bool|A flag to show if the user account is private|
|id|`string`|The Instagram Id of the user|
|username|`string`|The user's name|
|full_name|`string`|The full name of the user|
|is_private|`bool`|A flag to show if the user account is private|

### TProfile

|Field|Type|Details|
|---|---|---|
|id|string|The Instagram Id of the user|
|username|string|The user's name|
|biography|string|The biography of the user|
|is_private|bool|A flag to show if the user account is private|
|followers_count|integer|The follower count of the user|
|following_count|integer|The following count of the user|
|id|`string`|The Instagram Id of the user|
|username|`string`|The user's name|
|biography|`string`|The biography of the user|
|is_private|`bool`|A flag to show if the user account is private|
|followers_count|`integer`|The follower count of the user|
|following_count|`integer`|The following count of the user|

### TTimelinePost
|Field|Type|Details|
|---|---|---|
|id|string|The Instagram Id of the user|
|timestamp|integer|The timestamp of the post|
|is_video|bool|A flag to know if the story is a video|
|like_count|integer|The like count of the post|
|comment_count|integer|The comment count of the post|
|id|`string`|The Instagram Id of the user|
|owner|`string`|The owner account Instagram Id|
|timestamp|`integer`|The timestamp of the post|
|is_video|`bool`|A flag to know if the story is a video|
|like_count|`integer`|The like count of the post|
|comment_count|`integer`|The comment count of the post|
|display_resources|`list`|A list of image URLs associated with the post|
|video_url|`string`|The video URL (if available) associated with the post|

### TStoryReelItem
|Field|Type|Details|
|---|---|---|
|id|string|The Instagram Id of the story|
|timestamp|integer|The timestamp of the story|
|expire_at|integer|The expiration timestamp of the story|
|audience|string|The type of audience of the story. If public the value is `MediaAudience.DEFAULT`, if private the value is `MediaAudience.BESTIES`|
|is_video|bool|A flag to know if the story is a video|
|view_count|integer|The view count of the story. The count is only available for stories posted by the currently logged in user. Other accounts will have a count equal to `0`.|
|display_resources|list|A list of images URLs associated with the story|
|video_resources|list|A list of video URLs associated with the story|
|id|`string`|The Instagram Id of the story|
|owner|`string`|The owner account Instagram Id|
|timestamp|`integer`|The timestamp of the story|
|expire_at|`integer`|The expiration timestamp of the story|
|audience|`string`|The type of audience of the story. If public the value is `MediaAudience.DEFAULT`, if private the value is `MediaAudience.BESTIES`|
|is_video|`bool`|A flag to know if the story is a video|
|view_count|`integer`|The view count of the story. The count is only available for stories posted by the currently logged in user. Other accounts will have a count equal to `0`.|
|display_resources|`list`|A list of image URLs associated with the story|
|video_resources|`list`|A list of video URLs associated with the story|

### TStoryViewer
|Field|Type|Details|
|---|---|---|
|id|string|The Instagram Id of the story viewer|
|username|string|The user name of the viewer|
|id|`string`|The Instagram Id of the story viewer|
|username|`string`|The user name of the viewer|

# Development dependencies

Expand Down
1 change: 0 additions & 1 deletion examples/download_stories.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
from context import Instpector, endpoints

def download_stories(**options):

instpector = Instpector()
if not instpector.login(user=options.get("user"), password=options.get("password")):
return
Expand Down
32 changes: 32 additions & 0 deletions examples/download_timeline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from sys import argv
from context import Instpector, endpoints

def download_timeline(**options):
instpector = Instpector()
if not instpector.login(user=options.get("user"), password=options.get("password")):
return

profile = endpoints.factory.create("profile", instpector)
timeline = endpoints.factory.create("timeline", instpector)

target_profile = profile.of_user(options.get("target_username"))

for post in timeline.of_user(target_profile.id):
timeline.download(post)

instpector.logout()

if __name__ == '__main__':
if len(argv) < 6:
print((
"Missing arguments: "
"--user {user} "
"--password {password} "
"--target_username {username}"
))
exit(1)
download_timeline(
user=argv[2],
password=argv[4],
target_username=argv[6]
)
10 changes: 10 additions & 0 deletions instpector/apis/instagram/base_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,13 @@ def get(self, url_path, **options):
except json.decoder.JSONDecodeError:
raise ParseDataException
return None

def _download_resources_list(self, item, name, extension, low_quality):
try:
resources = getattr(item, name)
except AttributeError:
return
if resources:
quality = (len(resources) - 1) if not low_quality else 0
file_name = f"{item.owner}_{item.id}.{extension}"
super().download_file(resources[quality], file_name)
6 changes: 4 additions & 2 deletions instpector/apis/instagram/definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
"id username biography is_private followers_count following_count"
))

TTimelinePost = namedtuple("TTimelinePost", "id timestamp is_video like_count comment_count")
TTimelinePost = namedtuple("TTimelinePost", (
"id owner timestamp is_video like_count comment_count display_resources video_url"
))

TStoryReelItem = namedtuple("TStoryReelItem", (
"id timestamp expire_at audience is_video view_count display_resources video_resources"
"id owner timestamp expire_at audience is_video view_count display_resources video_resources"
))

TStoryViewer = namedtuple("TStoryViewer", "id username")
Loading

0 comments on commit fcdba4e

Please sign in to comment.