|
| 1 | +import requests |
| 2 | +import json |
| 3 | +import time |
| 4 | +import argparse |
| 5 | +import sys |
| 6 | +from xml.etree.ElementTree import Element, SubElement, tostring |
| 7 | +import xml.dom.minidom |
| 8 | + |
| 9 | + |
| 10 | +def fetch_wigle_data(ssid, token_name, api_token, max_results): |
| 11 | + base_url = 'https://api.wigle.net/api/v2/network/search' |
| 12 | + headers = {'accept': 'application/json'} |
| 13 | + params = { |
| 14 | + 'onlymine': 'false', |
| 15 | + 'freenet': 'false', |
| 16 | + 'paynet': 'false', |
| 17 | + 'ssid': ssid, |
| 18 | + 'resultsPerPage': 100 |
| 19 | + } |
| 20 | + |
| 21 | + all_results = [] |
| 22 | + total_fetched = 0 |
| 23 | + search_after = None |
| 24 | + |
| 25 | + print(f"[*] Searching for SSID: {ssid}") |
| 26 | + |
| 27 | + while total_fetched < max_results: |
| 28 | + if search_after: |
| 29 | + params['searchAfter'] = search_after |
| 30 | + |
| 31 | + response = requests.get(base_url, headers=headers, params=params, auth=(token_name, api_token)) |
| 32 | + if response.status_code != 200: |
| 33 | + print(f"[!] Error {response.status_code}: {response.text}") |
| 34 | + break |
| 35 | + |
| 36 | + data = response.json() |
| 37 | + results = data.get('results', []) |
| 38 | + |
| 39 | + if not results: |
| 40 | + print("[*] No more results returned. Ending search.") |
| 41 | + break |
| 42 | + |
| 43 | + all_results.extend(results) |
| 44 | + total_fetched += len(results) |
| 45 | + |
| 46 | + print(f"[+] Fetched {len(results)} records (Total: {total_fetched})") |
| 47 | + |
| 48 | + if 'searchAfter' in data: |
| 49 | + search_after = data['searchAfter'] |
| 50 | + else: |
| 51 | + print("[*] No 'searchAfter' field found. Ending search.") |
| 52 | + break |
| 53 | + |
| 54 | + time.sleep(1) |
| 55 | + |
| 56 | + return all_results |
| 57 | + |
| 58 | + |
| 59 | +def save_as_json(results, output_file): |
| 60 | + with open(output_file, 'w') as f: |
| 61 | + json.dump(results, f, indent=2) |
| 62 | + print(f"[✓] Saved {len(results)} records to {output_file} (JSON)") |
| 63 | + |
| 64 | + |
| 65 | +def save_as_kml(results, output_file): |
| 66 | + kml = Element('kml', xmlns="http://www.opengis.net/kml/2.2") |
| 67 | + doc = SubElement(kml, 'Document') |
| 68 | + |
| 69 | + for entry in results: |
| 70 | + lat = entry.get('trilat') |
| 71 | + lon = entry.get('trilong') |
| 72 | + ssid = entry.get('ssid', 'unknown') |
| 73 | + bssid = entry.get('netid', 'unknown') |
| 74 | + |
| 75 | + if lat is None or lon is None: |
| 76 | + continue |
| 77 | + |
| 78 | + placemark = SubElement(doc, 'Placemark') |
| 79 | + name = SubElement(placemark, 'name') |
| 80 | + name.text = f"{ssid} ({bssid})" |
| 81 | + |
| 82 | + point = SubElement(placemark, 'Point') |
| 83 | + coords = SubElement(point, 'coordinates') |
| 84 | + coords.text = f"{lon},{lat},0" |
| 85 | + |
| 86 | + kml_str = xml.dom.minidom.parseString(tostring(kml)).toprettyxml(indent=" ") |
| 87 | + with open(output_file, 'w', encoding='utf-8') as f: |
| 88 | + f.write(kml_str) |
| 89 | + |
| 90 | + print(f"[✓] Saved {len(results)} records to {output_file} (KML)") |
| 91 | + |
| 92 | + |
| 93 | +def main(): |
| 94 | + parser = argparse.ArgumentParser(description="Fetch WiGLE results for a given SSID and save as JSON or KML.") |
| 95 | + parser.add_argument('--ssid', required=True, help='SSID to search for') |
| 96 | + parser.add_argument('--token-name', required=True, help='WiGLE Account Token Name (not your login username)') |
| 97 | + parser.add_argument('--token', required=True, help='WiGLE API token (used with the token name)') |
| 98 | + parser.add_argument('--output', default=None, help='Output file (default: <SSID>_wigle_results.json or .kml)') |
| 99 | + parser.add_argument('--max', type=int, default=1000, help='Maximum number of records to fetch (default: 1000)') |
| 100 | + parser.add_argument('--format', choices=['json', 'kml'], default='json', help='Output format: json (default) or kml') |
| 101 | + |
| 102 | + args = parser.parse_args() |
| 103 | + |
| 104 | + output_file = args.output |
| 105 | + if not output_file: |
| 106 | + output_file = f"{args.ssid}_wigle_results.{args.format}" |
| 107 | + |
| 108 | + results = fetch_wigle_data(args.ssid, args.token_name, args.token, args.max) |
| 109 | + |
| 110 | + if args.format == 'json': |
| 111 | + save_as_json(results, output_file) |
| 112 | + else: |
| 113 | + save_as_kml(results, output_file) |
| 114 | + |
| 115 | + |
| 116 | +if __name__ == '__main__': |
| 117 | + main() |
0 commit comments