datasources.ninegag.search_9gag
Import scraped 9gag data
It's prohibitively difficult to scrape data from 9gag within 4CAT itself due to its aggressive rate limiting. Instead, import data collected elsewhere.
1""" 2Import scraped 9gag data 3 4It's prohibitively difficult to scrape data from 9gag within 4CAT itself due 5to its aggressive rate limiting. Instead, import data collected elsewhere. 6""" 7from datetime import datetime 8 9from backend.lib.search import Search 10from common.lib.item_mapping import MappedItem 11from common.lib.helpers import normalize_url_encoding 12 13 14class SearchNineGag(Search): 15 """ 16 Import scraped 9gag data 17 """ 18 type = "ninegag-search" # job ID 19 category = "Search" # category 20 title = "Import scraped 9gag data" # title displayed in UI 21 description = "Import 9gag data collected with an external tool such as Zeeschuimer." # description displayed in UI 22 extension = "ndjson" # extension of result file, used internally and in UI 23 is_from_zeeschuimer = True 24 25 # not available as a processor for existing datasets 26 accepts = [None] 27 references = [ 28 "[Zeeschuimer browser extension](https://github.com/digitalmethodsinitiative/zeeschuimer)", 29 "[Worksheet: Capturing TikTok data with Zeeschuimer and 4CAT](https://tinyurl.com/nmrw-zeeschuimer-tiktok)" 30 ] 31 32 def get_items(self, query): 33 """ 34 Run custom search 35 36 Not available for 9gag 37 """ 38 raise NotImplementedError("9gag datasets can only be created by importing data from elsewhere") 39 40 @staticmethod 41 def map_item(post): 42 post_timestamp = datetime.fromtimestamp(post["creationTs"]) 43 44 image = sorted([v for v in post["images"].values() if "hasAudio" not in v], key=lambda image: image["width"] * image["height"], reverse=True)[0] 45 video = sorted([v for v in post["images"].values() if "hasAudio" in v], key=lambda image: image["width"] * image["height"], reverse=True) 46 47 video_url = "" 48 if video: 49 # annoyingly, not all formats are always available 50 video = video[0] 51 if "av1Url" in video: 52 video_url = video["av1Url"] 53 elif "h265Url" in video: 54 video_url = video["h265Url"] 55 elif "vp9Url" in video: 56 video_url = video["vp9Url"] 57 elif "vp8Url" in video: 58 video_url = video["vp8Url"] 59 60 if not post["creator"]: 61 # anonymous posts exist 62 # they display as from the user '9GAGGER' on the website 63 post["creator"] = { 64 "username": "9GAGGER", 65 "fullName": "", 66 "emojiStatus": "", 67 "isVerifiedAccount": "" 68 } 69 70 return MappedItem({ 71 "collected_from_url": normalize_url_encoding(post.get("__import_meta", {}).get("source_platform_url", "")), # Zeeschuimer metadata 72 "id": post["id"], 73 "url": post["url"], 74 "subject": post["title"], 75 "body": post["description"], 76 "timestamp": post_timestamp.strftime("%Y-%m-%d %H:%M:%S"), 77 "author": post["creator"]["username"], 78 "author_name": post["creator"]["fullName"], 79 "author_status": post["creator"]["emojiStatus"], 80 "author_verified": "yes" if post["creator"]["isVerifiedAccount"] else "no", 81 "type": post["type"], 82 "image_url": image["url"], 83 "video_url": video_url, 84 "is_nsfw": "no" if post["nsfw"] == 0 else "yes", 85 "is_promoted": "no" if post["promoted"] == 0 else "yes", 86 "is_vote_masked": "no" if post["isVoteMasked"] == 0 else "yes", 87 "is_anonymous": "no" if not post["isAnonymous"] else "yes", 88 "source_domain": post["sourceDomain"], 89 "source_url": post["sourceUrl"], 90 "upvotes": post["upVoteCount"], 91 "downvotes": post["downVoteCount"], 92 "score": post["upVoteCount"] - post["downVoteCount"], 93 "comments": post["commentsCount"], 94 "tags": ",".join([tag["key"] for tag in post["tags"]]), 95 "tags_annotated": ",".join(post["annotationTags"]), 96 "unix_timestamp": int(post_timestamp.timestamp()), 97 })
15class SearchNineGag(Search): 16 """ 17 Import scraped 9gag data 18 """ 19 type = "ninegag-search" # job ID 20 category = "Search" # category 21 title = "Import scraped 9gag data" # title displayed in UI 22 description = "Import 9gag data collected with an external tool such as Zeeschuimer." # description displayed in UI 23 extension = "ndjson" # extension of result file, used internally and in UI 24 is_from_zeeschuimer = True 25 26 # not available as a processor for existing datasets 27 accepts = [None] 28 references = [ 29 "[Zeeschuimer browser extension](https://github.com/digitalmethodsinitiative/zeeschuimer)", 30 "[Worksheet: Capturing TikTok data with Zeeschuimer and 4CAT](https://tinyurl.com/nmrw-zeeschuimer-tiktok)" 31 ] 32 33 def get_items(self, query): 34 """ 35 Run custom search 36 37 Not available for 9gag 38 """ 39 raise NotImplementedError("9gag datasets can only be created by importing data from elsewhere") 40 41 @staticmethod 42 def map_item(post): 43 post_timestamp = datetime.fromtimestamp(post["creationTs"]) 44 45 image = sorted([v for v in post["images"].values() if "hasAudio" not in v], key=lambda image: image["width"] * image["height"], reverse=True)[0] 46 video = sorted([v for v in post["images"].values() if "hasAudio" in v], key=lambda image: image["width"] * image["height"], reverse=True) 47 48 video_url = "" 49 if video: 50 # annoyingly, not all formats are always available 51 video = video[0] 52 if "av1Url" in video: 53 video_url = video["av1Url"] 54 elif "h265Url" in video: 55 video_url = video["h265Url"] 56 elif "vp9Url" in video: 57 video_url = video["vp9Url"] 58 elif "vp8Url" in video: 59 video_url = video["vp8Url"] 60 61 if not post["creator"]: 62 # anonymous posts exist 63 # they display as from the user '9GAGGER' on the website 64 post["creator"] = { 65 "username": "9GAGGER", 66 "fullName": "", 67 "emojiStatus": "", 68 "isVerifiedAccount": "" 69 } 70 71 return MappedItem({ 72 "collected_from_url": normalize_url_encoding(post.get("__import_meta", {}).get("source_platform_url", "")), # Zeeschuimer metadata 73 "id": post["id"], 74 "url": post["url"], 75 "subject": post["title"], 76 "body": post["description"], 77 "timestamp": post_timestamp.strftime("%Y-%m-%d %H:%M:%S"), 78 "author": post["creator"]["username"], 79 "author_name": post["creator"]["fullName"], 80 "author_status": post["creator"]["emojiStatus"], 81 "author_verified": "yes" if post["creator"]["isVerifiedAccount"] else "no", 82 "type": post["type"], 83 "image_url": image["url"], 84 "video_url": video_url, 85 "is_nsfw": "no" if post["nsfw"] == 0 else "yes", 86 "is_promoted": "no" if post["promoted"] == 0 else "yes", 87 "is_vote_masked": "no" if post["isVoteMasked"] == 0 else "yes", 88 "is_anonymous": "no" if not post["isAnonymous"] else "yes", 89 "source_domain": post["sourceDomain"], 90 "source_url": post["sourceUrl"], 91 "upvotes": post["upVoteCount"], 92 "downvotes": post["downVoteCount"], 93 "score": post["upVoteCount"] - post["downVoteCount"], 94 "comments": post["commentsCount"], 95 "tags": ",".join([tag["key"] for tag in post["tags"]]), 96 "tags_annotated": ",".join(post["annotationTags"]), 97 "unix_timestamp": int(post_timestamp.timestamp()), 98 })
Import scraped 9gag data
references =
['[Zeeschuimer browser extension](https://github.com/digitalmethodsinitiative/zeeschuimer)', '[Worksheet: Capturing TikTok data with Zeeschuimer and 4CAT](https://tinyurl.com/nmrw-zeeschuimer-tiktok)']
def
get_items(self, query):
33 def get_items(self, query): 34 """ 35 Run custom search 36 37 Not available for 9gag 38 """ 39 raise NotImplementedError("9gag datasets can only be created by importing data from elsewhere")
Run custom search
Not available for 9gag
@staticmethod
def
map_item(post):
41 @staticmethod 42 def map_item(post): 43 post_timestamp = datetime.fromtimestamp(post["creationTs"]) 44 45 image = sorted([v for v in post["images"].values() if "hasAudio" not in v], key=lambda image: image["width"] * image["height"], reverse=True)[0] 46 video = sorted([v for v in post["images"].values() if "hasAudio" in v], key=lambda image: image["width"] * image["height"], reverse=True) 47 48 video_url = "" 49 if video: 50 # annoyingly, not all formats are always available 51 video = video[0] 52 if "av1Url" in video: 53 video_url = video["av1Url"] 54 elif "h265Url" in video: 55 video_url = video["h265Url"] 56 elif "vp9Url" in video: 57 video_url = video["vp9Url"] 58 elif "vp8Url" in video: 59 video_url = video["vp8Url"] 60 61 if not post["creator"]: 62 # anonymous posts exist 63 # they display as from the user '9GAGGER' on the website 64 post["creator"] = { 65 "username": "9GAGGER", 66 "fullName": "", 67 "emojiStatus": "", 68 "isVerifiedAccount": "" 69 } 70 71 return MappedItem({ 72 "collected_from_url": normalize_url_encoding(post.get("__import_meta", {}).get("source_platform_url", "")), # Zeeschuimer metadata 73 "id": post["id"], 74 "url": post["url"], 75 "subject": post["title"], 76 "body": post["description"], 77 "timestamp": post_timestamp.strftime("%Y-%m-%d %H:%M:%S"), 78 "author": post["creator"]["username"], 79 "author_name": post["creator"]["fullName"], 80 "author_status": post["creator"]["emojiStatus"], 81 "author_verified": "yes" if post["creator"]["isVerifiedAccount"] else "no", 82 "type": post["type"], 83 "image_url": image["url"], 84 "video_url": video_url, 85 "is_nsfw": "no" if post["nsfw"] == 0 else "yes", 86 "is_promoted": "no" if post["promoted"] == 0 else "yes", 87 "is_vote_masked": "no" if post["isVoteMasked"] == 0 else "yes", 88 "is_anonymous": "no" if not post["isAnonymous"] else "yes", 89 "source_domain": post["sourceDomain"], 90 "source_url": post["sourceUrl"], 91 "upvotes": post["upVoteCount"], 92 "downvotes": post["downVoteCount"], 93 "score": post["upVoteCount"] - post["downVoteCount"], 94 "comments": post["commentsCount"], 95 "tags": ",".join([tag["key"] for tag in post["tags"]]), 96 "tags_annotated": ",".join(post["annotationTags"]), 97 "unix_timestamp": int(post_timestamp.timestamp()), 98 })
Inherited Members
- backend.lib.worker.BasicWorker
- BasicWorker
- INTERRUPT_NONE
- INTERRUPT_RETRY
- INTERRUPT_CANCEL
- queue
- log
- manager
- interrupted
- modules
- init_time
- name
- run
- clean_up
- request_interrupt
- run_interruptable_process
- get_queue_id
- is_4cat_class
- backend.lib.search.Search
- max_workers
- prefix
- return_cols
- import_error_count
- import_warning_count
- process
- search
- import_from_file
- items_to_csv
- items_to_ndjson
- items_to_archive
- backend.lib.processor.BasicProcessor
- db
- job
- dataset
- owner
- source_dataset
- source_file
- config
- is_running_in_preset
- filepath
- for_cleanup
- work
- after_process
- clean_up_on_error
- abort
- iterate_proxied_requests
- push_proxied_request
- flush_proxied_requests
- unpack_archive_contents
- extract_archived_file_by_name
- write_csv_items_and_finish
- write_archive_and_finish
- create_standalone
- save_annotations
- map_item_method_available
- get_mapped_item
- is_filter
- get_options
- get_status
- is_top_dataset
- is_from_collector
- get_extension
- is_rankable
- exclude_followup_processors
- is_4cat_processor