在此网络抓取教程中,我们将抓取 Google 地图 ( google.com/maps ) – 一种地图服务,也是业务详细信息和评论的主要目录。
谷歌地图是一款复杂的网络软件,因此我们将使用Selenium、Playwright 等浏览器自动化工具包在 Python 中为我们呈现页面。我们将涵盖所有这三个选项,因此请随时按照您最熟悉的选项进行操作 – 让我们开始吧!
为什么要抓取谷歌地图?
谷歌地图包含大量商业资料数据,如地址、评级、电话号码和网站地址。它不仅是用于商业智能和市场分析的强大数据目录,还可以用于潜在客户生成,因为它包含业务联系详细信息。
项目设置
在本教程中,我们将主要使用浏览器自动化库(如Selenium、Playwright)的 Javascript 执行功能来检索完全呈现的 HTML 页面。
那么,哪个浏览器自动化库最适合抓取 Google 地图?
我们只需要渲染和 javascript 执行功能,所以无论您最喜欢哪个!
为了解析这些页面,我们将使用parsel——一个支持通过 CSS 或 XPath 选择器解析 HTML 的社区包。
这些包可以通过终端工具安装pip
:
# for selenium $ pip install selenium # for playwright $ pip install playwright
这些工具最强大的功能是javascript 执行功能。此功能允许我们向浏览器发送任何 javascript 代码片段,它会在当前页面的上下文中执行它。让我们快速浏览一下如何在我们的工具中使用它:
在 Selenium 中执行 Javascript
from selenium import webdriver driver = webdriver.Chrome() driver.get("https://httpbin.org/html") script = 'return document.querySelector("h1").innerHTML' # get first header text title = driver.execute_script(script) print(title)
Playwright 中的 Javascript 执行
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() page.goto("http://httpbin.org/html") script = 'return document.querySelector("h1").innerHTML' # get first header text title = page.evaluate("() => {" + script + "}") print(title)
Javascript 执行是所有浏览器自动化工具的关键特性,所以让我们来看看我们如何使用它来抓取 Google 地图!
查找 Google 地方信息
要在 Google 地图上查找地点,我们将利用搜索系统。作为 Google 产品的地图具有出色的搜索系统,可以理解自然语言查询。
例如,如果我们想查找巴黎卢浮宫博物馆的页面,我们可以使用类似人类的准确搜索查询:Louvre Museum in Paris
。可以直接通过 URL 端点/maps/search/louvr+paris提交:
另一方面,对于不太准确的搜索查询,例如
/maps/search/mcdonalds+in+paris将提供多个结果供您选择:
我们可以看到这个强大的端点可以将我们直接带到地点页面或向我们显示多个结果。首先,让我们看看后者——如何抓取多个搜索结果。
现在我们知道如何生成搜索 URL,我们需要做的就是打开它,等待它加载并提取链接。由于我们将处理高度动态的网页,因此我们需要一个辅助函数来帮助我们确保内容已加载:
// Wait for N amount of selectors to be present in the DOM function waitCss(selector, n=1, require=false, timeout=5000) { console.log(selector, n, require, timeout); var start = Date.now(); while (Date.now() - start < timeout){ if (document.querySelectorAll(selector).length >= n){ return document.querySelectorAll(selector); } } if (require){ throw new Error(`selector "${selector}" timed out in ${Date.now() - start} ms`); } else { return document.querySelectorAll(selector); } }
使用这个实用函数,我们将能够确保我们的抓取脚本在开始解析之前等待内容加载。
让我们把它用在我们的搜索抓取工具中:
使用Selenium
from selenium import webdriver script = """ function waitCss(selector, n=1, require=false, timeout=5000) { console.log(selector, n, require, timeout); var start = Date.now(); while (Date.now() - start < timeout){ if (document.querySelectorAll(selector).length >= n){ return document.querySelectorAll(selector); } } if (require){ throw new Error(`selector "${selector}" timed out in ${Date.now() - start} ms`); } else { return document.querySelectorAll(selector); } } var results = waitCss("div[role*=article]>a", n=10, require=false); return Array.from(results).map((el) => el.getAttribute("href")) """ driver = webdriver.Chrome() def search(query): url = f"https://www.google.com/maps/search/{query.replace(' ', '+')}/?hl=en" driver.get(url) urls = driver.execute_script(script) return urls or [url] print(f'single search: {search("louvre museum in paris")}') print(f'multi search: {search("mcdonalds in paris")}')
使用Playwright
from playwright.sync_api import sync_playwright script = """ function waitCss(selector, n=1, require=false, timeout=5000) { console.log(selector, n, require, timeout); var start = Date.now(); while (Date.now() - start < timeout){ if (document.querySelectorAll(selector).length >= n){ return document.querySelectorAll(selector); } } if (require){ throw new Error(`selector "${selector}" timed out in ${Date.now() - start} ms`); } else { return document.querySelectorAll(selector); } } var results = waitCss("div[role*=article]>a", n=10, require=false); return Array.from(results).map((el) => el.getAttribute("href")) """ def search(query, page): url = f"https://www.google.com/maps/search/{query.replace(' ', '+')}/?hl=en" page.goto(url) urls = page.evaluate("() => {" + script + "}") return urls or [url] with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() print(f'single search: {search("louvre museum in paris", page=page)}') print(f'multi search: {search("mcdonalds in paris", page=page)}')
结果
single search: ['https://www.google.com/maps/search/louvre+museum+in+paris/?hl=en'] multi search: [ "https://www.google.com/maps/place/McDonald's/data=!4m7!3m6!1s0x47e66fea26bafdc7:0x21ea7aaf1fb2b3e3!8m2!3d48.8729997!4d2.2991604!16s%2Fg%2F1hd_88rdh!19sChIJx_26Jupv5kcR47OyH6966iE?authuser=0&hl=en&rclk=1", ... ]
现在我们可以成功地在谷歌地图上找到地点,让我们来看看我们如何抓取他们的数据。
抓取 Google Places
为了抓取地点数据,我们将使用使用浏览器自动化呈现 javascript 内容的相同方法。为此,我们将获取之前发现的公司 URL 并抓取每家公司的概览数据。
为了解析呈现的 HTML 数据,我们将使用parsel
一些简单的 CSS 选择器:
def parse_place(selector): """parse Google Maps place""" def aria_with_label(label): """gets aria element as is""" return selector.css(f"*[aria-label*='{label}']::attr(aria-label)") def aria_no_label(label): """gets aria element as text with label stripped off""" text = aria_with_label(label) return text.split(label, 1)[1].strip() result = { "name": "".join(selector.css("h1 ::text").getall()).strip(), "category": selector.css("button[jsaction='pane.rating.category']::text").get(), # most of the data can be extracted through accessibility labels: "address": aria_no_label("Address: "), "website": aria_no_label("Website: "), "phone": aria_no_label("Phone: "), "review_count": aria_with_label(" reviews").get(), "work_hours": aria_with_label("Monday, ").get().split(". Hide")[0], # to extract star numbers from text we can use regex pattern for numbers: "\d+" "stars": aria_with_label(" stars").re("\d+.*\d+")[0], "5_stars": aria_with_label("5 stars").re(r"(\d+) review")[0], "4_stars": aria_with_label("4 stars").re(r"(\d+) review")[0], "3_stars": aria_with_label("3 stars").re(r"(\d+) review")[0], "2_stars": aria_with_label("2 stars").re(r"(\d+) review")[0], "1_stars": aria_with_label("1 stars").re(r"(\d+) review")[0], } return result
由于谷歌地图使用复杂的 HTML 结构和 CSS 样式,因此解析起来非常困难。幸运的是,谷歌地图还实现了我们可以利用的大量辅助功能!
让我们将其添加到我们的抓取工具中:
使用Playwright
import json from parsel import Selector from playwright.sync_api import sync_playwright def parse_place(selector): """parse Google Maps place""" def aria_with_label(label): """gets aria element as is""" return selector.css(f"*[aria-label*='{label}']::attr(aria-label)") def aria_no_label(label): """gets aria element as text with label stripped off""" text = aria_with_label(label).get("") return text.split(label, 1)[1].strip() result = { "name": "".join(selector.css("h1 ::text").getall()).strip(), "category": selector.css("button[jsaction='pane.rating.category']::text").get(), # most of the data can be extracted through accessibility labels: "address": aria_no_label("Address: "), "website": aria_no_label("Website: "), "phone": aria_no_label("Phone: "), "review_count": aria_with_label(" reviews").get(), # to extract star numbers from text we can use regex pattern for numbers: "\d+" "stars": aria_with_label(" stars").re("\d+.*\d+")[0], "5_stars": aria_with_label("5 stars").re(r"(\d+) review")[0], "4_stars": aria_with_label("4 stars").re(r"(\d+) review")[0], "3_stars": aria_with_label("3 stars").re(r"(\d+) review")[0], "2_stars": aria_with_label("2 stars").re(r"(\d+) review")[0], "1_stars": aria_with_label("1 stars").re(r"(\d+) review")[0], } return result urls = ["https://goo.gl/maps/Zqzfq43hrRPmWGVB7?hl=en"] places = [] with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() for url in urls: page.goto(url) page.wait_for_selector("button[jsaction='pane.rating.category']") places.append(parse_place(Selector(text=page.content()))) print(json.dumps(places, indent=2, ensure_ascii=False))
结果
[ { "name": "Louvre Museum", "category": "Art museum", "address": "Rue de Rivoli, 75001 Paris, France", "website": "louvre.fr", "phone": "+33 1 40 20 50 50", "review_count": "240,040 reviews", "stars": "4.7", "5_stars": "513", "4_stars": "211", "3_stars": "561", "2_stars": "984", "1_stars": "771" } ]
我们可以看到,只需几行巧妙的代码,我们就可以从谷歌地图中抓取业务数据。我们专注于一些可见的细节,但 HTML 正文中提供了更多信息,如评论、新闻文章和各种分类标签。
常问问题
为了总结本指南,让我们看一下有关网页抓取Google 地图的一些常见问题:
抓取谷歌地图合法吗?
是的。Google 地图数据是公开的,我们不会提取任何私人信息。以缓慢、尊重的速度抓取谷歌地图属于道德抓取定义。话虽如此,在抓取个人数据(例如评论所附的详细信息(图像或名称))时,应注意欧盟的 GDRP 合规性。
如何更改谷歌地图显示语言?
要更改 Google 地图上显示的内容的语言,我们可以使用 URL 参数hl
(代表“人类语言”)。例如,对于英语,我们会?hl=en
在 URL 的末尾添加,例如google.com/maps/search/big+ben+londong+uk/?hl=en
谷歌地图爬取工具摘要
在本教程中,我们构建了一个可在 Selenium、Playwright 中使用的Google 地图爬取工具。为此,我们启动了一个浏览器实例并通过 Python 代码控制它,以通过 Google 地图搜索查找企业,并抓取每个企业的网站、电话号码和元信息等详细信息。