stealth-google-search 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,320 @@
1
+ Metadata-Version: 2.4
2
+ Name: stealth-google-search
3
+ Version: 0.1.0
4
+ Summary: Stealthy Google Search and AI Overview scraper with anti-bot evasion and automatic redirect resolution.
5
+ Author-email: Your Name <your.email@example.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/SeMoazamAli/google-search
8
+ Project-URL: Bug Tracker, https://github.com/SeMoazamAli/google-search/issues
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: undetected-chromedriver>=3.5.5
21
+ Requires-Dist: selenium>=4.20.0
22
+ Requires-Dist: parsel>=1.9.0
23
+ Requires-Dist: requests>=2.31.0
24
+ Requires-Dist: setuptools>=65.0.0
25
+ Dynamic: license-file
26
+
27
+ # Google Search & AI Overview Scraper
28
+
29
+ A robust, stealthy Python scraper for Google Search results. Built using `undetected-chromedriver` and `parsel`, this tool automatically bypasses Google's bot detection, extracts **Google AI Overviews**, scrapes clean organic search results (titles, genuine snippets, provider domains, dates), and automatically resolves Google's `/goto?url=...` encrypted redirect links into their **original destination URLs**.
30
+
31
+ ---
32
+
33
+ ## Table of Contents
34
+
35
+ - [Features](#features)
36
+ - [Prerequisites](#prerequisites)
37
+ - [Installation](#installation)
38
+ - [Quick Start](#quick-start)
39
+ - [Detailed Parameter Reference](#detailed-parameter-reference)
40
+ - [Response Structure & Schema](#response-structure--schema)
41
+ - [Advanced Usage Examples](#advanced-usage-examples)
42
+ - [1. Scraping Multiple Pages](#1-scraping-multiple-pages)
43
+ - [2. Extracting AI Overview Answers](#2-extracting-ai-overview-answers)
44
+ - [3. Running in Visible (Non-Headless) Mode](#3-running-in-visible-non-headless-mode)
45
+ - [4. Ultra-Fast Scraping (Disabling URL Resolution)](#4-ultra-fast-scraping-disabling-url-resolution)
46
+ - [5. Pinning a Specific Chrome Version](#5-pinning-a-specific-chrome-version)
47
+ - [Troubleshooting & FAQs](#troubleshooting--faqs)
48
+
49
+ ---
50
+
51
+ ## Features
52
+
53
+ - ๐Ÿ›ก๏ธ **Anti-Bot Evasion**: Employs `undetected-chromedriver` with native browser fingerprinting to evade Google CAPTCHA blocks.
54
+ - ๐Ÿค– **Google AI Overview Extraction**: Captures the generated AI summary answer and cited sources whenever present.
55
+ - ๐Ÿ”— **Automatic Redirect Unmasking**: Uses concurrent thread pooling to automatically resolve Google's `/goto?url=CAES...` redirects into full, direct landing page URLs (e.g. `https://en.wikipedia.org/...`).
56
+ - ๐Ÿงน **Clean Snippet Parsing**: Specifically targets Google's snippet elements while stripping inline scripts, image loaders, and feedback modals.
57
+ - ๐ŸชŸ **Stealth Headless Mode**: Rather than using detectable `--headless=new` flags that trigger bot detection, runs off-screen (`--window-position=-52000,-52000`) so it doesn't disturb your desktop.
58
+ - ๐Ÿ“„ **Multi-Page Pagination**: Supports scraping across multiple pages (`start=0`, `start=10`, etc.) with URL deduplication.
59
+
60
+ ---
61
+
62
+ ## Prerequisites
63
+
64
+ 1. **Python 3.10+** (Python 3.10, 3.11, or 3.12+).
65
+ 2. **Google Chrome Browser**: A standard desktop installation of Google Chrome must be installed on your machine.
66
+
67
+ ---
68
+
69
+ ## Installation
70
+
71
+ ### 1. Clone the Repository
72
+
73
+ ```bash
74
+ git clone https://github.com/your-username/google-search.git
75
+ cd google-search
76
+ ```
77
+
78
+ ### 2. Create and Activate a Virtual Environment
79
+
80
+ #### On Windows (PowerShell):
81
+ ```powershell
82
+ python -m venv .venv
83
+ .venv\Scripts\Activate.ps1
84
+ ```
85
+
86
+ #### On Linux / macOS:
87
+ ```bash
88
+ python3 -m venv .venv
89
+ source .venv/bin/activate
90
+ ```
91
+
92
+ ### 3. Install Dependencies
93
+
94
+ ```bash
95
+ pip install -r requirements.txt
96
+ ```
97
+
98
+ > **Note for Python 3.12+ users:** `setuptools` is included in `requirements.txt`. It is required because Python 3.12 removed `distutils`, which `undetected-chromedriver` relies on for version resolution.
99
+
100
+ ---
101
+
102
+ ## Quick Start
103
+
104
+ Create a script (or run directly from `google_search/google_search.py`):
105
+
106
+ ```python
107
+ from google_search.google_search import get_search
108
+
109
+ # Run a simple search
110
+ response = get_search("Who won the 2022 FIFA World Cup?", pages=1)
111
+
112
+ # Check if Google provided an AI Overview
113
+ if response.get("ai_response"):
114
+ print("๐Ÿค– AI Overview:")
115
+ print(response["ai_response"]["text"])
116
+ print("-" * 50)
117
+
118
+ # Print organic search results
119
+ print(f"Found {response['meta']['result_count']} results:\n")
120
+ for item in response["results"]:
121
+ print(f"Title: {item['title']}")
122
+ print(f"URL: {item['url']}")
123
+ print(f"Provider: {item.get('provider')}")
124
+ print(f"Description: {item.get('description')}")
125
+ print(f"Date: {item.get('date')}")
126
+ print("-" * 50)
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Detailed Parameter Reference
132
+
133
+ The primary entry point is the function:
134
+
135
+ ```python
136
+ def get_search(
137
+ query: str,
138
+ pages: int = 1,
139
+ version: int | None = None,
140
+ headless: bool = True,
141
+ resolve_urls: bool = True
142
+ ) -> dict:
143
+ ```
144
+
145
+ ### Parameter Details
146
+
147
+ | Parameter | Type | Default | Description |
148
+ | :--- | :--- | :--- | :--- |
149
+ | `query` | `str` | *(Required)* | The search query string (e.g., `"Python web scraping"`, `"World Cup 2022 winner"`). Special characters and spaces are safely encoded. |
150
+ | `pages` | `int` | `1` | Number of search result pages to scrape. <br>โ€ข Set to `1` for the first 10 results.<br>โ€ข Set to `2` or higher to paginate (`&start=10`, `&start=20`, etc.).<br>โ€ข If set to `0` or `None`, it scrapes until no more results are found. |
151
+ | `version` | `int \| None` | `None` | The major version of Google Chrome installed on your machine (e.g., `128`, `130`, `155`). <br>โ€ข If `None` (recommended), `undetected-chromedriver` automatically detects your installed Chrome version and downloads the matching driver.<br>โ€ข Pin this only if auto-detection fails or you have multiple browser versions. |
152
+ | `headless` | `bool` | `True` | Browser visibility mode:<br>โ€ข `True` *(default)*: Spawns the browser pushed off-screen (`--window-position=-52000,-52000`). It operates invisibly without popping up windows or triggering Google's headless detection CAPTCHAs.<br>โ€ข `False`: Launches Chrome in normal visible mode so you can see navigation, scrolling, and debugging in real time. |
153
+ | `resolve_urls` | `bool` | `True` | Redirect resolution setting:<br>โ€ข `True` *(default)*: Concurrent background threads send HTTP requests to Google's `/goto?url=...` redirect tokens to extract and return the real landing page URL (e.g., `https://en.wikipedia.org/...`).<br>โ€ข `False`: Skips redirect resolution and leaves the raw Google redirect URLs. Useful if you want the absolute fastest scraping speed and do not need direct external URLs. |
154
+
155
+ ---
156
+
157
+ ## Response Structure & Schema
158
+
159
+ The function returns a Python dictionary with the following schema:
160
+
161
+ ```python
162
+ {
163
+ "query": "Who won the 2022 FIFA World Cup?",
164
+ "ai_response": {
165
+ "text": "The Argentina national football team won the 2022 FIFA World Cup...",
166
+ "sources": [
167
+ {
168
+ "title": "Learn more",
169
+ "url": "https://support.google.com/websearch?p=ai_overviews",
170
+ "provider": "support.google.com",
171
+ "description": "AI responses may include mistakes. Learn more",
172
+ "type": "ai_source"
173
+ }
174
+ ]
175
+ },
176
+ "results": [
177
+ {
178
+ "title": "2022 FIFA World Cup final",
179
+ "url": "https://en.wikipedia.org/wiki/2022_FIFA_World_Cup_final",
180
+ "provider": "en.wikipedia.org",
181
+ "description": "They defeated Poland 3โ€“1 in the round of 16, England 2โ€“1 in the quarter-final, and Morocco 2โ€“0 in the semi-final. The final took place in front of 88,966 ...",
182
+ "date": "18 December 2022",
183
+ "type": "organic"
184
+ },
185
+ {
186
+ "title": "FIFA World Cup Qatar 2022โ„ข",
187
+ "url": "https://www.fifa.com/en/tournaments/mens/worldcup/qatar2022",
188
+ "provider": "fifa.com",
189
+ "description": "The FIFA World Cup Qatar 2022 was played from 20 November to 18 December 2022. 32 teams competed across 64 matches in the 22nd edition of the tournament.",
190
+ "date": "18 December 2022",
191
+ "type": "organic"
192
+ }
193
+ ],
194
+ "urls": [
195
+ "https://en.wikipedia.org/wiki/2022_FIFA_World_Cup_final",
196
+ "https://www.fifa.com/en/tournaments/mens/worldcup/qatar2022"
197
+ ],
198
+ "meta": {
199
+ "result_count": 7,
200
+ "url_count": 7,
201
+ "has_ai_response": True,
202
+ "pages_scraped": 1
203
+ }
204
+ }
205
+ ```
206
+
207
+ ### Field Descriptions
208
+
209
+ - **`query`** (`str`): The query string executed.
210
+ - **`ai_response`** (`dict | None`): Contains Google's AI Overview answer. If Google did not generate an AI Overview for the query, this is `None`.
211
+ - **`text`** (`str`): Clean text of the AI Overview answer.
212
+ - **`sources`** (`list[dict]`): List of sources referenced by the AI answer.
213
+ - **`results`** (`list[dict]`): Deduplicated list of organic search results:
214
+ - **`title`** (`str`): Title of the web page.
215
+ - **`url`** (`str`): Original destination URL (unmasked from Google redirect).
216
+ - **`provider`** (`str`): Domain or provider name (e.g., `en.wikipedia.org`, `fifa.com`).
217
+ - **`description`** (`str`): Clean search snippet/summary from Google (with scripts and UI noise filtered).
218
+ - **`date`** (`str | None`): Publish or update date if Google displayed one.
219
+ - **`type`** (`str`): Type of result (`"organic"`).
220
+ - **`urls`** (`list[str]`): Deduplicated list of all external URLs found across the scraped pages.
221
+ - **`meta`** (`dict`):
222
+ - **`result_count`** (`int`): Total number of organic results returned.
223
+ - **`url_count`** (`int`): Total count of unique links.
224
+ - **`has_ai_response`** (`bool`): `True` if an AI overview was detected and parsed.
225
+ - **`pages_scraped`** (`int`): Number of Google result pages successfully processed.
226
+
227
+ ---
228
+
229
+ ## Advanced Usage Examples
230
+
231
+ ### 1. Scraping Multiple Pages
232
+
233
+ To scrape 3 pages of results (up to ~30 results):
234
+
235
+ ```python
236
+ from google_search.google_search import get_search
237
+
238
+ results = get_search("machine learning tutorials", pages=3)
239
+ print(f"Scraped {results['meta']['pages_scraped']} pages.")
240
+ print(f"Total results: {results['meta']['result_count']}")
241
+ ```
242
+
243
+ ### 2. Extracting AI Overview Answers
244
+
245
+ Google often provides generative AI summaries for informative or question queries:
246
+
247
+ ```python
248
+ from google_search.google_search import get_search
249
+
250
+ data = get_search("How does photosynthesis work?", pages=1)
251
+
252
+ if data["meta"]["has_ai_response"]:
253
+ ai = data["ai_response"]
254
+ print("Answer:\n", ai["text"])
255
+ print("\nCited Sources:")
256
+ for s in ai["sources"]:
257
+ print(f"- {s['title']} ({s['url']})")
258
+ else:
259
+ print("No AI Overview generated for this query.")
260
+ ```
261
+
262
+ ### 3. Running in Visible (Non-Headless) Mode
263
+
264
+ If you are debugging or want to observe Chrome typing and scrolling:
265
+
266
+ ```python
267
+ from google_search.google_search import get_search
268
+
269
+ data = get_search("latest tech news", pages=1, headless=False)
270
+ ```
271
+
272
+ ### 4. Ultra-Fast Scraping (Disabling URL Resolution)
273
+
274
+ If you only need titles and snippet text and do not require resolved destination URLs, pass `resolve_urls=False`:
275
+
276
+ ```python
277
+ from google_search.google_search import get_search
278
+
279
+ # Saves ~1 second of network time by skipping HTTP 302 redirect checks
280
+ data = get_search("fast query", pages=1, resolve_urls=False)
281
+ ```
282
+
283
+ ### 5. Pinning a Specific Chrome Version
284
+
285
+ If auto-detection fails or you have a standalone Chrome version installed:
286
+
287
+ ```python
288
+ from google_search.google_search import get_search
289
+
290
+ # Forces undetected-chromedriver to download and match Chrome 130 driver
291
+ data = get_search("Python tutorials", version=130)
292
+ ```
293
+
294
+ ---
295
+
296
+ ## Troubleshooting & FAQs
297
+
298
+ ### Q1: `ModuleNotFoundError: No module named 'distutils'`
299
+ * **Cause**: You are running Python 3.12+ where `distutils` was removed.
300
+ * **Fix**: Ensure `setuptools` is installed in your virtual environment:
301
+ ```bash
302
+ pip install setuptools
303
+ ```
304
+
305
+ ### Q2: `SessionNotCreatedException: This version of ChromeDriver only supports Chrome version X`
306
+ * **Cause**: Your installed Chrome version was updated, but the driver cache is stale or a version was hardcoded.
307
+ * **Fix**: Leave `version=None` in `get_search(...)` so it matches automatically. You can also delete the driver cache directory located in `%APPDATA%\undetected_chromedriver` (Windows) or `~/.local/share/undetected_chromedriver` (Linux/Mac).
308
+
309
+ ### Q3: `OSError: [WinError 6] The handle is invalid` on script exit
310
+ * **Cause**: On Windows, when Chrome closes its background communication pipes, Python's garbage collection occasionally warns that the process handle closed early.
311
+ * **Status**: This is harmless and does not affect the scraped data. The script includes a safe deletion hook (`_safe_del`) to suppress this warning.
312
+
313
+ ### Q4: Does this work on cloud servers (AWS / DigitalOcean / Heroku)?
314
+ * **Note**: Google flags datacenter IP ranges very aggressively. If running on a cloud VM, install standard Chrome (`google-chrome-stable`), `xvfb` for display emulation, and route traffic through residential proxies to prevent CAPTCHAs.
315
+
316
+ ---
317
+
318
+ ## License
319
+
320
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,294 @@
1
+ # Google Search & AI Overview Scraper
2
+
3
+ A robust, stealthy Python scraper for Google Search results. Built using `undetected-chromedriver` and `parsel`, this tool automatically bypasses Google's bot detection, extracts **Google AI Overviews**, scrapes clean organic search results (titles, genuine snippets, provider domains, dates), and automatically resolves Google's `/goto?url=...` encrypted redirect links into their **original destination URLs**.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ - [Features](#features)
10
+ - [Prerequisites](#prerequisites)
11
+ - [Installation](#installation)
12
+ - [Quick Start](#quick-start)
13
+ - [Detailed Parameter Reference](#detailed-parameter-reference)
14
+ - [Response Structure & Schema](#response-structure--schema)
15
+ - [Advanced Usage Examples](#advanced-usage-examples)
16
+ - [1. Scraping Multiple Pages](#1-scraping-multiple-pages)
17
+ - [2. Extracting AI Overview Answers](#2-extracting-ai-overview-answers)
18
+ - [3. Running in Visible (Non-Headless) Mode](#3-running-in-visible-non-headless-mode)
19
+ - [4. Ultra-Fast Scraping (Disabling URL Resolution)](#4-ultra-fast-scraping-disabling-url-resolution)
20
+ - [5. Pinning a Specific Chrome Version](#5-pinning-a-specific-chrome-version)
21
+ - [Troubleshooting & FAQs](#troubleshooting--faqs)
22
+
23
+ ---
24
+
25
+ ## Features
26
+
27
+ - ๐Ÿ›ก๏ธ **Anti-Bot Evasion**: Employs `undetected-chromedriver` with native browser fingerprinting to evade Google CAPTCHA blocks.
28
+ - ๐Ÿค– **Google AI Overview Extraction**: Captures the generated AI summary answer and cited sources whenever present.
29
+ - ๐Ÿ”— **Automatic Redirect Unmasking**: Uses concurrent thread pooling to automatically resolve Google's `/goto?url=CAES...` redirects into full, direct landing page URLs (e.g. `https://en.wikipedia.org/...`).
30
+ - ๐Ÿงน **Clean Snippet Parsing**: Specifically targets Google's snippet elements while stripping inline scripts, image loaders, and feedback modals.
31
+ - ๐ŸชŸ **Stealth Headless Mode**: Rather than using detectable `--headless=new` flags that trigger bot detection, runs off-screen (`--window-position=-52000,-52000`) so it doesn't disturb your desktop.
32
+ - ๐Ÿ“„ **Multi-Page Pagination**: Supports scraping across multiple pages (`start=0`, `start=10`, etc.) with URL deduplication.
33
+
34
+ ---
35
+
36
+ ## Prerequisites
37
+
38
+ 1. **Python 3.10+** (Python 3.10, 3.11, or 3.12+).
39
+ 2. **Google Chrome Browser**: A standard desktop installation of Google Chrome must be installed on your machine.
40
+
41
+ ---
42
+
43
+ ## Installation
44
+
45
+ ### 1. Clone the Repository
46
+
47
+ ```bash
48
+ git clone https://github.com/your-username/google-search.git
49
+ cd google-search
50
+ ```
51
+
52
+ ### 2. Create and Activate a Virtual Environment
53
+
54
+ #### On Windows (PowerShell):
55
+ ```powershell
56
+ python -m venv .venv
57
+ .venv\Scripts\Activate.ps1
58
+ ```
59
+
60
+ #### On Linux / macOS:
61
+ ```bash
62
+ python3 -m venv .venv
63
+ source .venv/bin/activate
64
+ ```
65
+
66
+ ### 3. Install Dependencies
67
+
68
+ ```bash
69
+ pip install -r requirements.txt
70
+ ```
71
+
72
+ > **Note for Python 3.12+ users:** `setuptools` is included in `requirements.txt`. It is required because Python 3.12 removed `distutils`, which `undetected-chromedriver` relies on for version resolution.
73
+
74
+ ---
75
+
76
+ ## Quick Start
77
+
78
+ Create a script (or run directly from `google_search/google_search.py`):
79
+
80
+ ```python
81
+ from google_search.google_search import get_search
82
+
83
+ # Run a simple search
84
+ response = get_search("Who won the 2022 FIFA World Cup?", pages=1)
85
+
86
+ # Check if Google provided an AI Overview
87
+ if response.get("ai_response"):
88
+ print("๐Ÿค– AI Overview:")
89
+ print(response["ai_response"]["text"])
90
+ print("-" * 50)
91
+
92
+ # Print organic search results
93
+ print(f"Found {response['meta']['result_count']} results:\n")
94
+ for item in response["results"]:
95
+ print(f"Title: {item['title']}")
96
+ print(f"URL: {item['url']}")
97
+ print(f"Provider: {item.get('provider')}")
98
+ print(f"Description: {item.get('description')}")
99
+ print(f"Date: {item.get('date')}")
100
+ print("-" * 50)
101
+ ```
102
+
103
+ ---
104
+
105
+ ## Detailed Parameter Reference
106
+
107
+ The primary entry point is the function:
108
+
109
+ ```python
110
+ def get_search(
111
+ query: str,
112
+ pages: int = 1,
113
+ version: int | None = None,
114
+ headless: bool = True,
115
+ resolve_urls: bool = True
116
+ ) -> dict:
117
+ ```
118
+
119
+ ### Parameter Details
120
+
121
+ | Parameter | Type | Default | Description |
122
+ | :--- | :--- | :--- | :--- |
123
+ | `query` | `str` | *(Required)* | The search query string (e.g., `"Python web scraping"`, `"World Cup 2022 winner"`). Special characters and spaces are safely encoded. |
124
+ | `pages` | `int` | `1` | Number of search result pages to scrape. <br>โ€ข Set to `1` for the first 10 results.<br>โ€ข Set to `2` or higher to paginate (`&start=10`, `&start=20`, etc.).<br>โ€ข If set to `0` or `None`, it scrapes until no more results are found. |
125
+ | `version` | `int \| None` | `None` | The major version of Google Chrome installed on your machine (e.g., `128`, `130`, `155`). <br>โ€ข If `None` (recommended), `undetected-chromedriver` automatically detects your installed Chrome version and downloads the matching driver.<br>โ€ข Pin this only if auto-detection fails or you have multiple browser versions. |
126
+ | `headless` | `bool` | `True` | Browser visibility mode:<br>โ€ข `True` *(default)*: Spawns the browser pushed off-screen (`--window-position=-52000,-52000`). It operates invisibly without popping up windows or triggering Google's headless detection CAPTCHAs.<br>โ€ข `False`: Launches Chrome in normal visible mode so you can see navigation, scrolling, and debugging in real time. |
127
+ | `resolve_urls` | `bool` | `True` | Redirect resolution setting:<br>โ€ข `True` *(default)*: Concurrent background threads send HTTP requests to Google's `/goto?url=...` redirect tokens to extract and return the real landing page URL (e.g., `https://en.wikipedia.org/...`).<br>โ€ข `False`: Skips redirect resolution and leaves the raw Google redirect URLs. Useful if you want the absolute fastest scraping speed and do not need direct external URLs. |
128
+
129
+ ---
130
+
131
+ ## Response Structure & Schema
132
+
133
+ The function returns a Python dictionary with the following schema:
134
+
135
+ ```python
136
+ {
137
+ "query": "Who won the 2022 FIFA World Cup?",
138
+ "ai_response": {
139
+ "text": "The Argentina national football team won the 2022 FIFA World Cup...",
140
+ "sources": [
141
+ {
142
+ "title": "Learn more",
143
+ "url": "https://support.google.com/websearch?p=ai_overviews",
144
+ "provider": "support.google.com",
145
+ "description": "AI responses may include mistakes. Learn more",
146
+ "type": "ai_source"
147
+ }
148
+ ]
149
+ },
150
+ "results": [
151
+ {
152
+ "title": "2022 FIFA World Cup final",
153
+ "url": "https://en.wikipedia.org/wiki/2022_FIFA_World_Cup_final",
154
+ "provider": "en.wikipedia.org",
155
+ "description": "They defeated Poland 3โ€“1 in the round of 16, England 2โ€“1 in the quarter-final, and Morocco 2โ€“0 in the semi-final. The final took place in front of 88,966 ...",
156
+ "date": "18 December 2022",
157
+ "type": "organic"
158
+ },
159
+ {
160
+ "title": "FIFA World Cup Qatar 2022โ„ข",
161
+ "url": "https://www.fifa.com/en/tournaments/mens/worldcup/qatar2022",
162
+ "provider": "fifa.com",
163
+ "description": "The FIFA World Cup Qatar 2022 was played from 20 November to 18 December 2022. 32 teams competed across 64 matches in the 22nd edition of the tournament.",
164
+ "date": "18 December 2022",
165
+ "type": "organic"
166
+ }
167
+ ],
168
+ "urls": [
169
+ "https://en.wikipedia.org/wiki/2022_FIFA_World_Cup_final",
170
+ "https://www.fifa.com/en/tournaments/mens/worldcup/qatar2022"
171
+ ],
172
+ "meta": {
173
+ "result_count": 7,
174
+ "url_count": 7,
175
+ "has_ai_response": True,
176
+ "pages_scraped": 1
177
+ }
178
+ }
179
+ ```
180
+
181
+ ### Field Descriptions
182
+
183
+ - **`query`** (`str`): The query string executed.
184
+ - **`ai_response`** (`dict | None`): Contains Google's AI Overview answer. If Google did not generate an AI Overview for the query, this is `None`.
185
+ - **`text`** (`str`): Clean text of the AI Overview answer.
186
+ - **`sources`** (`list[dict]`): List of sources referenced by the AI answer.
187
+ - **`results`** (`list[dict]`): Deduplicated list of organic search results:
188
+ - **`title`** (`str`): Title of the web page.
189
+ - **`url`** (`str`): Original destination URL (unmasked from Google redirect).
190
+ - **`provider`** (`str`): Domain or provider name (e.g., `en.wikipedia.org`, `fifa.com`).
191
+ - **`description`** (`str`): Clean search snippet/summary from Google (with scripts and UI noise filtered).
192
+ - **`date`** (`str | None`): Publish or update date if Google displayed one.
193
+ - **`type`** (`str`): Type of result (`"organic"`).
194
+ - **`urls`** (`list[str]`): Deduplicated list of all external URLs found across the scraped pages.
195
+ - **`meta`** (`dict`):
196
+ - **`result_count`** (`int`): Total number of organic results returned.
197
+ - **`url_count`** (`int`): Total count of unique links.
198
+ - **`has_ai_response`** (`bool`): `True` if an AI overview was detected and parsed.
199
+ - **`pages_scraped`** (`int`): Number of Google result pages successfully processed.
200
+
201
+ ---
202
+
203
+ ## Advanced Usage Examples
204
+
205
+ ### 1. Scraping Multiple Pages
206
+
207
+ To scrape 3 pages of results (up to ~30 results):
208
+
209
+ ```python
210
+ from google_search.google_search import get_search
211
+
212
+ results = get_search("machine learning tutorials", pages=3)
213
+ print(f"Scraped {results['meta']['pages_scraped']} pages.")
214
+ print(f"Total results: {results['meta']['result_count']}")
215
+ ```
216
+
217
+ ### 2. Extracting AI Overview Answers
218
+
219
+ Google often provides generative AI summaries for informative or question queries:
220
+
221
+ ```python
222
+ from google_search.google_search import get_search
223
+
224
+ data = get_search("How does photosynthesis work?", pages=1)
225
+
226
+ if data["meta"]["has_ai_response"]:
227
+ ai = data["ai_response"]
228
+ print("Answer:\n", ai["text"])
229
+ print("\nCited Sources:")
230
+ for s in ai["sources"]:
231
+ print(f"- {s['title']} ({s['url']})")
232
+ else:
233
+ print("No AI Overview generated for this query.")
234
+ ```
235
+
236
+ ### 3. Running in Visible (Non-Headless) Mode
237
+
238
+ If you are debugging or want to observe Chrome typing and scrolling:
239
+
240
+ ```python
241
+ from google_search.google_search import get_search
242
+
243
+ data = get_search("latest tech news", pages=1, headless=False)
244
+ ```
245
+
246
+ ### 4. Ultra-Fast Scraping (Disabling URL Resolution)
247
+
248
+ If you only need titles and snippet text and do not require resolved destination URLs, pass `resolve_urls=False`:
249
+
250
+ ```python
251
+ from google_search.google_search import get_search
252
+
253
+ # Saves ~1 second of network time by skipping HTTP 302 redirect checks
254
+ data = get_search("fast query", pages=1, resolve_urls=False)
255
+ ```
256
+
257
+ ### 5. Pinning a Specific Chrome Version
258
+
259
+ If auto-detection fails or you have a standalone Chrome version installed:
260
+
261
+ ```python
262
+ from google_search.google_search import get_search
263
+
264
+ # Forces undetected-chromedriver to download and match Chrome 130 driver
265
+ data = get_search("Python tutorials", version=130)
266
+ ```
267
+
268
+ ---
269
+
270
+ ## Troubleshooting & FAQs
271
+
272
+ ### Q1: `ModuleNotFoundError: No module named 'distutils'`
273
+ * **Cause**: You are running Python 3.12+ where `distutils` was removed.
274
+ * **Fix**: Ensure `setuptools` is installed in your virtual environment:
275
+ ```bash
276
+ pip install setuptools
277
+ ```
278
+
279
+ ### Q2: `SessionNotCreatedException: This version of ChromeDriver only supports Chrome version X`
280
+ * **Cause**: Your installed Chrome version was updated, but the driver cache is stale or a version was hardcoded.
281
+ * **Fix**: Leave `version=None` in `get_search(...)` so it matches automatically. You can also delete the driver cache directory located in `%APPDATA%\undetected_chromedriver` (Windows) or `~/.local/share/undetected_chromedriver` (Linux/Mac).
282
+
283
+ ### Q3: `OSError: [WinError 6] The handle is invalid` on script exit
284
+ * **Cause**: On Windows, when Chrome closes its background communication pipes, Python's garbage collection occasionally warns that the process handle closed early.
285
+ * **Status**: This is harmless and does not affect the scraped data. The script includes a safe deletion hook (`_safe_del`) to suppress this warning.
286
+
287
+ ### Q4: Does this work on cloud servers (AWS / DigitalOcean / Heroku)?
288
+ * **Note**: Google flags datacenter IP ranges very aggressively. If running on a cloud VM, install standard Chrome (`google-chrome-stable`), `xvfb` for display emulation, and route traffic through residential proxies to prevent CAPTCHAs.
289
+
290
+ ---
291
+
292
+ ## License
293
+
294
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1,4 @@
1
+ from .google_search import get_search
2
+ from .utils.parser import GoogleHTMLParser
3
+
4
+ __all__ = ["get_search", "GoogleHTMLParser"]