Этот обучающий пост в блоге покажет вам, как очистить рейтинг позиции веб-сайта, заголовок, ссылку, отображаемую ссылку и фрагмент.

Что такое поиск Naver

Я уже отвечал на это в своем первом блоге о парсинге Результаты Naver News, там можно найти информацию о том, что такое Naver Search.

вступление

Этот обучающий пост в блоге является продолжением серии веб-скрейпинга Naver. Здесь вы увидите, как очистить рейтинг веб-сайта Naver Organic Results, заголовок, ссылку, отображаемую ссылку и фрагмент кода с помощью Python, используя библиотеки beautifulsoup, requests, lxml.

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

Предпосылки и импорт

pip install requests
pip install lxml 
pip install beautifulsoup4
  • Базовые знания Python.
  • Базовое знакомство с упомянутыми выше пакетами.
  • Базовое понимание селекторов CSS, потому что вы увидите в основном использование методов select()/select_one() beautifulsoup, которые принимают селекторы CSS.

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

Импорт

import requests, lxml
from bs4 import BeautifulSoup

Что будет очищено

Процесс

Если вам не нужно объяснение, переходите к разделу кода.

Нам нужно сделать три шага, чтобы сделать:

  1. Сохраните HTML локально, чтобы протестировать все, прежде чем делать много прямых запросов.
  2. Выберите CSS селекторов для всех необходимых данных.
  3. Извлеките данные.

Сохраните HTML для локального тестирования синтаксического анализатора

Локальное сохранение HTML предотвращает блокировку или блокировку IP-адресов, особенно когда необходимо сделать несколько запросов к одному и тому же веб-сайту для тестирования кода.

Обычный пользователь не будет выполнять более 100 запросов за очень короткий промежуток времени и не будет делать одно и то же снова и снова (шаблон), как это делают скрипты, поэтому веб-сайты могут помечать такое поведение как необычный и заблокировать IP-адрес на некоторое время (может быть написано в ответе: requests.get("URL").text) или навсегда.

import requests
headers = {
    "User-Agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19582"
}
params = {
    "query": "bruce lee",
    "where": "web"        # theres's also a "nexearch" param that will produce different results
}
def save_naver_organic_results():
    html = requests.get("https://search.naver.com/search.naver", params=params, headers=headers).text
    # replacing every space to underline (_) so bruce lee will become bruce_lee 
    query = params['query'].replace(" ", "_")
    with open(f"{query}_naver_organic_results.html", mode="w") as file:
        file.write(html)

Теперь, что здесь происходит

Импортировать requests библиотеку

import requests

Добавьте user-agent и параметры запроса

headers = {
    "User-Agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19582"
}
# query parameters
params = {
    "query": "bruce lee",
    "where": "web"
}

Я предпочитаю передавать параметры запроса в requests.get(params=params) вместо того, чтобы оставлять их в URL-адресе. Я нахожу это более читаемым, например, давайте посмотрим на тот же самый URL:

params = {
    "where": "web",
    "sm": "top_hty",
    "fbm": "1",
    "ie": "utf8",
    "query": "bruce+lee"
}
requests.get("https://search.naver.com/search.naver", params=params)
# VS 
requests.get("https://search.naver.com/search.naver?where=web&sm=top_hty&fbm=1&ie=utf8&query=bruce+lee")  # Press F.

Что насчет user-agent, он должен действовать как реальный визит пользователя, иначе запрос может быть отклонен. Вы можете прочитать больше об этом в другом моем сообщении в блоге о том, как уменьшить вероятность блокировки при просмотре веб-страниц поисковыми системами.

Выберите и протестируйте CSS селекторов

Выбор контейнера (селектор CSS, который содержит все необходимые данные), заголовка, ссылки, отображаемой ссылки и фрагмента.

GIF выше переводится как этот фрагмент кода:

for result in soup.select(".total_wrap"):
    title = result.select_one(".total_tit").text.strip()
    link = result.select_one(".total_tit .link_tit")["href"]
    displayed_link = result.select_one(".total_source").text.strip()
    snippet = result.select_one(".dsc_txt").text

Извлечь данные

import lxml, json
from bs4 import BeautifulSoup

def extract_local_html_naver_organic_results():
    with open("bruce_lee_naver_organic_results.html", mode="r") as html_file:
        html = html_file.read()
        soup = BeautifulSoup(html, "lxml")
        data = []
        for index, result in enumerate(soup.select(".total_wrap")):
            title = result.select_one(".total_tit").text.strip()
            link = result.select_one(".total_tit .link_tit")["href"]
            displayed_link = result.select_one(".total_source").text.strip()
            snippet = result.select_one(".dsc_txt").text
            data.append({
                "position": index + 1, # starts from 1, not from 0
                "title": title,
                "link": link,
                "displayed_link": displayed_link,
                "snippet": snippet
            })
        print(json.dumps(data, indent=2, ensure_ascii=False))

Теперь давайте разберем часть извлечения

Импортировать библиотеки bs4, lxml, json

import lxml, json
from bs4 import BeautifulSoup

Откройте сохраненный файл HTML, прочитайте его и передайте объекту BeautifulSoup() и назначьте lxml в качестве парсера HTML.

with open("bruce_lee_naver_organic_results.html", mode="r") as html_file:
    html = html_file.read()
    soup = BeautifulSoup(html, "lxml")

Создайте временный list() для хранения извлеченных данных

data = []

Повторить и добавить как словарь к временному list()

Так как нам также нужно получить индекс (позиция в рейтинге), мы можем использовать метод enumerate(), который добавляет счетчик к итерируемому объекту и возвращает его. Еще примеры.

Пример:

grocery = ["bread", "milk", "butter"]  # iterable
for index, item in enumerate(grocery):
  print(f"{index} {item}\n")
  
'''
0 bread
1 milk
2 butter
'''

Фактический код:

# in our case iterable is soup.select() since it returns an iterable as well
for index, result in enumerate(soup.select(".total_wrap")):
    title = result.select_one(".total_tit").text.strip()
    link = result.select_one(".total_tit .link_tit")["href"]
    displayed_link = result.select_one(".total_source").text.strip()
    snippet = result.select_one(".dsc_txt").text
    data.append({
        "position": index + 1,  # starts from 1, not from 0
        "title": title,
        "link": link,
        "displayed_link": displayed_link,
        "snippet": snippet
    })

Полный код

Теперь, объединив все функции вместе, мы получим четыре (4) функции:

  • Первая функция сохраняет HTML локально.
  • Вторая функция открывает локальный HTML и вызывает функцию парсера.
  • Третья функция делает фактический запрос и вызывает функцию парсера.
  • Четвертая функция — это синтаксический анализатор, который вызывается второй и третьей функциями.

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

import requests
import lxml, json
from bs4 import BeautifulSoup
headers = {
    "User-Agent":
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19582"
}
params = {
    "query": "bruce lee",  # search query
    "where": "web"         # nexearch will produce different results
}

# function that saves HTML locally
def save_naver_organic_results():
    html = requests.get("https://search.naver.com/search.naver", params=params, headers=headers).text
    # replacing every spaces so bruce lee will become bruce_lee 
    query = params['query'].replace(" ", "_")
    with open(f"{query}_naver_organic_results.html", mode="w") as file:
        file.write(html)

# fucntion that opens local HTML and calls a parser function
def extract_naver_organic_results_from_html():
    with open("bruce_lee_naver_organic_results.html", mode="r") as html_file:
        html = html_file.read()
        # calls naver_organic_results_parser() function to parse the page
        data = naver_organic_results_parser(html)
        print(json.dumps(data, indent=2, ensure_ascii=False))

# function that make an actual request and calls a parser function
def extract_naver_organic_results_from_url():
    html = requests.get("https://search.naver.com/search.naver", params=params, headers=headers)
    # calls naver_organic_results_parser() function to parse the page
    data = naver_organic_results_parser(html)
    print(json.dumps(data, indent=2, ensure_ascii=False))

# parser that's being called by 2-3 functions
def naver_organic_results_parser(html):
    soup = BeautifulSoup(html.text, "lxml")
    data = []
    for index, result in enumerate(soup.select(".total_wrap")):
        title = result.select_one(".total_tit").text.strip()
        link = result.select_one(".total_tit .link_tit")["href"]
        displayed_link = result.select_one(".total_source").text.strip()
        snippet = result.select_one(".dsc_txt").text
        data.append({
            "position": index + 1, # starts from 1, not from 0
            "title": title,
            "link": link,
            "displayed_link": displayed_link,
            "snippet": snippet
        })
    return data

Использование API органических результатов Naver Web

Кроме того, вы можете добиться тех же результатов, используя SerpApi. SerpApi — это платный API с бесплатным планом.

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

Кроме того, нет необходимости обходить блокировки от Google (или других поисковых систем), понимая, как масштабировать объем запросов, потому что это уже происходит под капотом у конечных пользователей с соответствующими планами. Попробуй на детской площадке.

Установить библиотеку SerpApi

pip install google-search-results

Пример кода для интеграции:

from serpapi import GoogleSearch
import os, json

def serpapi_get_naver_organic_results():
    params = {
        "api_key": os.getenv("API_KEY"),
        "engine": "naver",     # search engine (Google, Bing, DuckDuckGo..)
        "query": "Bruce Lee",  # search query
        "where": "web"
    }
    search = GoogleSearch(params)
    results = search.get_dict()
    data = []
    for result in results["organic_results"]:
        data.append({
            "position": result["position"],
            "title": result["title"],
            "link": result["link"],
            "displayed_link": result["displayed_link"],
            "snippet": result["snippet"]
        })
    print(json.dumps(data, indent=2, ensure_ascii=False))

Давайте посмотрим, что здесь происходит

Импортировать библиотеки serpapi, os, json

from serpapi import GoogleSearch
import os, json

Передать параметры поиска в виде словаря ({})

params = {
    "api_key": os.getenv("API_KEY"),
    "engine": "naver",                # search engine (Google, Bing, DuckDuckGo..)
    "query": "Bruce Lee",             # search query
    "where": "web"                    # filter to extract data from organic results
}

Извлечение данных

Это происходит под капотом, поэтому вам не нужно думать об этих двух строках кода.

search = GoogleSearch(params) # data extraction
results = search.get_dict()   # structured JSON which is being called later

Создайте list() для временного хранения данных

data = []

Повторить и append() извлечь данные в list() как словарь ({})

for result in results["organic_results"]:
    data.append({
        "position": result["position"],
        "title": result["title"],
        "link": result["link"],
        "displayed_link": result["displayed_link"],
        "snippet": result["snippet"]
    })

Распечатать добавленные данные

print(json.dumps(data, indent=2, ensure_ascii=False))
    
    
# ----------------
# part of the output
'''
[
  {
    "position": 1,
    "title": "Bruce Lee",
    "link": "https://brucelee.com/",
    "displayed_link": "brucelee.com",
    "snippet": "New Podcast Episode: #402 Flowing with Dustin Nguyen Watch + Listen to Episode “Your inspiration continues to guide us toward our personal liberation.” - Bruce Lee - More Podcast Episodes HBO Announces Order For Season 3 of Warrior! WARRIOR Seasons 1 & 2 Streaming Now on HBO & HBO Max “Warrior is still the best show you’re"
  }
  # other results..
]
'''

Если вам нужна дополнительная информация о планах, это объяснил ранее член команды SerpApi Джастин О'Хара в своем сообщении в блоге Разбивка подписок SerpApi (информация та же, за исключением того, что вам не нужно входить в SerpApi). сайт).

Ссылки

Outro

Если вам есть чем поделиться, какие-либо вопросы, предложения или что-то, что не работает должным образом, не стесняйтесь оставлять комментарии в разделе комментариев или через Twitter в @dimitryzub или @serp_api.

С уважением,
Дмитрий, и остальная часть команды SerpApi.

Присоединяйтесь к нам на Reddit | Твиттер | Ютуб

Больше контента на plainenglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку здесь.