in

如何使用Python爬取谷歌搜索结果

如何使用Python爬取谷歌搜索结果

在本网络抓取教程中,我们将了解如何使用 Python 抓取 Google 搜索结果。 SERP是搜索结果抓取的通用行业术语,主要用于数据采集的 SEO 和品牌知名度领域。通过抓取谷歌,我们可以跟踪我们的产品的表现,甚至可以优化我们的页面以在谷歌上排名更高。 抓取谷歌可能很困难,因为谷歌使用了大量混淆和反抓取技术,因此我们将深入探讨几个技术要点,如 URL 格式、动态 HTML 解析以及如何避免被阻止。 在本文中,我们将使用 Python 和传统工具(如 HTTP 客户端和 HTML 解析器)来进行抓取。

谷歌搜索可能是互联网上最大的公共数据库,它是许多用例的重要数据源。由于谷歌索引了大部分公共网络,我们可以访问许多流行数据字段的摘要,这些摘要可以以多种方式使用。从市场研究到 SEO,有许多用于抓取 Google 搜索结果的用例。 另一个流行的用例是 SEO(搜索引擎优化),其中正在抓取 Google 搜索以查看您的竞争对手对哪些关键字进行排名以及它们的排名情况。换句话说,了解搜索结果是深入了解竞争对手和市场表现的好方法。 谷歌搜索还具有一个片段系统,该系统汇总来自 IMDb、维基百科等热门来源的数据。这可用于从热门来源抓取数据,而无需直接抓取它们。

项目设置

在本教程中,我们将使用 Python 和一些流行的社区包:

  • httpx作为我们的 HTTP 客户端,我们将使用它来检索搜索结果 HTML。
  • parsel作为我们的 HTML 解析器。由于 Google 使用大量动态 HTML,我们将使用一些聪明的 XPath 选择器来查找结果数据。

这两个包有许多流行的替代品,例如beautifulsoup是 parsel 的流行替代品,但是由于 Google 页面可能难以解析,我们将使用 parsel 的XPath 选择器,它比beautifulsoup 使用的CSS 选择器更强大。 对于我们选择的 HTTP 客户端,httpx因为它支持 HTTP/2,这有助于避免阻塞。尽管也可以使用requestsor等​​其他客户端。aiohttp

抓取谷歌搜索结果

让我们首先抓取 Google 搜索结果的第一页以查询“jingzhengli”。首先,让我们看看当我们将此查询输入到 Google 搜索时会发生什么。 我们可以看到,一旦我们输入查询,我们就会被带到看起来像google.com/search?hl=en&q=jingzhengli%20的搜索结果 URL 。 因此,搜索使用/search端点,查询作为q参数传递。参数hl是语言代码,我们可以看到它被设置为en英语。 此 URL 将为我们提供页面,但我们如何为搜索结果解析它?为此,我们将使用 XPath 选择器,并且由于 Google 使用动态 HTML,我们将遵循以下heading元素:

谷歌搜索页面结构图
简化的谷歌搜索页面结构

虽然 Google 使用动态 HTML,但我们仍然可以依靠相对结构进行抓取。我们可以选择<h3>元素并将它们视为每个搜索结果的容器。让我们试试看:

from collections import defaultdict
from urllib.parse import quote
from httpx import Client
from parsel import Selector

# 1. Create HTTP client with headers that look like a real web browser
client = Client(
    headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-US,en;q=0.9,lt;q=0.8,et;q=0.7,de;q=0.6",
    },
    follow_redirects=True,
    http2=True,  # use HTTP/2 
)


def parse_search_results(selector: Selector):
    """parse search results from google search page"""
    results = []
    for box in selector.xpath("//h1[contains(text(),'Search Results')]/following-sibling::div[1]/div"):
        title = box.xpath(".//h3/text()").get()
        url = box.xpath(".//h3/../@href").get()
        text = "".join(box.xpath(".//div[@data-content-feature=1]//text()").getall())
        if not title or not url:
            continue
        url = url.split("://")[1].replace("www.", "")
        results.append(title, url, text)
    return results


def scrape_search(query: str, page=1):
    """scrape search results for a given keyword"""
    # retrieve the SERP
    url = f"https://www.google.com/search?hl=en&q={quote(query)}" + (f"&start={10*(page-1)}" if page > 1 else "")
    print(f"scraping {query=} {max_pages=}")
    results = defaultdict(list)
    response = client.get(url)
    assert response.status_code200,f"failed status_code={response.status_code}"
    # parse SERP for search result data
    selector = Selector(response.text)
    results["search"].extend(parse_search_results(selector))
    return dict(results)

# example use: scrape 3 pages: 1,2,3
for page in [1, 2, 3]:
    results = scrape_search("jingzhengli", page=page)
    for result in results["search"]:
        print(result)
 

在上面的示例中,我们编写了一个简短的 google 搜索 python 抓取工具。我们首先创建了一个 httpx 客户端,其标头模仿网络浏览器以防止被 Google 阻止。 然后,我们定义了解析parse_search_results来自给定SERP 的搜索结果的函数。请注意,对于解析,我们使用 XPath 选择器,该选择器使用标题文本匹配而不是通常的类或 ID 匹配。这是因为 Google 使用动态 HTML,我们不能依赖静态类名。 最后,我们定义了scrape_search接受查询和页码并返回搜索结果列表的函数。我们可以使用这个函数来废弃给定查询的谷歌结果。所以,让我们试一试,接下来做一些 SEO 分析!

抓取 SEO 排名

现在我们可以抓取SERP让我们来看看我们如何在SEO实践中使用这些数据。 首先,我们可以使用这些数据来确定我们在给定查询或关键字的搜索结果中的位置。例如,假设我们想看看我们关于网络抓取 instagram 的博客文章对关键字/查询“scrape instagram”的排名:

import re

def check_ranking(keyword: str, url_match: str, max_pages=3):
    """check ranking of a given url (partial) for a given keyword"""
    rank = 1
    for page in range(1, max_pages + 1):
        results = scrape_search(keyword, page=page)
        for (title, result_url, text) in results["search"]:
            if url_match in result_url:
                print(f"rank found:\n  {title}\n  {text}\n  {result_url}")
                return rank
            rank += 1
    return None

check_ranking(
    keyword="scraping instagram", 
    url_match="jingzhengli.com",
)

在上面,我们使用我们之前定义的 Google SERP 抓取器(函数scrape_search)从 SERP 收集搜索结果数据,直到我们遇到我们的博客文章。在现实生活中,我们会偶尔运行这个 Google 抓取工具来收集我们的搜索引擎性能。我们还可以使用这些数据来确认搜索结果标题和描述是否按照我们的意愿出现,并相应地调整我们的内容。

抓取谷歌关键字数据

搜索引擎优化的很大一部分是关键字研究——了解人们正在搜索什么以及如何针对这些查询优化我们的内容。 当涉及到谷歌抓取时,“People Also Ask”和“Related Searches”部分可用于关键字研究:

谷歌搜索关键字研究领域

上面,从“相关搜索”部分来看,我们可以看到对抓取 instagram 感兴趣的人也有兴趣使用 Python 或 R 来做。在“People also ask”部分我们也可以看到人们对以下内容感兴趣如何抓取 Instagram 个人资料以及抓取它的合法性。很容易想象这两个数据字段在关键字研究中的作用! 让我们继续研究网络抓取 instagram文章,看看如何抓取它们:

from collections import defaultdict
import json
from urllib.parse import quote
from httpx import Client
from parsel import Selector

# 1. Create HTTP client with headers that look like a real web browser
client = Client(
    headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-US,en;q=0.9,lt;q=0.8,et;q=0.7,de;q=0.6",
    },
    follow_redirects=True,
    http2=True,
)


def parse_related_search(selector: Selector):
    """get related search keywords of current SERP"""
    results = []
    for suggestion in selector.xpath(
        "//div[re:test(div/div/span/text(),'related searches','i')]/following-sibling::div//a"
    ):
        results.append("".join(suggestion.xpath(".//text()").getall()))
    return results


def parse_people_also_ask(selector: Selector):
    """get people also ask questions of current SERP"""
    return selector.css(".related-question-pair span::text").getall()


def scrape_search(query: str, page=1):
    """scrape search results for a given keyword"""
    # retrieve the SERP
    url = f"https://www.google.com/search?hl=en&q={quote(query)}" + (f"&start={10*(page-1)}" if page > 1 else "")
    print(f"scraping {query=} {page=}")
    results = defaultdict(list)
    response = client.get(url)
    assert response.status_code == 200, f"failed status_code={response.status_code}"
    # parse SERP for search result data
    selector = Selector(response.text)
    results["related_search"].extend(parse_related_search(selector))
    results["people_also_ask"].extend(parse_people_also_ask(selector))
    return dict(results)


# Example use: 
results = scrape_search("scraping instagram")
print(json.dumps(results, indent=2))

示例输出

{
  "related_search": [
    "scraping instagram with python",
    "is scraping instagram legal",
    "instagram scraping api",
    "scraping instagram data with r",
    "instagram-scraper python github",
    "instagram comment scraper python",
    "instagram scraper free",
    "instagram-scraper github"
  ],
  "people_also_ask": [
    "Does Instagram allow scraping?",
    "What does scraping Instagram mean?",
    "How do you scrape an Instagram account?",
    "Can you scrape Instagram with Python?"
  ]
}

上面,我们定义了两个函数来解析我们可以在 SEO 关键词研究中使用的相关搜索和相关问题。

抓取谷歌丰富的结果

Google 还以摘要的形式提供丰富的结果。这些是维基百科、IMDb 等流行数据源的摘要。例如,这是谷歌自己的片段:

谷歌搜索结果中可见的丰富网页摘要
右侧提供丰富的公司概览结果

Snippet 可以以简洁、可预测的格式聚合来自多个来源的数据,因此它是一个流行的网络抓取目标,因为我们可以轻松地收集有关流行目标的信息,如公司、公众人物和工作机构。让我们来看看如何抓取它们:

from parsel import Selector
from httpx import Client

client = Client(
    headers={
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "en-US,en;q=0.9,lt;q=0.8,et;q=0.7,de;q=0.6",
    },
    follow_redirects=True,
)


def parse_search_snippet(selector: Selector):
    snippet = selector.xpath("//h2[re:test(.,'complementary results','i')]/following-sibling::div[1]")
    data = {
        "title": snippet.xpath(".//*[@data-attrid='title']//text()").get(),
        "subtitle": snippet.xpath(".//*[@data-attrid='subtitle']//text()").get(),
        "website": snippet.xpath(".//a[@data-attrid='visit_official_site']/@href").get(),
        "description": snippet.xpath(".//div[@data-attrid='description']//span//text()").get(),
        "description_more_link": snippet.xpath(".//div[@data-attrid='description']//@href").get(),
    }
    # get summary info rows
    data["info"] = {}
    for row in snippet.xpath(".//div[contains(@class,'__wholepage-card')]//div[re:test(@data-attrid, 'kc:|hw:')]"):
        text = "".join(row.xpath(".//text()").getall()).strip()
        label, value = text.split(": ")
        data["info"][label.lower()] = value.strip()
    # get social media links
    data["socials"] = {}
    for profile in snippet.xpath(".//div[@data-attrid='kc:/common/topic:social media presence']//g-link/a"):
        label = profile.xpath(".//text()").get()
        url = profile.xpath(".//@href").get()
        data["socials"][label] = url
    return data

def scrape_search(query: str, page=1):
    """scrape search results for a given keyword"""
    url = f"https://www.google.com/search?hl=en&q={quote(query)}" + (f"&start={10*(page-1)}" if page > 1 else "")
    print(f"scraping {query=} {max_pages=}")
    results = defaultdict(list)
    response = client.get(url)
    assert response.status_code == 200, f"failed status_code={response.status_code}"
    selector = Selector(response.text)
    results["search"].extend(parse_search_results(selector))
    results["rich_snippets"] = parse_search_snippet(selector)
    return dict(results)

# example:
print(scrape_search("google")["rich_snippets"])

示例输出

{
  "title": "Google",
  "subtitle": "Technology company",
  "website": "http://www.google.com/",
  "description": "Google LLC is an American multinational technology company focusing on search engine technology, online advertising, cloud computing, computer software, quantum computing, e-commerce, artificial intelligence, and consumer electronics.",
  "description_more_link": "https://en.wikipedia.org/wiki/Google",
  "info": {
    "founders": "Larry Page, Sergey Brin",
    "ceo": "Sundar Pichai (Oct 2, 2015\u2013)",
    "parent organization": "Alphabet Inc.",
    "founded": "September 4, 1998, Menlo Park, CA",
    "headquarters": "Mountain View, CA",
    "subsidiaries": "YouTube, Kaggle, Mandiant, Firebase, MORE"
  },
  "socials": {
    "Twitter": "https://twitter.com/Google",
    "Facebook": "https://www.facebook.com/Google",
    "LinkedIn": "https://www.linkedin.com/company/google",
    "YouTube": "https://www.youtube.com/c/google",
    "Instagram": "https://www.instagram.com/google"
  }
}

在此示例中,我们从丰富的公司概览片段中收集了详细信息。为了解析这个,我们依赖于data-可以可靠地用于解析动态 HTML 文档的标题和属性。请注意,丰富网页摘要因主题而异,在我们的示例中,我们仅涵盖一种丰富网页摘要。但是,大多数抓取逻辑都可以重复用于其他细节。 谷歌提供了许多不同类型的丰富网页摘要,它们可以通过类似的方式被抓取。

常问问题

为了总结我们的 Google 搜索结果抓取工具教程,让我们来看看一些常见问题:

是的,抓取 Google 搜索结果是完全合法的,因为它是公开的、不受版权保护的数据。但是,应注意可能包含在搜索结果中的受版权保护的图像和视频。

有谷歌搜索 API 吗?

不,没有官方的 Google 搜索 API。但是,您可以使用 Python 创建我们自己的 Google 抓取工具,并使用本教程中介绍的方法将其用作 API。

如何抓取谷歌地图?

谷歌地图是一项独立的服务,可以通过与谷歌搜索结果类似的方式进行抓取。为此,请参阅我们关于如何抓取谷歌地图的教程。

谷歌爬虫摘要

在本教程中,我们使用 Python 和一些社区包编写了一个 Google 网络抓取工具。为了检索我们使用的支持 http2 和异步连接的 SERP 内容httpx,以及解析我们使用parselXPath 选择器从动态 HTML 页面中提取数据的数据。 本文说明了使用 Python 和常见的网络抓取实践免费抓取谷歌结果是多么容易。

Written by 河小马

河小马是一位杰出的数字营销行业领袖,广告中国论坛的重要成员,其专业技能涵盖了PPC广告、域名停放、网站开发、联盟营销以及跨境电商咨询等多个领域。作为一位资深程序开发者,他不仅具备强大的技术能力,而且在出海网络营销方面拥有超过13年的经验。