|
|
|
@ -447,6 +447,27 @@ class InstagramTaggedExtractor(InstagramExtractor):
|
|
|
|
|
return self.api.user_tagged(self.user_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InstagramGuideExtractor(InstagramExtractor):
|
|
|
|
|
"""Extractor for an Instagram guide"""
|
|
|
|
|
subcategory = "guide"
|
|
|
|
|
pattern = USER_PATTERN + r"/guide/[^/?#]+/(\d+)"
|
|
|
|
|
test = (("https://www.instagram.com/kadakaofficial/guide"
|
|
|
|
|
"/knit-i-need-collection/18131821684305217/"), {
|
|
|
|
|
"range": "1-16",
|
|
|
|
|
"count": ">= 16",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
def __init__(self, match):
|
|
|
|
|
InstagramExtractor.__init__(self, match)
|
|
|
|
|
self.guide_id = match.group(2)
|
|
|
|
|
|
|
|
|
|
def metadata(self):
|
|
|
|
|
return {"guide": self.api.guide(self.guide_id)}
|
|
|
|
|
|
|
|
|
|
def posts(self):
|
|
|
|
|
return self.api.guide_media(self.guide_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InstagramSavedExtractor(InstagramExtractor):
|
|
|
|
|
"""Extractor for an Instagram user's saved media"""
|
|
|
|
|
subcategory = "saved"
|
|
|
|
@ -707,6 +728,15 @@ class InstagramRestAPI():
|
|
|
|
|
def __init__(self, extractor):
|
|
|
|
|
self.extractor = extractor
|
|
|
|
|
|
|
|
|
|
def guide(self, guide_id):
|
|
|
|
|
endpoint = "/v1/guides/web_info/"
|
|
|
|
|
params = {"guide_id": guide_id}
|
|
|
|
|
return self._call(endpoint, params=params)
|
|
|
|
|
|
|
|
|
|
def guide_media(self, guide_id):
|
|
|
|
|
endpoint = "/v1/guides/guide/{}/".format(guide_id)
|
|
|
|
|
return self._pagination_guides(endpoint)
|
|
|
|
|
|
|
|
|
|
def highlights_media(self, user_id):
|
|
|
|
|
chunk_size = 5
|
|
|
|
|
reel_ids = [hl["id"] for hl in self.highlights_tray(user_id)]
|
|
|
|
@ -858,13 +888,28 @@ class InstagramRestAPI():
|
|
|
|
|
params["page"] = info["next_page"]
|
|
|
|
|
params["max_id"] = extr._update_cursor(info["next_max_id"])
|
|
|
|
|
|
|
|
|
|
def _pagination_guides(self, endpoint):
|
|
|
|
|
extr = self.extractor
|
|
|
|
|
params = {"max_id": extr._init_cursor()}
|
|
|
|
|
|
|
|
|
|
while True:
|
|
|
|
|
data = self._call(endpoint, params=params)
|
|
|
|
|
|
|
|
|
|
for item in data["items"]:
|
|
|
|
|
yield from item["media_items"]
|
|
|
|
|
|
|
|
|
|
if "next_max_id" not in data:
|
|
|
|
|
return
|
|
|
|
|
params["max_id"] = extr._update_cursor(data["next_max_id"])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class InstagramGraphqlAPI():
|
|
|
|
|
|
|
|
|
|
def __init__(self, extractor):
|
|
|
|
|
self.extractor = extractor
|
|
|
|
|
self.user_collection = self.user_saved = self.reels_media = \
|
|
|
|
|
self.highlights_media = self._login_required
|
|
|
|
|
self.highlights_media = self.guide = self.guide_media = \
|
|
|
|
|
self._unsupported
|
|
|
|
|
self._json_dumps = json.JSONEncoder(separators=(",", ":")).encode
|
|
|
|
|
|
|
|
|
|
api = InstagramRestAPI(extractor)
|
|
|
|
@ -873,8 +918,8 @@ class InstagramGraphqlAPI():
|
|
|
|
|
self.user_id = api.user_id
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _login_required(_=None):
|
|
|
|
|
raise exception.AuthorizationError("Login required")
|
|
|
|
|
def _unsupported(_=None):
|
|
|
|
|
raise exception.StopExtraction("Unsupported with GraphQL API")
|
|
|
|
|
|
|
|
|
|
def highlights_tray(self, user_id):
|
|
|
|
|
query_hash = "d4d88dc1500312af6f937f7b804c68c3"
|
|
|
|
|