Соберите смелые новости с помощью Python

вступление

В настоящее время у нас нет API, поддерживающего извлечение данных из Brave Search.

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

Решение можно использовать для личного использования, так как оно не включает Юридический щит США, который мы предлагаем для наших платных производственных и выше планов, и имеет свои ограничения, такие как необходимость обхода блоков, например, CAPTCHA.

Вы можете проверить нашу общедоступную дорожную карту, чтобы отслеживать прогресс для этого API: [Новый API] Brave Search

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

📌Примечание: Иногда в органической выдаче новостей может не быть. Этот пост в блоге получает новости из органических результатов и вкладок новостей.

Что такое смелый поиск

В предыдущем сообщении в блоге Brave ранее описывалось что такое Brave search. Во избежание дублирования контента эта информация не упоминается в этом сообщении блога.

Полный код

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

from bs4 import BeautifulSoup
import requests, lxml, json

# https://docs.python-requests.org/en/master/user/quickstart/#passing-parameters-in-urls
params = {
    'q': 'dune',            # query
    'source': 'web',        # source
    'tf': 'at'              # publish time (by default any time)
}

# https://docs.python-requests.org/en/master/user/quickstart/#custom-headers
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}


def scrape_organic_news():
    html = requests.get('https://search.brave.com/search', headers=headers, params=params)
    soup = BeautifulSoup(html.text, 'lxml')

    brave_organic_news = []

    for result in soup.select('#news-carousel .card'):
        title = result.select_one('.title').get_text().strip()
        link = result.get('href')
        time_published = result.select_one('.card-footer__timestamp').get_text().strip()
        source = result.select_one('.anchor').get_text().strip()
        favicon = result.select_one('.favicon').get('src')
        thumbnail = result.select_one('.img-bg').get('style').split(', ')[0].replace("background-image: url('", "").replace("')", "")

        brave_organic_news.append({
            'title': title,
            'link': link,
            'time_published': time_published,
            'source': source,
            'favicon': favicon,
            'thumbnail': thumbnail
        })

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


def scrape_tab_news():
    del params['source']
    html = requests.get('https://search.brave.com/news', headers=headers, params=params)
    soup = BeautifulSoup(html.text, 'lxml')

    brave_tab_news = []

    for result in soup.select('.snippet'):
        title = result.select_one('.snippet-title').get_text()
        link = result.select_one('.result-header').get('href')
        snippet = result.select_one('.snippet-description').get_text().strip()
        time_published = result.select_one('.ml-5+ .text-gray').get_text()
        source = result.select_one('.netloc').get_text()
        favicon = result.select_one('.favicon').get('src')
        thumbnail = result.select_one('.thumb')
        thumbnail = thumbnail.get('src') if thumbnail else None

        brave_tab_news.append({
            'title': title,
            'link': link,
            'snippet': snippet,
            'time_published': time_published,
            'source': source,
            'favicon': favicon,
            'thumbnail': thumbnail
        })

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


if __name__ == "__main__":
    # scrape_organic_news()
    scrape_tab_news()

Подготовка

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

pip install requests lxml beautifulsoup4

Извлечение базовых знаний с помощью селекторов CSS

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

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

Уменьшить вероятность блокировки

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

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

Код Пояснение

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

from bs4 import BeautifulSoup
import requests, lxml, json
  • BeautifulSoupдля сбора информации с веб-страниц. Он находится поверх синтаксического анализатора HTML или XML, предоставляя идиомы Pythonic для итерации, поиска и изменения дерева синтаксического анализа.
  • requestsсделать запрос на сайт.
  • lxmlдля быстрой обработки документов XML/HTML.
  • jsonдля преобразования извлеченных данных в объект JSON.

Создайте параметры URL и заголовки запроса:

# https://docs.python-requests.org/en/master/user/quickstart/#passing-parameters-in-urls
params = {
    'q': 'dune',            # query
    'source': 'web',        # source
    'tf': 'at'              # publish time (by default any time)
}

# https://docs.python-requests.org/en/master/user/quickstart/#custom-headers
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
}

Собрать органические новости

Эта функция очищает все органические данные новостей для URL-адреса https://search.brave.com/search и выводит все результаты в формате JSON.

Вам нужно сделать запрос, передать созданные параметры запроса и заголовки. Запрос возвращает HTML в BeautifulSoup:

html = requests.get('https://search.brave.com/search', headers=headers, params=params)
soup = BeautifulSoup(html.text, 'lxml')
  • timeout=30перестать ждать ответа через 30 секунд.
  • BeautifulSoup()где возвращенные данные HTML будут обработаны bs4.

Создайте список brave_organic_news для хранения всех новостей:

brave_organic_news = []

Чтобы извлечь нужные данные, нужно найти селектор, где они находятся. В нашем случае это селектор #news-carousel .card, в котором собраны все органические новости. Вам нужно повторить каждую новость в цикле:

for result in soup.select('#news-carousel .card'):
    # data extraction will be here

Чтобы извлечь данные, вам нужно найти соответствующие селекторы. SelectorGadget использовался для захвата селекторов CSS. Я хочу продемонстрировать, как работает процесс выбора селектора:

После того, как селекторы найдены, нам нужно получить соответствующий текст или значение атрибута:

title = result.select_one('.title').get_text().strip()
link = result.get('href')
time_published = result.select_one('.card-footer__timestamp').get_text().strip()
source = result.select_one('.anchor').get_text().strip()
favicon = result.select_one('.favicon').get('src')
thumbnail = result.select_one('.img-bg').get('style').split(', ')[0].replace("background-image: url('", "").replace("')", "")
  • select_one()/select(), чтобы запустить селектор CSS для проанализированного документа и вернуть все соответствующие элементы.
  • get_text()для получения текстовых данных от узла.
  • get(<attribute>)для получения данных атрибутов от узла.
  • strip()чтобы вернуть копию строки с удаленными начальными и конечными символами.
  • replace()для замены всех вхождений старой подстроки на новую без лишних элементов.
  • split()чтобы вернуть список слов в строке, разделяя строку строкой-разделителем.

После извлечения данных из элемента они добавляются в список brave_organic_news:

brave_organic_news.append({
    'title': title,
    'link': link,
    'time_published': time_published,
    'source': source,
    'favicon': favicon,
    'thumbnail': thumbnail
})

Полная функция парсинга органических новостей будет выглядеть так:

def scrape_organic_news():
    html = requests.get('https://search.brave.com/search', headers=headers, params=params)
    soup = BeautifulSoup(html.text, 'lxml')

    brave_organic_news = []

    for result in soup.select('#news-carousel .card'):
        title = result.select_one('.title').get_text().strip()
        link = result.get('href')
        time_published = result.select_one('.card-footer__timestamp').get_text().strip()
        source = result.select_one('.anchor').get_text().strip()
        favicon = result.select_one('.favicon').get('src')
        thumbnail = result.select_one('.img-bg').get('style').split(', ')[0].replace("background-image: url('", "").replace("')", "")

        brave_organic_news.append({
            'title': title,
            'link': link,
            'time_published': time_published,
            'source': source,
            'favicon': favicon,
            'thumbnail': thumbnail
        })

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

Выход:

[
  {
    "title": "Dune subreddit group bans AI-generated art for being ‘low effort’ ...",
    "link": "https://www.theguardian.com/film/2022/oct/16/dune-subreddit-group-bans-ai-generated-art-for-being-low-effort",
    "time_published": "2 days ago",
    "source": "theguardian.com",
    "favicon": "https://imgs.search.brave.com/9NJ5RrmLraV8oAt2-ItS_A5rM7MNWTBcXog1rbJwni0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNGRmYTNkMTZl/NmJhYTQwYmQ4NDRj/MzQ4NDZkNGQ0YTgy/ZWRlZDM4YWVkMzM4/NmM0Y2Y2NTgyMTQ5/NzQxOTExYy93d3cu/dGhlZ3VhcmRpYW4u/Y29tLw",
    "thumbnail": "https://imgs.search.brave.com/PO4d1ks7aUaIUG07Aty1tXis_sdCsr9ZUJ-IXB5Hr7U/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9pLmd1/aW0uY28udWsvaW1n/L21lZGlhL2EzOTQy/NWM5N2M0MzlmY2Vi/Yzc3NTFlYzUzMDQ0/MmJmYWFjOWNlZGYv/Njk3XzBfMjk1OF8x/Nzc3L21hc3Rlci8y/OTU4LmpwZz93aWR0/aD0xMjAwJmhlaWdo/dD02MzAmcXVhbGl0/eT04NSZhdXRvPWZv/cm1hdCZmaXQ9Y3Jv/cCZvdmVybGF5LWFs/aWduPWJvdHRvbSUy/Q2xlZnQmb3Zlcmxh/eS13aWR0aD0xMDBw/Jm92ZXJsYXktYmFz/ZTY0PUwybHRaeTl6/ZEdGMGFXTXZiM1ps/Y214aGVYTXZkR2N0/WkdWbVlYVnNkQzV3/Ym1jJmVuYWJsZT11/cHNjYWxlJnM9NTVi/NDY1MzM1ZDcyNWNh/YzAxNDg2Nzk2NTNm/ZGJlMzg"
  },
  {
    "title": "Emily Watson: The Dune: ‘The Sisterhood’, ‘God’s Creatures’ ...",
    "link": "https://deadline.com/2022/10/emily-watson-the-dune-the-sisterhood-and-gods-creatures-star-says-she-loves-being-in-front-of-the-camera-because-it-gives-her-a-level-of-trust-1235145603/",
    "time_published": "3 days ago",
    "source": "deadline.com",
    "favicon": "https://imgs.search.brave.com/hbAJswXoM5V6EWHa7svVfcuTtKDvVN3HaccvtoCfhVo/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvMjk2OWMwMWU5/ZDU0MGJjMDdkZjY1/NTJmZmU3OGEzMDU5/Y2U2MWQ2ODE5Njdj/NTEwYzA2MGY5ODYy/N2NlNTkzYS9kZWFk/bGluZS5jb20v",
    "thumbnail": "https://imgs.search.brave.com/-vqS2wBthAQPSJiCpxSHW_IcG2CFsVw-MWbUykIOIZQ/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9kZWFk/bGluZS5jb20vd3At/Y29udGVudC91cGxv/YWRzLzIwMjIvMTAv/ZW1pbHkuanBnP3c9/MTAyNA"
  },
  {
    "title": "DUNE: THE SISTERHOOD Taps OBI-WAN KENOBI And GAME OF THRONES Star ...",
    "link": "https://comicbookmovie.com/sci-fi/dune/dune-the-sisterhood-taps-obi-wan-kenobi-and-game-of-thrones-star-indira-varma-for-lead-role-a197335",
    "time_published": "2 days ago",
    "source": "comicbookmovie.com",
    "favicon": "https://imgs.search.brave.com/ZqE9eQ5BIk1l3ZH7MOTWEPqScYt79E7VyJ5D46uRTeA/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvYzNlOWQ3NGE2/MzQwYWExZjRhMDEx/Njk1NGE5OTlkYzhj/NjZmZmEwNjVlYmY1/ODM1MzIyMWZjNWQy/M2FjM2JlNi9jb21p/Y2Jvb2ttb3ZpZS5j/b20v",
    "thumbnail": "https://imgs.search.brave.com/-wstGGJXxONeT0Ig7TNrqFw1DLK5kIWLdm9V-_Ne4lU/rs:fit:200:200:1/g:ce/aHR0cHM6Ly9jb21p/Y2Jvb2ttb3ZpZS5j/b20vaW1hZ2VzL2Fy/dGljbGVzL2Jhbm5l/cnMvMTk3MzM1Lmpw/ZWc"
  },
  ... other news
]

Очистить вкладку новости

Эта функция очищает все данные новостей вкладки для URL-адреса https://search.brave.com/news и выводит все результаты в формате JSON.

Вам нужно сделать запрос, передать созданные заголовки запроса и параметры без параметра 'source'. Запрос возвращает HTML в BeautifulSoup:

del params['source']
html = requests.get('https://search.brave.com/news', headers=headers, params=params)
soup = BeautifulSoup(html.text, 'lxml')

Создайте список brave_tab_news для хранения всех новостей:

brave_tab_news = []

Чтобы получить данные из всех новостей на странице, вам нужно найти селектор элементов .snippet. Вам нужно повторить каждый элемент в цикле:

for result in soup.select('.snippet'):
    # data extraction will be here

На этой странице соответствующие селекторы отличаются. Так что эта функция также использовала SelectorGadget для захвата селекторов CSS. Я хочу продемонстрировать, как работает процесс выбора селектора:

Отличие извлечения данных в этой функции в том, что здесь можно получить snippet:

title = result.select_one('.snippet-title').get_text()
link = result.select_one('.result-header').get('href')
snippet = result.select_one('.snippet-description').get_text().strip()
time_published = result.select_one('.ml-5+ .text-gray').get_text()
source = result.select_one('.netloc').get_text()
favicon = result.select_one('.favicon').get('src')
thumbnail = result.select_one('.thumb')
thumbnail = thumbnail.get('src') if thumbnail else None

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

После извлечения данных из элемента они добавляются в список brave_tab_news:

brave_tab_news.append({
    'title': title,
    'link': link,
    'snippet': snippet,
    'time_published': time_published,
    'source': source,
    'favicon': favicon,
    'thumbnail': thumbnail
})

Полная функция очистки новостей вкладок будет выглядеть так:

def scrape_tab_news():
    del params['source']
    html = requests.get('https://search.brave.com/news', headers=headers, params=params)
    soup = BeautifulSoup(html.text, 'lxml')

    brave_tab_news = []

    for result in soup.select('.snippet'):
        title = result.select_one('.snippet-title').get_text()
        link = result.select_one('.result-header').get('href')
        snippet = result.select_one('.snippet-description').get_text().strip()
        time_published = result.select_one('.ml-5+ .text-gray').get_text()
        source = result.select_one('.netloc').get_text()
        favicon = result.select_one('.favicon').get('src')
        thumbnail = result.select_one('.thumb')
        thumbnail = thumbnail.get('src') if thumbnail else None

        brave_tab_news.append({
            'title': title,
            'link': link,
            'snippet': snippet,
            'time_published': time_published,
            'source': source,
            'favicon': favicon,
            'thumbnail': thumbnail
        })

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

Выход:

[
  {
    "title": "Dune Games All Have The Same Problem",
    "link": "https://www.msn.com/en-us/entertainment/gaming/dune-games-all-have-the-same-problem/ar-AA1364qI",
    "snippet": "Dune video games have managed to reflect the strategy of ruling Arrakis, but they've failed to capture the franchise’s most fascinating quality.",
    "time_published": "1 day ago",
    "source": "ScreenRant on MSN.com",
    "favicon": "https://imgs.search.brave.com/-8C0opPjysKHAWE2H2sJ4d6TC-jhlh7zWo326qw_QK4/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNGJmODdhNGJk/YmYxY2RkMDU4YzNl/ZjY3OTUyMmNmMzlm/YjYyMmM4MDJlYmQ5/Yzg4ZjY2MzJiZDg4/MWEzYThkNi93d3cu/bXNuLmNvbS8",
    "thumbnail": "https://imgs.search.brave.com/JqEcp16LXMD8UqcFusDHrpixgLnI5EBQURQ9b02ox4U/rs:fit:1335:225:1/g:ce/aHR0cHM6Ly93d3cu/YmluZy5jb20vdGg_/aWQ9T1ZGVC4waktv/VGVKV21DY2VtNUhU/anJyb3NDJnBpZD1O/ZXdz"
  },
  {
    "title": "Dune: The Sisterhood to begin shooting in November as Indira Varma joins cast",
    "link": "https://www.flickeringmyth.com/2022/10/dune-the-sisterhood-to-begin-shooting-in-november-as-indira-varma-joins-cast/",
    "snippet": "As HBO Max and Legendary Television prepare to kick off production on Dune: The Sisterhood, Deadline is reporting that Indira Varma (Game of Thrones, Star Wars: Obi-Wan Kenobi) has joined the cast of the Dune spinoff television series.",
    "time_published": "4 hours ago",
    "source": "Flickeringmyth",
    "favicon": "https://imgs.search.brave.com/syftwTbOGwbuYrlw8LiSFZpqkyNYOzcn2zYsu9tP7g4/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNzQzMTViMjdk/ZWUxNTc3MWY3N2Vi/ZDEwZWI1ODgzOTIy/YzMzYjE5ZGYxODdi/YTUzYzZlZTFkOWM1/M2RlNWI3Yi93d3cu/ZmxpY2tlcmluZ215/dGguY29tLw",
    "thumbnail": null
  },
  {
    "title": "Dune subreddit group bans AI-generated art for being ‘low effort’",
    "link": "https://www.theguardian.com/film/2022/oct/16/dune-subreddit-group-bans-ai-generated-art-for-being-low-effort?amp;amp;amp",
    "snippet": "Moderators of community devoted to sci-fi films and novels say they want to prioritise ‘human-made’ art",
    "time_published": "3 days ago",
    "source": "The Guardian",
    "favicon": "https://imgs.search.brave.com/9NJ5RrmLraV8oAt2-ItS_A5rM7MNWTBcXog1rbJwni0/rs:fit:32:32:1/g:ce/aHR0cDovL2Zhdmlj/b25zLnNlYXJjaC5i/cmF2ZS5jb20vaWNv/bnMvNGRmYTNkMTZl/NmJhYTQwYmQ4NDRj/MzQ4NDZkNGQ0YTgy/ZWRlZDM4YWVkMzM4/NmM0Y2Y2NTgyMTQ5/NzQxOTExYy93d3cu/dGhlZ3VhcmRpYW4u/Y29tLw",
    "thumbnail": "https://imgs.search.brave.com/To--PX2Q-ovIhZzDlN07Go1mLvlaZmj7p6Nb3x-4dZU/rs:fit:1335:225:1/g:ce/aHR0cHM6Ly93d3cu/YmluZy5jb20vdGg_/aWQ9T1ZGVC5ZVUVJ/VzhYc1hxdDZmLTlR/OFl6S1NpJnBpZD1O/ZXdz"
  },
  ... other news
]

Ссылки

Первоначально опубликовано на SerpApi: https://serpapi.com/blog/scrape-organic-news-from-brave-search-with-python/

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

Добавьте Запрос функции💫 или Ошибку🐞