11"""LeetCode GraphQL API scraper to fetch problem information."""
22
3- from typing import Any , Dict , Optional
3+ from typing import Any
44
55import requests
66
77from .parser import HTMLParser
88
9+ GRAPHQL_URL = "https://leetcode.com/graphql"
10+ ALGORITHMS_API_URL = "https://leetcode.com/api/problems/algorithms/"
11+ USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"
12+
13+ COMMON_SLUGS = {
14+ 1 : "two-sum" ,
15+ 2 : "add-two-numbers" ,
16+ 3 : "longest-substring-without-repeating-characters" ,
17+ 15 : "3sum" ,
18+ 20 : "valid-parentheses" ,
19+ 21 : "merge-two-sorted-lists" ,
20+ 53 : "maximum-subarray" ,
21+ 121 : "best-time-to-buy-and-sell-stock" ,
22+ 125 : "valid-palindrome" ,
23+ 226 : "invert-binary-tree" ,
24+ }
925
10- class LeetCodeScraper :
11- """Scraper for LeetCode problem information using GraphQL API."""
1226
27+ class LeetCodeScraper :
1328 def __init__ (self ):
14- self .base_url = "https://leetcode.com/graphql"
29+ self .base_url = GRAPHQL_URL
1530 self .headers = {
1631 "Content-Type" : "application/json" ,
17- "User-Agent" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36" ,
32+ "User-Agent" : USER_AGENT ,
1833 }
1934
20- def get_problem_by_slug (self , problem_slug : str ) -> Optional [Dict [str , Any ]]:
21- """Get problem info by problem slug (e.g., 'two-sum')."""
35+ def get_problem_by_slug (self , problem_slug : str ) -> dict [str , Any ] | None :
2236 query = """
2337 query questionData($titleSlug: String!) {
2438 question(titleSlug: $titleSlug) {
@@ -51,54 +65,32 @@ def get_problem_by_slug(self, problem_slug: str) -> Optional[Dict[str, Any]]:
5165 return data .get ("data" , {}).get ("question" )
5266 return None
5367
54- def get_problem_by_number (self , problem_number : int ) -> Optional [Dict [str , Any ]]:
55- """Get problem info by problem number (e.g., 1 for Two Sum)."""
68+ def get_problem_by_number (self , problem_number : int ) -> dict [str , Any ] | None :
5669 # First try to get slug from algorithms API
5770 slug = self ._get_slug_by_number (problem_number )
5871 if slug :
5972 return self .get_problem_by_slug (slug )
6073
6174 return self ._try_common_slugs (problem_number )
6275
63- def _get_slug_by_number (self , problem_number : int ) -> Optional [str ]:
64- """Get problem slug by number using the algorithms API."""
76+ def _get_slug_by_number (self , problem_number : int ) -> str | None :
6577 try :
66- response = requests .get (
67- "https://leetcode.com/api/problems/algorithms/" , headers = self .headers
68- )
69-
78+ response = requests .get (ALGORITHMS_API_URL , headers = self .headers )
7079 if response .status_code == 200 :
7180 data = response .json ()
7281 for problem in data .get ("stat_status_pairs" , []):
7382 if problem ["stat" ]["frontend_question_id" ] == problem_number :
7483 return problem ["stat" ]["question__title_slug" ]
7584 except Exception :
7685 pass
77-
7886 return None
7987
80- def _try_common_slugs (self , problem_number : int ) -> Optional [Dict [str , Any ]]:
81- """Try common slug patterns for well-known problems."""
82- common_slugs = {
83- 1 : "two-sum" ,
84- 2 : "add-two-numbers" ,
85- 3 : "longest-substring-without-repeating-characters" ,
86- 15 : "3sum" ,
87- 20 : "valid-parentheses" ,
88- 21 : "merge-two-sorted-lists" ,
89- 53 : "maximum-subarray" ,
90- 121 : "best-time-to-buy-and-sell-stock" ,
91- 125 : "valid-palindrome" ,
92- 226 : "invert-binary-tree" ,
93- }
94-
95- if problem_number in common_slugs :
96- return self .get_problem_by_slug (common_slugs [problem_number ])
97-
88+ def _try_common_slugs (self , problem_number : int ) -> dict [str , Any ] | None :
89+ if problem_number in COMMON_SLUGS :
90+ return self .get_problem_by_slug (COMMON_SLUGS [problem_number ])
9891 return None
9992
100- def get_python_code (self , problem_info : Dict [str , Any ]) -> Optional [str ]:
101- """Extract Python code snippet from problem info."""
93+ def get_python_code (self , problem_info : dict [str , Any ]) -> str | None :
10294 if not problem_info or "codeSnippets" not in problem_info :
10395 return None
10496
@@ -107,8 +99,7 @@ def get_python_code(self, problem_info: Dict[str, Any]) -> Optional[str]:
10799 return snippet .get ("code" )
108100 return None
109101
110- def format_problem_info (self , problem_info : Dict [str , Any ]) -> Dict [str , Any ]:
111- """Format problem info into a clean structure."""
102+ def format_problem_info (self , problem_info : dict [str , Any ]) -> dict [str , Any ]:
112103 if not problem_info :
113104 return {}
114105
0 commit comments