Сокращение поисковых тенденций Google в реальном времени с помощью Python

  • Что будет соскоблено
  • Полный код
  • Подготовка
  • Объяснение кода
    • Среда кода верхнего уровня
    • Прокрутка страницы
    • Поиск в реальном времени
  • Вывод

Что будет соскоблено

📌Примечание: На данный момент у нас нет API, поддерживающего извлечение данных из Google Realtime Search Trends.

Эта запись в блоге призвана показать вам, как вы можете сделать это самостоятельно, пока мы работаем над выпуском нашего собственного API в ближайшее время. Мы сообщим вам в нашем Twitter, когда этот API будет выпущен.

Полный код

Если вам не нужны объяснения, посмотрите пример полного кода в онлайн IDE.

import time, json
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from parsel import Selector


def scroll_page(url):
    service = Service(ChromeDriverManager().install())

    options = webdriver.ChromeOptions()
    options.add_argument("--headless")
    options.add_argument('--lang=en')
    options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36")


    driver = webdriver.Chrome(service=service, options=options)
    driver.get(url)

    WebDriverWait(driver, 10000).until(EC.visibility_of_element_located((By.TAG_NAME, 'body')))

    flag = True

    while flag:
        try:
            search_input = driver.find_element(By.CSS_SELECTOR, 'div[class*="feed-load-more-button"]')
            driver.execute_script("arguments[0].click();", search_input)
            time.sleep(3)
        except:
            flag = False

    selector = Selector(driver.page_source)
    driver.quit()

    return selector


def scrape_realtime_search(selector):   
    realtime_search_trends = []

    for result in selector.css('.feed-item-header'):
        index = result.css('.index::text').get().strip()
        subtitle = result.css('.summary-text a::text').get()
        subtitle_link = result.css('.summary-text a::attr(href)').get()
        source = result.css('.source-and-time span::text').get().strip()
        time = result.css('.subtitles-overlap div::text').get().strip()
        image_source = result.css('.image-text::text').get().strip()
        image_source_link = result.css('.image-link-wrapper a::attr(href)').get()
        thumbnail = f"https:{result.css('.feed-item-image-wrapper img::attr(src)').get()}"

        title = []
        title_links = {}

        for part in result.css('.title span a'):
            title_part = part.css('::text').get().strip()
            title.append(title_part)
            title_links[title_part] = f"https://trends.google.com{part.css('::attr(href)').get()}"

        realtime_search_trends.append({
            'index': index,
            'title': " • ".join(title),
            'title_links': title_links,
            'subtitle': subtitle,
            'subtitle_link': subtitle_link,
            'source': source,
            'time': time,
            'image_source': image_source,
            'image_source_link': image_source_link,
            'thumbnail': thumbnail,
        })

    print(json.dumps(realtime_search_trends, indent=2, ensure_ascii=False))


def main():
    GEO = "US"
    CATEGORY = "all"
    URL = f"https://trends.google.com/trends/trendingsearches/realtime?geo={GEO}&category={CATEGORY}"
    result = scroll_page(URL)
    scrape_realtime_search(result)


if __name__ == "__main__":
    main()
Вход в полноэкранный режим Выход из полноэкранного режима

Подготовка

Установите библиотеки:

pip install parsel selenium webdriver webdriver_manager
Войдите в полноэкранный режим Выйти из полноэкранного режима

Основные знания о скраппинге с помощью селекторов CSS

CSS-селекторы определяют, к какой части разметки применяется стиль, что позволяет извлекать данные из соответствующих тегов и атрибутов.

Если вы еще не занимались скраппингом с помощью CSS-селекторов, в моем блоге есть отдельная статья
о том, как использовать CSS-селекторы при веб-скрапинге, в которой рассказывается о том, что это такое, плюсы и минусы, и почему они важны с точки зрения веб-скрапинга.

Снизить вероятность блокировки

Убедитесь, что вы используете заголовки запроса user-agent, чтобы действовать как «настоящий» пользователь. Поскольку по умолчанию requests user-agent — это python-requests, и веб-сайты понимают, что, скорее всего, запрос посылает скрипт. Проверьте, какой у вас user-agent.

В блоге есть статья о том, как уменьшить вероятность блокировки при веб-скреппинге, в которой вы можете ознакомиться с базовыми и более продвинутыми подходами.

Объяснение кода

Импортируйте библиотеки:

import time, json
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from parsel import Selector
Вход в полноэкранный режим Выход из полноэкранного режима
Библиотека Назначение
time для работы со временем в Python.
json для преобразования извлеченных данных в объект JSON.
webdriver управлять браузером нативно, как это делает пользователь, локально или на удаленной машине с помощью сервера Selenium.
Service управлять запуском и остановкой ChromeDriver.
By набор поддерживаемых стратегий локаторов (By.ID, By.TAG_NAME, By.XPATH и т.д.).
WebDriverWait ждать только столько, сколько требуется.
expected_conditions содержит набор предопределенных условий для использования с WebDriverWait.
Selector парсер XML/HTML с полной поддержкой XPath и CSS селекторов.

Среда кода верхнего уровня

В этом коде используется общепринятое правило использования конструкции __name__ == "__main__":

def main():
    GEO = "US" 
    CATEGORY = "all"
    URL = f"https://trends.google.com/trends/trendingsearches/realtime?geo={GEO}&category={CATEGORY}"
    result = scroll_page(URL)
    scrape_realtime_search(result)


if __name__ == "__main__":
    main()
Вход в полноэкранный режим Выход из полноэкранного режима

Эта проверка будет выполнена только в том случае, если пользователь запустил этот файл. Если пользователь импортирует этот файл в другой, то проверка не сработает.

Для получения более подробной информации вы можете посмотреть видео Python Tutorial: if name == ‘main’.

Небольшое описание функции main:

Прокрутка страницы

Функция принимает URL и возвращает полную HTML-структуру.

Сначала давайте разберемся, как работает пагинация на странице Realtime search trends. Чтобы загрузить больше информации, необходимо нажать на кнопку LOAD MORE:

📌Примечание: Чтобы получить все данные, нужно нажимать на кнопку до тех пор, пока данные не закончатся.

В данном случае используется библиотека selenium, которая позволяет имитировать действия пользователя в браузере. Для работы selenium необходимо использовать ChromeDriver, который можно загрузить вручную или с помощью кода. В нашем случае используется второй способ. Чтобы управлять запуском и остановкой ChromeDriver, необходимо использовать Service, который установит двоичные файлы браузера под капотом:

service = Service(ChromeDriverManager().install())
Вход в полноэкранный режим Выйти из полноэкранного режима

Для корректной работы необходимо также добавить options:

options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument('--lang=en')
options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36")
Войти в полноэкранный режим Выйти из полноэкранного режима
Параметры Chrome Пояснение
--headless запустить Chrome в безголовом режиме.
--lang=en установить язык браузера на английский.
user-agent действовать как «настоящий» пользовательский запрос браузера, передавая его в заголовки запроса. Проверьте, какой у вас user-agent.

Теперь мы можем запустить webdriver и передать url в метод get().

driver = webdriver.Chrome(service=service, options=options)
driver.get(url)
Вход в полноэкранный режим Выход из полноэкранного режима

Иногда трудно рассчитать, сколько времени потребуется для загрузки страницы, все зависит от скорости интернета, мощности компьютера и других факторов. Описанный ниже метод намного лучше, чем использование задержки в секундах, так как ожидание происходит ровно до того момента, когда страница полностью загрузится:

WebDriverWait(driver, 10000).until(EC.visibility_of_element_located((By.TAG_NAME, 'body')))
Войти в полноэкранный режим Выйти из полноэкранного режима

📌Примечание: В данном случае мы даем 10 секунд на загрузку страницы, если она загрузится раньше, то ожидание закончится.

Когда страница загрузилась, необходимо найти кнопку LOAD MORE. Selenium предоставляет возможность поиска элемента по селекторам CSS.

Нажатие на кнопку осуществляется путем вставки кода JavaScript в метод execute_script(). Подождите некоторое время для загрузки данных, используя метод sleep(). Эти действия повторяются до тех пор, пока кнопка существует и позволяет загружать данные.

flag = True

while flag:
    try:
        search_input = driver.find_element(By.CSS_SELECTOR, 'div[class*="feed-load-more-button"]')
        driver.execute_script("arguments[0].click();", search_input)
        time.sleep(3)
    except:
        flag = False
Войдите в полноэкранный режим Выход из полноэкранного режима

Теперь мы будем использовать Selector из библиотеки Parsel, куда мы передаем структуру html со всеми данными, с учетом пагинации.

В parsel время скраппинга намного быстрее благодаря самому движку, здесь больше нет сетевого компонента, нет взаимодействия в реальном времени со страницей и элементом, есть только парсинг HTML.

После выполнения всех операций остановите драйвер:

selector = Selector(driver.page_source)
# extracting code from HTML
driver.quit()
Войти в полноэкранный режим Выход из полноэкранного режима

Функция выглядит следующим образом:

def scroll_page(url):
    service = Service(ChromeDriverManager().install())

    options = webdriver.ChromeOptions()
    options.add_argument("--headless")
    options.add_argument('--lang=en')
    options.add_argument("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36")


    driver = webdriver.Chrome(service=service, options=options)
    driver.get(url)

    WebDriverWait(driver, 10000).until(EC.visibility_of_element_located((By.TAG_NAME, 'body')))

    flag = True

    while flag:
        try:
            search_input = driver.find_element(By.CSS_SELECTOR, 'div[class*="feed-load-more-button"]')
            driver.execute_script("arguments[0].click();", search_input)
            time.sleep(3)
        except:
            flag = False

    selector = Selector(driver.page_source)
    driver.quit()

    return selector
Войти в полноэкранный режим Выйти из полноэкранного режима

На рисунке ниже я демонстрирую, как работает эта функция:

Поиск в реальном времени

Эта функция принимает полную структуру HTML и печатает все результаты в формате json.

Чтобы соскрести все элементы, вам нужно получить доступ к селектору .feed-item-header. Все данные, кроме заголовка, легко извлекаются.

Заголовок состоит из нескольких частей, разделенных символом -. Чтобы извлечь его полностью, каждый результат селектора .title span a должен быть дополнительно итерирован. Добавьте все части в список title и затем соберите строку из списка. Кроме того, каждая часть заголовка имеет свою собственную ссылку, которая извлекается и добавляется в словарь title_links, где key — часть заголовка, а value — ссылка.

Полная функция для отбора всех данных будет выглядеть следующим образом:

def scrape_realtime_search(selector):   
    realtime_search_trends = []

    for result in selector.css('.feed-item-header'):
        index = result.css('.index::text').get().strip()
        subtitle = result.css('.summary-text a::text').get()
        subtitle_link = result.css('.summary-text a::attr(href)').get()
        source = result.css('.source-and-time span::text').get().strip()
        time = result.css('.subtitles-overlap div::text').get().strip()
        image_source = result.css('.image-text::text').get().strip()
        image_source_link = result.css('.image-link-wrapper a::attr(href)').get()
        thumbnail = f"https:{result.css('.feed-item-image-wrapper img::attr(src)').get()}"

        title = []
        title_links = {}

        for part in result.css('.title span a'):
            title_part = part.css('::text').get().strip()
            title.append(title_part)
            title_links[title_part] = f"https://trends.google.com{part.css('::attr(href)').get()}"

        realtime_search_trends.append({
            'index': index,
            'title': " • ".join(title),
            'title_links': title_links,
            'subtitle': subtitle,
            'subtitle_link': subtitle_link,
            'source': source,
            'time': time,
            'image_source': image_source,
            'image_source_link': image_source_link,
            'thumbnail': thumbnail,
        })

    print(json.dumps(realtime_search_trends, indent=2, ensure_ascii=False))
Войти в полноэкранный режим Выход из полноэкранного режима
Код Пояснение
realtime_search_trends временный список, куда в конце функции будут добавлены извлеченные данные.
css() доступ к элементам по переданному селектору.
::text или ::attr(<attribute>) для извлечения текстовых или атрибутивных данных из узла.
get() для фактического извлечения текстовых данных.
strip() вернуть копию строки с удаленными ведущими и отстающими символами.
"".join() конкатенировать список в строку.
realtime_search_trends.append({}) применить извлеченные данные к списку в виде словаря.

Вывод

[
  {
    "index": "1",
    "title": "Student • Inflation • Student debt • Student loan • CNN • Joe Biden",
    "title_links": {
      "Student": "https://trends.google.com/trends/explore?q=/m/014cnc&date=now+7-d&geo=US",
      "Inflation": "https://trends.google.com/trends/explore?q=/m/09jx2&date=now+7-d&geo=US",
      "Student debt": "https://trends.google.com/trends/explore?q=/m/051zcxv&date=now+7-d&geo=US",
      "Student loan": "https://trends.google.com/trends/explore?q=/m/02crs_&date=now+7-d&geo=US",
      "CNN": "https://trends.google.com/trends/explore?q=/m/0gsgr&date=now+7-d&geo=US",
      "Joe Biden": "https://trends.google.com/trends/explore?q=/m/012gx2&date=now+7-d&geo=US"
    },
    "subtitle": "Rival Senate candidates offer differing solutions for inflation woes",
    "subtitle_link": "https://www.cachevalleydaily.com/news/archive/2022/08/23/rival-senate-candidates-offer-differing-solutions-for-inflation-woes/",
    "source": "Cache Valley Daily",
    "time": "Aug 22, 2022 - Now",
    "image_source": "Cache Valley Daily",
    "image_source_link": "https://www.cachevalleydaily.com/news/archive/2022/08/23/rival-senate-candidates-offer-differing-solutions-for-inflation-woes/",
    "thumbnail": "https://t2.gstatic.com/images?q=tbn:ANd9GcRR2kTVJd2bJLcv4U1CgyLUf5DWZFVekQF5tRbUS6QgEKKPLcB2QvMLCC2SnuID1gr370ISH6RniOc"
  },
  ... other results
  {
    "index": "225",
    "title": "Primary election • Lee County • Ron DeSantis",
    "title_links": {
      "Primary election": "https://trends.google.com/trends/explore?q=/m/016ncr&date=now+7-d&geo=US",
      "Lee County": "https://trends.google.com/trends/explore?q=/m/0jrjb&date=now+7-d&geo=US",
      "Ron DeSantis": "https://trends.google.com/trends/explore?q=/m/0l8mn35&date=now+7-d&geo=US"
    },
    "subtitle": "Karnes, governor appointee, retains Lee Clerk of Courts position with more than 55% of the vote",
    "subtitle_link": "https://www.news-press.com/story/news/politics/elections/2022/08/23/lee-county-florida-election-results-kevin-karnes-secures-republican-primary-lee-clerk-of-courts/7836666001/",
    "source": "The News-Press",
    "time": "Aug 23, 2022 - Now",
    "image_source": "The News-Press",
    "image_source_link": "https://www.news-press.com/story/news/politics/elections/2022/08/23/lee-county-florida-election-results-kevin-karnes-secures-republican-primary-lee-clerk-of-courts/7836666001/",
    "thumbnail": "https://t1.gstatic.com/images?q=tbn:ANd9GcScg7Zo9OiYmDd4rlaqqaFkOm1okyJvAgjHZP8MQdsSjwNFtQcjbWiL0KXZl6X-VMGSYXnOa-Msa-w"
  }
]
Вход в полноэкранный режим Выйти из полноэкранного режима

Присоединяйтесь к нам на Twitter | YouTube

Add a Feature Request💫 or a Bug🐞

Оцените статью
devanswers.ru
Добавить комментарий