158 lines
3.9 KiB
Python
158 lines
3.9 KiB
Python
import argparse
|
|
import bs4
|
|
import datetime
|
|
import json
|
|
import os
|
|
import requests
|
|
import sys
|
|
|
|
# pip install voussoirkit
|
|
from voussoirkit import clipext
|
|
from voussoirkit import downloady
|
|
|
|
|
|
''' '''
|
|
STRFTIME = '%Y%m%d-%H%M%S'
|
|
# strftime used for filenames when downloading
|
|
|
|
URL_PROFILE = 'https://www.instagram.com/{username}'
|
|
URL_QUERY = 'https://www.instagram.com/query/'
|
|
|
|
PAGE_QUERY_TEMPLATE = '''
|
|
ig_user({user_id})
|
|
{{
|
|
media.after({end_cur}, {count})
|
|
{{
|
|
count,
|
|
nodes
|
|
{{
|
|
code,
|
|
date,
|
|
display_src,
|
|
id,
|
|
video_url
|
|
}},
|
|
page_info
|
|
}}
|
|
}}
|
|
'''.replace('\n', '').replace(' ', '')
|
|
|
|
USERAGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
|
|
''' '''
|
|
|
|
last_cookie = None
|
|
|
|
def download_media(media_list):
|
|
for media in media_list:
|
|
timestamp = datetime.datetime.utcfromtimestamp(media['created'])
|
|
timestamp = timestamp.strftime(STRFTIME)
|
|
basename = downloady.basename_from_url(media['url'])
|
|
extension = os.path.splitext(basename)[1]
|
|
|
|
filename = timestamp + extension
|
|
downloady.download_file(
|
|
url=media['url'],
|
|
localname=filename,
|
|
callback_progress=downloady.progress2,
|
|
overwrite=False,
|
|
)
|
|
|
|
def get_page(user_id, end_cur, count, cookies):
|
|
query = PAGE_QUERY_TEMPLATE.format(
|
|
count=count,
|
|
end_cur=end_cur,
|
|
user_id=user_id,
|
|
)
|
|
headers = {
|
|
'referer': 'https://www.instagram.com/',
|
|
'user-agent': USERAGENT,
|
|
'x-csrftoken': cookies['csrftoken'],
|
|
}
|
|
data = {
|
|
'q': query,
|
|
'ref': 'users::show',
|
|
}
|
|
|
|
response = requests.post(
|
|
url=URL_QUERY,
|
|
cookies=cookies,
|
|
data=data,
|
|
headers=headers,
|
|
)
|
|
response.raise_for_status()
|
|
page = response.json()
|
|
return page
|
|
|
|
def get_user_info(username):
|
|
global last_cookie
|
|
url = URL_PROFILE.format(username=username)
|
|
response = requests.get(url)
|
|
response.raise_for_status()
|
|
|
|
text = response.text
|
|
soup = bs4.BeautifulSoup(text, 'html.parser')
|
|
|
|
scripts = soup.find_all('script')
|
|
for script in scripts:
|
|
if 'window._sharedData' in script.text:
|
|
break
|
|
else:
|
|
raise Exception('Did not find expected javascript')
|
|
|
|
user_data = script.text
|
|
user_data = user_data.split('window._sharedData = ')[1].rstrip(';').strip()
|
|
user_data = json.loads(user_data)
|
|
user_data = user_data['entry_data']['ProfilePage'][0]['user']
|
|
|
|
user_id = user_data['id']
|
|
page_info = user_data['media']['page_info']
|
|
if page_info['has_next_page']:
|
|
end_cur = page_info['start_cursor']
|
|
# Minus 1 because the queries use "after" parameters for pagination, and
|
|
# if we just take this cursor then we will only get items after it.
|
|
end_cur = int(end_cur) - 1
|
|
else:
|
|
end_cur = None
|
|
|
|
user_data = {
|
|
'user_id': user_id,
|
|
'end_cur': end_cur,
|
|
'cookies': response.cookies,
|
|
}
|
|
last_cookie = response.cookies
|
|
return user_data
|
|
|
|
def get_user_media(username):
|
|
user_info = get_user_info(username)
|
|
end_cur = user_info.pop('end_cur')
|
|
|
|
while True:
|
|
page = get_page(count=50, end_cur=end_cur, **user_info)
|
|
page = page['media']
|
|
|
|
posts = page['nodes']
|
|
for post in posts:
|
|
timestamp = post['date']
|
|
media_url = post.get('video_url') or post.get('display_src')
|
|
ret = {
|
|
'created': timestamp,
|
|
'url': media_url
|
|
}
|
|
yield ret
|
|
|
|
page_info = page['page_info']
|
|
if page_info['has_next_page']:
|
|
end_cur = page_info['end_cursor']
|
|
else:
|
|
break
|
|
|
|
|
|
def main():
|
|
username = sys.argv[1]
|
|
media = get_user_media(username)
|
|
for (timestamp, url) in media:
|
|
print(url)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|