{"id":4426,"date":"2025-05-22T09:52:21","date_gmt":"2025-05-22T09:52:21","guid":{"rendered":"https:\/\/ingenio.vip\/?post_type=tools-pages&#038;p=4426"},"modified":"2025-05-29T15:58:36","modified_gmt":"2025-05-29T15:58:36","slug":"bulk-geo-ip-locator","status":"publish","type":"tools-pages","link":"https:\/\/ingenio.vip\/zh\/tools-pages\/bulk-geo-ip-locator\/","title":{"rendered":"Bulk Geo IP Locator"},"content":{"rendered":"<div class=\"ingenio-tool geo-ip-locator-wrapper\" role=\"region\" aria-labelledby=\"geo-ip-locator-title\">\r\n  <div class=\"geo-ip-locator\">\r\n    <h2 id=\"geo-ip-locator-title\">\ud83c\udf0d Bulk Geo IP Locator<\/h2>\r\n    \r\n    <div class=\"locator-controls\">\r\n      <div class=\"input-group\">\r\n        <label for=\"ip-input\">Enter IP Addresses:<\/label>\r\n        <textarea id=\"ip-input\" rows=\"5\" placeholder=\"Enter one IP address per line or separate with commasExample: 8.8.8.81.1.1.1208.67.222.222\"><\/textarea>\r\n      <\/div>\r\n      \r\n      <div class=\"action-group\">\r\n        <button id=\"lookup-ips\" class=\"action-btn\">\ud83d\udd0d Lookup IPs<\/button>\r\n        <button id=\"clear-ips\" class=\"action-btn\">\ud83d\uddd1\ufe0f Clear<\/button>\r\n        <button id=\"sample-ips\" class=\"action-btn\">\ud83d\udccb Sample IPs<\/button>\r\n      <\/div>\r\n      \r\n      <div class=\"options-group\">\r\n        <label for=\"api-provider\">Data Provider:<\/label>\r\n        <select id=\"api-provider\">\r\n          <option value=\"ipapi\">IPAPI (Free Tier)<\/option>\r\n          <option value=\"ipgeolocation\">IPGeolocation<\/option>\r\n          <option value=\"ip2location\">IP2Location<\/option>\r\n          <option value=\"maxmind\">MaxMind<\/option>\r\n        <\/select>\r\n        \r\n        <label for=\"output-format\">Output Format:<\/label>\r\n        <select id=\"output-format\">\r\n          <option value=\"table\">Table View<\/option>\r\n          <option value=\"json\">JSON<\/option>\r\n          <option value=\"csv\">CSV<\/option>\r\n          <option value=\"xml\">XML<\/option>\r\n        <\/select>\r\n        \r\n        <label for=\"fields-to-show\">Fields to Show:<\/label>\r\n        <select id=\"fields-to-show\" multiple>\r\n          <option value=\"ip\" selected>IP Address<\/option>\r\n          <option value=\"country\" selected>Country<\/option>\r\n          <option value=\"country_code\" selected>Country Code<\/option>\r\n          <option value=\"region\" selected>Region<\/option>\r\n          <option value=\"city\" selected>City<\/option>\r\n          <option value=\"zip\">Zip Code<\/option>\r\n          <option value=\"lat\">Latitude<\/option>\r\n          <option value=\"lon\">Longitude<\/option>\r\n          <option value=\"timezone\">Timezone<\/option>\r\n          <option value=\"isp\">ISP<\/option>\r\n          <option value=\"org\">Organization<\/option>\r\n          <option value=\"as\">AS Number<\/option>\r\n          <option value=\"proxy\">Proxy\/VPN<\/option>\r\n        <\/select>\r\n      <\/div>\r\n    <\/div>\r\n    \r\n    <div class=\"locator-results\">\r\n      <div class=\"results-header\">\r\n        <h3>\ud83d\udcca Lookup Results<\/h3>\r\n        <div class=\"results-actions\">\r\n          <button id=\"export-results\" class=\"action-btn\">\ud83d\udcbe Export Results<\/button>\r\n          <button id=\"copy-results\" class=\"action-btn\">\ud83d\udccb Copy to Clipboard<\/button>\r\n        <\/div>\r\n      <\/div>\r\n      \r\n      <div class=\"results-container\">\r\n        <div id=\"results-table\" class=\"results-table\">\r\n          <table>\r\n            <thead>\r\n              <tr>\r\n                <th>IP Address<\/th>\r\n                <th>Country<\/th>\r\n                <th>Country Code<\/th>\r\n                <th>Region<\/th>\r\n                <th>City<\/th>\r\n                <th>ISP<\/th>\r\n                <th>Map<\/th>\r\n              <\/tr>\r\n            <\/thead>\r\n            <tbody id=\"results-body\">\r\n              <tr>\r\n                <td colspan=\"7\">Lookup results will appear here<\/td>\r\n              <\/tr>\r\n            <\/tbody>\r\n          <\/table>\r\n        <\/div>\r\n        \r\n        <div id=\"results-json\" class=\"results-json\" style=\"display:none;\">\r\n          <pre><code id=\"json-output\">[]<\/code><\/pre>\r\n        <\/div>\r\n        \r\n        <div id=\"results-csv\" class=\"results-csv\" style=\"display:none;\">\r\n          <pre><code id=\"csv-output\"><\/code><\/pre>\r\n        <\/div>\r\n        \r\n        <div id=\"results-xml\" class=\"results-xml\" style=\"display:none;\">\r\n          <pre><code id=\"xml-output\">&lt;ipgeo&gt;&lt;\/ipgeo&gt;<\/code><\/pre>\r\n        <\/div>\r\n      <\/div>\r\n      \r\n      <div class=\"results-stats\">\r\n        <div class=\"stat-card\">\r\n          <div class=\"stat-value\" id=\"total-ips\">0<\/div>\r\n          <div class=\"stat-label\">IPs Processed<\/div>\r\n        <\/div>\r\n        <div class=\"stat-card\">\r\n          <div class=\"stat-value\" id=\"successful\">0<\/div>\r\n          <div class=\"stat-label\">Successful<\/div>\r\n        <\/div>\r\n        <div class=\"stat-card\">\r\n          <div class=\"stat-value\" id=\"failed\">0<\/div>\r\n          <div class=\"stat-label\">Failed<\/div>\r\n        <\/div>\r\n        <div class=\"stat-card\">\r\n          <div class=\"stat-value\" id=\"private-ips\">0<\/div>\r\n          <div class=\"stat-label\">Private IPs<\/div>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n    \r\n    <div class=\"ip-geo-info\">\r\n      <h3>\u2139\ufe0f About IP Geolocation<\/h3>\r\n      <div class=\"info-content\">\r\n        <p>IP geolocation is the process of determining the physical location of an IP address. This information typically includes:<\/p>\r\n        <ul>\r\n          <li><strong>Country<\/strong>: The country where the IP is registered<\/li>\r\n          <li><strong>Region\/State<\/strong>: The regional location within the country<\/li>\r\n          <li><strong>City<\/strong>: The city or metropolitan area<\/li>\r\n          <li><strong>Coordinates<\/strong>: Approximate latitude and longitude<\/li>\r\n          <li><strong>ISP<\/strong>: Internet Service Provider managing the IP<\/li>\r\n        <\/ul>\r\n        <p><strong>Accuracy Note:<\/strong> IP geolocation is not always precise. Results may show the ISP's location rather than the actual user location, especially for mobile networks.<\/p>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<style>\r\n  .geo-ip-locator-wrapper {\r\n    background-color: var(--bg, #f5f5fa);\r\n    padding: 1rem;\r\n  }\r\n\r\n  .geo-ip-locator {\r\n    max-width: 1200px;\r\n    margin: auto;\r\n    background: var(--bg, #fff);\r\n    border-radius: 10px;\r\n    padding: 25px;\r\n    box-shadow: 0 4px 12px rgba(0,0,0,0.08);\r\n    font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Oxygen-Sans, Ubuntu, Cantarell, \"Helvetica Neue\", sans-serif;\r\n  }\r\n\r\n  .geo-ip-locator h2 {\r\n    text-align: center;\r\n    font-size: 1.8rem;\r\n    color: var(--primary, #0a66c2);\r\n    margin-bottom: 1.5rem;\r\n  }\r\n\r\n  .locator-controls {\r\n    background: #f9f9f9;\r\n    border-radius: 8px;\r\n    padding: 20px;\r\n    margin-bottom: 25px;\r\n  }\r\n\r\n  .input-group {\r\n    display: flex;\r\n    flex-direction: column;\r\n    margin-bottom: 20px;\r\n  }\r\n\r\n  .input-group label {\r\n    margin-bottom: 8px;\r\n    font-size: 0.9rem;\r\n    font-weight: 500;\r\n    color: #333;\r\n  }\r\n\r\n  .input-group textarea {\r\n    padding: 10px;\r\n    border: 1px solid #ddd;\r\n    border-radius: 6px;\r\n    font-size: 1rem;\r\n    resize: vertical;\r\n    font-family: monospace;\r\n  }\r\n\r\n  .action-group {\r\n    display: flex;\r\n    gap: 10px;\r\n    margin-bottom: 20px;\r\n  }\r\n\r\n  .action-btn {\r\n    padding: 10px 15px;\r\n    border: none;\r\n    border-radius: 6px;\r\n    cursor: pointer;\r\n    font-size: 0.95rem;\r\n    transition: all 0.2s;\r\n    display: flex;\r\n    align-items: center;\r\n    gap: 6px;\r\n  }\r\n\r\n  .action-btn:hover {\r\n    transform: translateY(-1px);\r\n    box-shadow: 0 2px 5px rgba(0,0,0,0.1);\r\n  }\r\n\r\n  #lookup-ips {\r\n    background: var(--primary, #0a66c2);\r\n    color: white;\r\n  }\r\n\r\n  #lookup-ips:hover {\r\n    background: var(--primary-dark, #084a9e);\r\n  }\r\n\r\n  #clear-ips, #sample-ips {\r\n    background: #f0f0f0;\r\n    color: #333;\r\n  }\r\n\r\n  #clear-ips:hover, #sample-ips:hover {\r\n    background: #e0e0e0;\r\n  }\r\n\r\n  .options-group {\r\n    display: grid;\r\n    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\r\n    gap: 15px;\r\n  }\r\n\r\n  .options-group label {\r\n    display: block;\r\n    margin-bottom: 5px;\r\n    font-size: 0.9rem;\r\n    font-weight: 500;\r\n  }\r\n\r\n  .options-group select {\r\n    width: 100%;\r\n    padding: 8px;\r\n    border: 1px solid #ddd;\r\n    border-radius: 6px;\r\n    font-size: 0.9rem;\r\n  }\r\n\r\n  #fields-to-show {\r\n    height: 120px;\r\n  }\r\n\r\n  .locator-results {\r\n    background: #f9f9f9;\r\n    border-radius: 8px;\r\n    padding: 20px;\r\n    margin-bottom: 25px;\r\n  }\r\n\r\n  .results-header {\r\n    display: flex;\r\n    justify-content: space-between;\r\n    align-items: center;\r\n    margin-bottom: 20px;\r\n  }\r\n\r\n  .results-header h3 {\r\n    margin: 0;\r\n    font-size: 1.2rem;\r\n  }\r\n\r\n  .results-actions {\r\n    display: flex;\r\n    gap: 10px;\r\n  }\r\n\r\n  #export-results {\r\n    background: #4CAF50;\r\n    color: white;\r\n  }\r\n\r\n  #export-results:hover {\r\n    background: #3e8e41;\r\n  }\r\n\r\n  #copy-results {\r\n    background: #2196F3;\r\n    color: white;\r\n  }\r\n\r\n  #copy-results:hover {\r\n    background: #0b7dda;\r\n  }\r\n\r\n  .results-container {\r\n    background: white;\r\n    border-radius: 8px;\r\n    padding: 15px;\r\n    box-shadow: 0 1px 3px rgba(0,0,0,0.1);\r\n    margin-bottom: 20px;\r\n    overflow-x: auto;\r\n  }\r\n\r\n  .results-table table {\r\n    width: 100%;\r\n    border-collapse: collapse;\r\n  }\r\n\r\n  .results-table th, .results-table td {\r\n    padding: 12px 15px;\r\n    text-align: left;\r\n    border-bottom: 1px solid #eee;\r\n  }\r\n\r\n  .results-table th {\r\n    background: #f5f5f5;\r\n    font-weight: 500;\r\n    font-size: 0.85rem;\r\n  }\r\n\r\n  .results-table tr:nth-child(even) {\r\n    background: #f9f9f9;\r\n  }\r\n\r\n  .country-flag {\r\n    width: 20px;\r\n    height: 15px;\r\n    margin-right: 8px;\r\n    vertical-align: middle;\r\n  }\r\n\r\n  .map-link {\r\n    color: var(--primary, #0a66c2);\r\n    text-decoration: none;\r\n  }\r\n\r\n  .map-link:hover {\r\n    text-decoration: underline;\r\n  }\r\n\r\n  .results-json, .results-csv, .results-xml {\r\n    background: #2d2d2d;\r\n    color: #f8f8f2;\r\n    padding: 15px;\r\n    border-radius: 6px;\r\n    font-family: monospace;\r\n    font-size: 0.85rem;\r\n    white-space: pre-wrap;\r\n    overflow-x: auto;\r\n  }\r\n\r\n  .results-stats {\r\n    display: grid;\r\n    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\r\n    gap: 15px;\r\n  }\r\n\r\n  .stat-card {\r\n    background: white;\r\n    border-radius: 8px;\r\n    padding: 15px;\r\n    text-align: center;\r\n    box-shadow: 0 2px 5px rgba(0,0,0,0.05);\r\n  }\r\n\r\n  .stat-value {\r\n    font-size: 1.5rem;\r\n    font-weight: bold;\r\n    color: var(--primary, #0a66c2);\r\n    margin-bottom: 5px;\r\n  }\r\n\r\n  .stat-label {\r\n    font-size: 0.9rem;\r\n    color: #666;\r\n  }\r\n\r\n  .ip-geo-info {\r\n    background: #f9f9f9;\r\n    border-radius: 8px;\r\n    padding: 20px;\r\n  }\r\n\r\n  .ip-geo-info h3 {\r\n    margin-top: 0;\r\n    margin-bottom: 15px;\r\n    font-size: 1.2rem;\r\n  }\r\n\r\n  .info-content {\r\n    font-size: 0.9rem;\r\n    line-height: 1.6;\r\n  }\r\n\r\n  .info-content p {\r\n    margin-top: 0;\r\n  }\r\n\r\n  .info-content ul {\r\n    margin: 10px 0;\r\n    padding-left: 20px;\r\n  }\r\n\r\n  .info-content li {\r\n    margin-bottom: 8px;\r\n  }\r\n\r\n  @media (max-width: 768px) {\r\n    .action-group, .results-actions {\r\n      flex-direction: column;\r\n    }\r\n    \r\n    .action-btn {\r\n      width: 100%;\r\n      justify-content: center;\r\n    }\r\n    \r\n    .results-header {\r\n      flex-direction: column;\r\n      align-items: flex-start;\r\n      gap: 10px;\r\n    }\r\n    \r\n    .results-actions {\r\n      width: 100%;\r\n    }\r\n  }\r\n<\/style>\r\n\r\n<script>\r\ndocument.addEventListener(\"DOMContentLoaded\", function() {\r\n  \/\/ DOM elements\r\n  const ipInput = document.getElementById('ip-input');\r\n  const lookupBtn = document.getElementById('lookup-ips');\r\n  const clearBtn = document.getElementById('clear-ips');\r\n  const sampleBtn = document.getElementById('sample-ips');\r\n  const apiProvider = document.getElementById('api-provider');\r\n  const outputFormat = document.getElementById('output-format');\r\n  const fieldsToShow = document.getElementById('fields-to-show');\r\n  const exportBtn = document.getElementById('export-results');\r\n  const copyBtn = document.getElementById('copy-results');\r\n  \r\n  \/\/ Results elements\r\n  const resultsBody = document.getElementById('results-body');\r\n  const jsonOutput = document.getElementById('json-output');\r\n  const csvOutput = document.getElementById('csv-output');\r\n  const xmlOutput = document.getElementById('xml-output');\r\n  const resultsTable = document.getElementById('results-table');\r\n  const resultsJson = document.getElementById('results-json');\r\n  const resultsCsv = document.getElementById('results-csv');\r\n  const resultsXml = document.getElementById('results-xml');\r\n  \r\n  \/\/ Stats elements\r\n  const totalIps = document.getElementById('total-ips');\r\n  const successful = document.getElementById('successful');\r\n  const failed = document.getElementById('failed');\r\n  const privateIps = document.getElementById('private-ips');\r\n  \r\n  \/\/ Lookup IPs\r\n  lookupBtn.addEventListener('click', function() {\r\n    const ipText = ipInput.value.trim();\r\n    if (!ipText) {\r\n      alert('Please enter at least one IP address');\r\n      return;\r\n    }\r\n    \r\n    \/\/ Parse IPs from input\r\n    const ipList = parseIps(ipText);\r\n    if (ipList.length === 0) {\r\n      alert('No valid IP addresses found');\r\n      return;\r\n    }\r\n    \r\n    \/\/ Show loading state\r\n    lookupBtn.disabled = true;\r\n    lookupBtn.innerHTML = '<span class=\"spinner\"><\/span> Processing...';\r\n    \r\n    \/\/ Reset stats\r\n    totalIps.textContent = ipList.length;\r\n    successful.textContent = '0';\r\n    failed.textContent = '0';\r\n    privateIps.textContent = '0';\r\n    \r\n    \/\/ Process IPs\r\n    processIpList(ipList);\r\n  });\r\n  \r\n  \/\/ Clear inputs\r\n  clearBtn.addEventListener('click', function() {\r\n    ipInput.value = '';\r\n    resultsBody.innerHTML = '<tr><td colspan=\"7\">Lookup results will appear here<\/td><\/tr>';\r\n    jsonOutput.textContent = '[]';\r\n    csvOutput.textContent = '';\r\n    xmlOutput.textContent = '<ipgeo><\/ipgeo>';\r\n    totalIps.textContent = '0';\r\n    successful.textContent = '0';\r\n    failed.textContent = '0';\r\n    privateIps.textContent = '0';\r\n    showOutputFormat('table');\r\n  });\r\n  \r\n  \/\/ Load sample IPs\r\n  sampleBtn.addEventListener('click', function() {\r\n    ipInput.value = `8.8.8.8\\n1.1.1.1\\n208.67.222.222\\n142.250.190.46\\n151.101.1.69\\n`;\r\n  });\r\n  \r\n  \/\/ Change output format\r\n  outputFormat.addEventListener('change', function() {\r\n    showOutputFormat(this.value);\r\n  });\r\n  \r\n  \/\/ Export results\r\n  exportBtn.addEventListener('click', function() {\r\n    const format = outputFormat.value;\r\n    let content, filename, mimeType;\r\n    \r\n    switch(format) {\r\n      case 'json':\r\n        content = jsonOutput.textContent;\r\n        filename = 'ip-geo-results.json';\r\n        mimeType = 'application\/json';\r\n        break;\r\n      case 'csv':\r\n        content = csvOutput.textContent;\r\n        filename = 'ip-geo-results.csv';\r\n        mimeType = 'text\/csv';\r\n        break;\r\n      case 'xml':\r\n        content = xmlOutput.textContent;\r\n        filename = 'ip-geo-results.xml';\r\n        mimeType = 'application\/xml';\r\n        break;\r\n      default:\r\n        \/\/ For table, we'll create a CSV\r\n        const rows = [];\r\n        const headers = [];\r\n        \r\n        \/\/ Get headers from table\r\n        document.querySelectorAll('#results-table th').forEach(th => {\r\n          headers.push(th.textContent);\r\n        });\r\n        \r\n        rows.push(headers.join(','));\r\n        \r\n        \/\/ Get data from table\r\n        document.querySelectorAll('#results-table tbody tr').forEach(tr => {\r\n          const cells = [];\r\n          tr.querySelectorAll('td').forEach(td => {\r\n            \/\/ Remove flag image from country cell\r\n            if (td.querySelector('.country-flag')) {\r\n              cells.push(td.textContent.trim());\r\n            } else {\r\n              cells.push(td.textContent);\r\n            }\r\n          });\r\n          rows.push(cells.join(','));\r\n        });\r\n        \r\n        content = rows.join('\\n');\r\n        filename = 'ip-geo-results.csv';\r\n        mimeType = 'text\/csv';\r\n    }\r\n    \r\n    downloadFile(filename, content, mimeType);\r\n  });\r\n  \r\n  \/\/ Copy results\r\n  copyBtn.addEventListener('click', function() {\r\n    const format = outputFormat.value;\r\n    let content;\r\n    \r\n    switch(format) {\r\n      case 'json':\r\n        content = jsonOutput.textContent;\r\n        break;\r\n      case 'csv':\r\n        content = csvOutput.textContent;\r\n        break;\r\n      case 'xml':\r\n        content = xmlOutput.textContent;\r\n        break;\r\n      default:\r\n        \/\/ For table, we'll copy the JSON\r\n        content = jsonOutput.textContent;\r\n    }\r\n    \r\n    navigator.clipboard.writeText(content).then(() => {\r\n      \/\/ Show feedback\r\n      const originalText = copyBtn.innerHTML;\r\n      copyBtn.innerHTML = '\u2705 Copied!';\r\n      setTimeout(() => {\r\n        copyBtn.innerHTML = originalText;\r\n      }, 2000);\r\n    });\r\n  });\r\n  \r\n  \/\/ Parse IPs from input text\r\n  function parseIps(text) {\r\n    \/\/ Split by newlines or commas\r\n    const ipStrings = text.split(\/[\\n,]+\/).map(s => s.trim()).filter(s => s);\r\n    const ipList = [];\r\n    const privateRanges = [\r\n      \/^10\\.\/, \r\n      \/^172\\.(1[6-9]|2\\d|3[01])\\.\/, \r\n      \/^192\\.168\\.\/,\r\n      \/^127\\.\/,\r\n      \/^::1$\/,\r\n      \/^fc00::\/,\r\n      \/^fe80::\/\r\n    ];\r\n    \r\n    let privateCount = 0;\r\n    \r\n    ipStrings.forEach(ip => {\r\n      \/\/ Basic IP validation\r\n      if (\/^(?:[0-9]{1,3}\\.){3}[0-9]{1,3}$\/.test(ip) || \/^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$\/.test(ip)) {\r\n        \/\/ Check for private IPs\r\n        const isPrivate = privateRanges.some(regex => regex.test(ip));\r\n        if (isPrivate) {\r\n          privateCount++;\r\n        } else {\r\n          ipList.push(ip);\r\n        }\r\n      }\r\n    });\r\n    \r\n    privateIps.textContent = privateCount;\r\n    return ipList;\r\n  }\r\n  \r\n  \/\/ Process list of IPs\r\n  function processIpList(ipList) {\r\n    const results = [];\r\n    let processed = 0;\r\n    let successCount = 0;\r\n    let failCount = 0;\r\n    \r\n    \/\/ For demo purposes, we'll simulate API calls with timeout\r\n    ipList.forEach((ip, index) => {\r\n      setTimeout(() => {\r\n        \/\/ Simulate API call with random success\/failure\r\n        const success = Math.random() > 0.1; \/\/ 90% success rate for demo\r\n        \r\n        if (success) {\r\n          successCount++;\r\n          successful.textContent = successCount;\r\n          \r\n          \/\/ Generate mock data for demo\r\n          const mockData = generateMockGeoData(ip);\r\n          results.push(mockData);\r\n          \r\n          \/\/ Update UI\r\n          updateResultsUI(results);\r\n        } else {\r\n          failCount++;\r\n          failed.textContent = failCount;\r\n          \r\n          \/\/ Add error result\r\n          results.push({\r\n            ip: ip,\r\n            error: 'Lookup failed'\r\n          });\r\n        }\r\n        \r\n        processed++;\r\n        \r\n        \/\/ When all IPs are processed\r\n        if (processed === ipList.length) {\r\n          lookupBtn.disabled = false;\r\n          lookupBtn.textContent = '\ud83d\udd0d Lookup IPs';\r\n          \r\n          \/\/ Update JSON, CSV, XML outputs\r\n          updateAlternativeOutputs(results);\r\n        }\r\n      }, index * 300); \/\/ Stagger requests for demo\r\n    });\r\n  }\r\n  \r\n  \/\/ Generate mock geo data for demo\r\n  function generateMockGeoData(ip) {\r\n    const countries = [\r\n      { name: 'United States', code: 'US' },\r\n      { name: 'United Kingdom', code: 'GB' },\r\n      { name: 'Canada', code: 'CA' },\r\n      { name: 'Australia', code: 'AU' },\r\n      { name: 'Germany', code: 'DE' },\r\n      { name: 'Japan', code: 'JP' },\r\n      { name: 'France', code: 'FR' },\r\n      { name: 'Brazil', code: 'BR' }\r\n    ];\r\n    \r\n    const cities = {\r\n      US: ['New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix'],\r\n      GB: ['London', 'Manchester', 'Birmingham', 'Glasgow', 'Liverpool'],\r\n      CA: ['Toronto', 'Vancouver', 'Montreal', 'Calgary', 'Ottawa'],\r\n      AU: ['Sydney', 'Melbourne', 'Brisbane', 'Perth', 'Adelaide'],\r\n      DE: ['Berlin', 'Munich', 'Hamburg', 'Frankfurt', 'Cologne'],\r\n      JP: ['Tokyo', 'Osaka', 'Yokohama', 'Nagoya', 'Sapporo'],\r\n      FR: ['Paris', 'Marseille', 'Lyon', 'Toulouse', 'Nice'],\r\n      BR: ['S\u00e3o Paulo', 'Rio de Janeiro', 'Bras\u00edlia', 'Salvador', 'Fortaleza']\r\n    };\r\n    \r\n    const isps = [\r\n      'Comcast', 'AT&T', 'Verizon', 'Spectrum', \r\n      'BT', 'Virgin Media', 'Rogers', 'Bell Canada',\r\n      'Telstra', 'Optus', 'Deutsche Telekom', 'Vodafone',\r\n      'NTT', 'SoftBank', 'Orange', 'Telef\u00f3nica'\r\n    ];\r\n    \r\n    const country = countries[Math.floor(Math.random() * countries.length)];\r\n    const city = cities[country.code][Math.floor(Math.random() * cities[country.code].length)];\r\n    const isp = isps[Math.floor(Math.random() * isps.length)];\r\n    \r\n    return {\r\n      ip: ip,\r\n      country: country.name,\r\n      country_code: country.code,\r\n      region: 'State\/Region',\r\n      city: city,\r\n      zip: Math.floor(10000 + Math.random() * 90000).toString(),\r\n      lat: (Math.random() * 180 - 90).toFixed(6),\r\n      lon: (Math.random() * 360 - 180).toFixed(6),\r\n      timezone: 'UTC' + (Math.random() > 0.5 ? '+' : '-') + Math.floor(Math.random() * 12),\r\n      isp: isp,\r\n      org: isp + ' Networks',\r\n      as: 'AS' + Math.floor(Math.random() * 10000),\r\n      proxy: Math.random() > 0.8 ? 'Yes' : 'No'\r\n    };\r\n  }\r\n  \r\n  \/\/ Update results table\r\n  function updateResultsUI(results) {\r\n    \/\/ Clear existing results\r\n    resultsBody.innerHTML = '';\r\n    \r\n    \/\/ Add each result to table\r\n    results.forEach(result => {\r\n      if (result.error) {\r\n        resultsBody.innerHTML += `\r\n          <tr class=\"error-row\">\r\n            <td>${result.ip}<\/td>\r\n            <td colspan=\"6\">${result.error}<\/td>\r\n          <\/tr>\r\n        `;\r\n      } else {\r\n        const mapLink = `https:\/\/www.google.com\/maps?q=${result.lat},${result.lon}`;\r\n        resultsBody.innerHTML += `\r\n          <tr>\r\n            <td>${result.ip}<\/td>\r\n            <td><img decoding=\"async\" src=\"https:\/\/flagcdn.com\/16x12\/${result.country_code.toLowerCase()}.png\" class=\"country-flag\" alt=\"${result.country_code}\"> ${result.country}<\/td>\r\n            <td>${result.country_code}<\/td>\r\n            <td>${result.region}<\/td>\r\n            <td>${result.city}<\/td>\r\n            <td>${result.isp}<\/td>\r\n            <td><a href=\"${mapLink}\" target=\"_blank\" class=\"map-link\">View Map<\/a><\/td>\r\n          <\/tr>\r\n        `;\r\n      }\r\n    });\r\n  }\r\n  \r\n  \/\/ Update JSON, CSV, XML outputs\r\n  function updateAlternativeOutputs(results) {\r\n    \/\/ Get selected fields\r\n    const selectedFields = Array.from(fieldsToShow.selectedOptions).map(opt => opt.value);\r\n    \r\n    \/\/ Filter results to only include selected fields\r\n    const filteredResults = results.map(result => {\r\n      if (result.error) {\r\n        return { ip: result.ip, error: result.error };\r\n      }\r\n      \r\n      const filtered = {};\r\n      selectedFields.forEach(field => {\r\n        if (result[field] !== undefined) {\r\n          filtered[field] = result[field];\r\n        }\r\n      });\r\n      return filtered;\r\n    });\r\n    \r\n    \/\/ Update JSON output\r\n    jsonOutput.textContent = JSON.stringify(filteredResults, null, 2);\r\n    \r\n    \/\/ Update CSV output\r\n    if (filteredResults.length > 0) {\r\n      const fields = Object.keys(filteredResults[0]);\r\n      let csv = fields.join(',') + '\\n';\r\n      \r\n      filteredResults.forEach(result => {\r\n        const row = fields.map(field => {\r\n          if (typeof result[field] === 'object') {\r\n            return JSON.stringify(result[field]);\r\n          }\r\n          return result[field] !== undefined ? result[field] : '';\r\n        });\r\n        csv += row.join(',') + '\\n';\r\n      });\r\n      \r\n      csvOutput.textContent = csv;\r\n    }\r\n    \r\n    \/\/ Update XML output\r\n    let xml = '<ipgeo>\\n';\r\n    filteredResults.forEach(result => {\r\n      xml += '  <entry>\\n';\r\n      for (const [key, value] of Object.entries(result)) {\r\n        xml += `    <${key}>${value}<\/${key}>\\n`;\r\n      }\r\n      xml += '  <\/entry>\\n';\r\n    });\r\n    xml += '<\/ipgeo>';\r\n    xmlOutput.textContent = xml;\r\n  }\r\n  \r\n  \/\/ Show selected output format\r\n  function showOutputFormat(format) {\r\n    resultsTable.style.display = 'none';\r\n    resultsJson.style.display = 'none';\r\n    resultsCsv.style.display = 'none';\r\n    resultsXml.style.display = 'none';\r\n    \r\n    switch(format) {\r\n      case 'json':\r\n        resultsJson.style.display = 'block';\r\n        break;\r\n      case 'csv':\r\n        resultsCsv.style.display = 'block';\r\n        break;\r\n      case 'xml':\r\n        resultsXml.style.display = 'block';\r\n        break;\r\n      default:\r\n        resultsTable.style.display = 'block';\r\n    }\r\n  }\r\n  \r\n  \/\/ Download file helper\r\n  function downloadFile(filename, content, mimeType = 'text\/plain') {\r\n    const element = document.createElement('a');\r\n    const blob = new Blob([content], {type: mimeType});\r\n    element.href = URL.createObjectURL(blob);\r\n    element.download = filename;\r\n    document.body.appendChild(element);\r\n    element.click();\r\n    document.body.removeChild(element);\r\n    \r\n    setTimeout(() => {\r\n      URL.revokeObjectURL(element.href);\r\n    }, 100);\r\n  }\r\n});\r\n<\/script>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83c\udf0d Bulk Geo IP Locator<\/h2>\n\n\n\n<p>Need to locate a bunch of IP addresses \u2014 <strong>fast<\/strong>?<br>The <strong>Bulk Geo IP Locator<\/strong> is your go-to tool for transforming raw IPs into real-world insights. Upload. Analyze. Done.<\/p>\n\n\n\n<p>Whether you\u2019re monitoring traffic, analyzing suspicious logins, or just curious where your visitors are coming from, this tool does the heavy lifting for you \u2014 in bulk.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\udde0 What It Does<\/h2>\n\n\n\n<p>Drop in a list of IP addresses, and we\u2019ll fetch the following geo-location data for each:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83c\udf10 <strong>Country<\/strong><\/li>\n\n\n\n<li>\ud83c\udfd9\ufe0f <strong>City<\/strong><\/li>\n\n\n\n<li>\ud83d\udccd <strong>Region\/State<\/strong><\/li>\n\n\n\n<li>\ud83d\udef0\ufe0f <strong>Latitude &amp; Longitude<\/strong><\/li>\n\n\n\n<li>\ud83c\udfe2 <strong>ISP \/ Organization (optional)<\/strong><\/li>\n\n\n\n<li>\ud83d\udd50 <strong>Timezone Info<\/strong><\/li>\n<\/ul>\n\n\n\n<p>All in one clean table. Fully exportable.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udccc Who It&#8217;s For<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udd10 <strong>Security analysts<\/strong> tracking login attempts or attacks<\/li>\n\n\n\n<li>\ud83d\udcca <strong>Marketers &amp; webmasters<\/strong> analyzing visitor origin<\/li>\n\n\n\n<li>\ud83e\uddd1\u200d\ud83d\udcbb <strong>Sysadmins &amp; DevOps<\/strong> reviewing server logs<\/li>\n\n\n\n<li>\ud83d\udecd\ufe0f <strong>Ecommerce owners<\/strong> understanding customer locations<\/li>\n\n\n\n<li>\ud83d\udcc8 <strong>SEO geeks<\/strong> spotting regional trends<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2699\ufe0f How It Works<\/h2>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Paste or upload your list of IPs (supports .txt, .csv, or direct input)<\/li>\n\n\n\n<li>Hit <strong>\u201cLocate IPs\u201d<\/strong><\/li>\n\n\n\n<li>Boom \u2014 instant geolocation results<\/li>\n\n\n\n<li>(Optional) <strong>Export<\/strong> to CSV or JSON for further analysis<\/li>\n<\/ol>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\u2705 Why Use This Tool?<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\ude80 <strong>Handles thousands of IPs<\/strong> at once<\/li>\n\n\n\n<li>\ud83e\udded <strong>Accurate and fast<\/strong> (using reliable geolocation APIs)<\/li>\n\n\n\n<li>\ud83d\udd12 <strong>Privacy-focused<\/strong> \u2014 we don\u2019t store your IPs or data<\/li>\n\n\n\n<li>\ud83c\udf13 <strong>Dark mode ready<\/strong> \u2014 log analysis looks better at night<\/li>\n\n\n\n<li>\ud83d\udce4 <strong>Export-friendly<\/strong> \u2014 one click to download results<\/li>\n\n\n\n<li>\ud83d\udd27 <strong>Built for performance<\/strong> \u2014 no bloat, no waiting<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udcac Bonus Features (Depending on Your Setup)<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udcc2 Drag-and-drop file support<\/li>\n\n\n\n<li>\ud83e\udde9 WordPress integration via shortcode or admin tool<\/li>\n\n\n\n<li>\ud83c\udf10 Auto-detect visitor IP &amp; lookup<\/li>\n\n\n\n<li>\ud83d\udd17 Link geolocation data with Google Maps or OpenStreetMap<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83e\uddea Try It Now<\/h2>\n\n\n\n<p>Upload your IPs and see where they\u2019re coming from \u2014 across the globe, across devices.<\/p>","protected":false},"excerpt":{"rendered":"<p>\ud83c\udf0d Bulk Geo IP Locator Enter IP Addresses: \ud83d\udd0d Lookup IPs \ud83d\uddd1\ufe0f Clear \ud83d\udccb Sample IPs Data Provider: IPAPI (Free Tier) IPGeolocation IP2Location MaxMind Output Format: Table View JSON CSV XML Fields to Show: IP Address Country Country Code Region City Zip Code Latitude Longitude Timezone ISP Organization AS Number Proxy\/VPN \ud83d\udcca Lookup Results \ud83d\udcbe [&hellip;]<\/p>\n<\/p><div class=\"more-link\"><a href=\"https:\/\/ingenio.vip\/zh\/tools-pages\/bulk-geo-ip-locator\/\" class=\"btn btn-small btn--dark btn-hover-shadow\"><span class=\"text\">Continue Reading<\/span><i class=\"seoicon-right-arrow\"><\/i><\/a><\/div>","protected":false},"author":1,"featured_media":0,"menu_order":0,"template":"elementor_header_footer","meta":{"_acf_changed":false,"googlesitekit_rrm_CAowrpbbCw:productID":"","footnotes":""},"tools-categories":[],"class_list":["post-4426","tools-pages","type-tools-pages","status-publish","hentry"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/tools-pages\/4426","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/tools-pages"}],"about":[{"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/types\/tools-pages"}],"author":[{"embeddable":true,"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/users\/1"}],"version-history":[{"count":1,"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/tools-pages\/4426\/revisions"}],"predecessor-version":[{"id":4427,"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/tools-pages\/4426\/revisions\/4427"}],"wp:attachment":[{"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/media?parent=4426"}],"wp:term":[{"taxonomy":"tools-categories","embeddable":true,"href":"https:\/\/ingenio.vip\/zh\/wp-json\/wp\/v2\/tools-categories?post=4426"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}