google map scraper

google map result

当前为 2025-05-25 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         google map scraper
// @namespace    http://google.com/
// @version      2025-05-22
// @description  google map result
// @author       Web Automation Lover
// @match        https://www.google.com/maps/search/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=xiaohongshu.com
// @require      https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.16.9/xlsx.full.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    window.jsonArr = [];

    const button = document.createElement('button');
    button.innerText = 'Click to Export (0)';
    button.style.marginLeft = '10px';

    button.style.backgroundColor = '#f0f0f0';
    button.style.border = '1px solid #ccc';
    button.style.borderRadius = '5px';
    button.style.padding = '5px 10px';
    button.style.fontSize = '14px';
    button.style.cursor = 'pointer';
    button.style.transition = 'background-color 0.3s';

    button.addEventListener('mouseenter', () => {
        button.style.backgroundColor = '#e0e0e0';
    });

    button.addEventListener('mouseleave', () => {
        button.style.backgroundColor = '#f0f0f0';
    });

    button.addEventListener('click', function() {
        const ws = XLSX.utils.json_to_sheet(window.jsonArr);

        const wb = XLSX.utils.book_new();
        XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');

        const wbout = XLSX.write(wb, { type: 'binary', bookType: 'xlsx' });
        const s2ab = function(s) {
            const buf = new ArrayBuffer(s.length);
            const view = new Uint8Array(buf);
            for (let i = 0; i < s.length; i++) view[i] = s.charCodeAt(i) & 0xFF;
            return buf;
        };
        const blob = new Blob([s2ab(wbout)], { type: 'application/octet-stream' });
        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob);
        a.download = 'data.xlsx';
        a.click();

    });

    function updateButtonText() {
        button.innerText = 'Click to Export (' + window.jsonArr.length + ')';
    }

    const injectButton = () => {
        const targetDiv = document.querySelectorAll('#assistive-chips > div > div > div > div > div > div > div > div > div')[1];
        if (targetDiv && !document.querySelector('#my-custom-button')) {
            button.id = 'my-custom-button';
            targetDiv.appendChild(button);
        }
    };

    setInterval(injectButton, 1000);

    const originalOpen = XMLHttpRequest.prototype.open;
    const originalSend = XMLHttpRequest.prototype.send;

    XMLHttpRequest.prototype.open = function(method, url) {
        this._url = url;
        return originalOpen.apply(this, arguments);
    };

    XMLHttpRequest.prototype.send = function() {
        this.addEventListener('load', function() {
            if (this._url.includes('/search?tbm=map')) {

                var rspJson = JSON.parse(this.responseText.replace(`/*""*/`,""));
                var e = rspJson.d;
                var cleanedData = e.replace(`)]}'`, "");

                let parsedData = JSON.parse(cleanedData);

                let dataList = parsedData[0][1];

                let filteredData = dataList.filter(item => {
                    return item?.[14] !== undefined;
                });

                if (!filteredData || filteredData.length < 1) {
                    filteredData = parsedData[64];
                }

                if (filteredData) {
                    var formatedData = formatAllData(filteredData)
                    window.jsonArr.push(...formatedData)
                    console.log('song jsonArr:' + window.jsonArr.length)
                    updateButtonText()
                }
            }
        });

        return originalSend.apply(this, arguments);
    };


function formatAllData(allDataList) {
    return allDataList.map(d => formatDataItem(d)).filter(d => d.name)
}

function formatDataItem(item) {
    const fieldConfig = {
        fullAddress: [39],
        placeId: [78],
        kgmid: [89],
        categories: [13],
        feature: [32, 0, 1],
        cid: [10],
        featuredImage: [37, 0, 0, 6, 0],
        phones: [],
        icon: [122, 0, 1],
        name: [11],
        latitude: [9, 2],
        longitude: [9, 3],
        reviewCount: [4, 8],
        reviewURL: [4, 3, 0],
        averageRating: [4, 7],
        street: [183, 0, 0, 1, 1],
        municipality: [183, 1, 3],
        openingHours: [],
        website: [7, 0],
        domain: [7, 1]
    }

    const resultData = {}
    Object.keys(fieldConfig).forEach(key => {
        resultData[key] = handleSingleField(fieldConfig[key])
    })
    resultData.phones = handleSingleField([178, 0, 1])?.map(d => d?.[0])
    resultData.openingHours = handleSingleField([34, 1])?.map(d => [`${d[0]}:[${d[1]}]`])?.join(', ')
    resultData.googleMapsURL = "https://www.google.com/maps?cid=".concat(resultData.cid)
    resultData.googleKnowledgeURL = "https://www.google.com/search?kgmid=".concat(resultData.kgmid, "&kponly")

    resultData.phones = resultData.phones?.join?.(', ')
    resultData.categories = resultData.categories?.join?.(', ')
    resultData.street = resultData.street?.join?.(', ')

    function handleSingleField(config) {
        const itemData = item[1]
        if (!itemData) {
            return
        }
        if (!config || !config.length) {
            return
        }
        let currentData = itemData
        for (let i = 0; i < config.length; i++) {
            currentData = currentData?.[config[i]]
        }
        return currentData
    }

    return resultData
}

})();