Как парсить новости с Reuters с помощью Python и Scrapy
Table Of Content
- Пошаговое руководство по сбору новостных данных
- Введение в Scrapy
- Цели парсинга
- Парсинг результатов поиска
- Парсинг конкретных статей
- Настройка наших инструментов
- CSS Селекторы - Памятка по классам, именам и списку дочерних селекторов
- CSS селекторы позволяют выбирать и стилизовать HTML элементы. В частности, CSS селекторы позволяют выбирать...
- Шаг 1: Определение Парсера и Загрузка Селекторов
- Шаг 2: Создание запросов
- Шаг 3: Парсинг ответа
- Шаг 4: Запуск пауков
- Заключение
Пошаговое руководство по сбору новостных данных
Добро пожаловать в это практическое руководство по извлечению новостных статей с веб-сайта Reuters с использованием Python и Scrapy.
В эпоху принятия решений на основе данных, возможность собирать новостные данные может дать нам много преимуществ, начиная от понимания актуальных тем до проведения анализа настроений и улучшения журналистики данных. В течение этой статьи мы разблокируем потенциал парсинга веб-страниц, преобразуя сырые данные в осмысленные идеи.
Будь вы опытным программистом или новичком в программировании, это пошаговое руководство предназначено, чтобы оснастить вас навыками, необходимыми для ориентации в мире парсинга веб-страниц. Давайте приступим!
Введение в Scrapy
Представьте, что у вас есть собственный неутомимый цифровой агент, способный навигировать по огромному лабиринту интернета, точно извлекая и организовывая нужные вам данные. Познакомьтесь с Scrapy, мощным фреймворком для парсинга веб-страниц, который превращает эту фантазию в реальность.
В основе Scrapy лежит библиотека Python, специально разработанная для парсинга веб-страниц, метода, нацеленного на быстрое и эффективное извлечение больших объемов данных с веб-сайтов.
Этот инструмент открывает мир возможностей для анализа данных, прогнозирования трендов и многого другого. Давайте поглубже изучим его функциональные возможности и то, как его можно использовать для раскрытия истинной силы данных.
Сначала несколько понятий:
С помощью этих компонентов Scrapy способен справиться с проектами парсинга веб-страниц большого масштаба. Его способность обрабатывать как ширину (несколько веб-сайтов), так и глубину (детальное извлечение данных) задач парсинга веб-страниц делает его лучшим выбором для глобальных дата-майнеров. И сегодня мы воспользуемся этой силой, чтобы изучить кладовую, которой является Reuters. Давайте начнем!
Цели парсинга
В нашем путешествии по сбору данных, мы обратим внимание на настоящую золотую жилу информации - веб-сайт Reuters. Эта статья легко может быть адаптирована для других новостных веб-сайтов, которые функционируют похожим образом, таких как CNN или New York Times, но мы будем использовать Reuters для этой статьи. В частности, нас интересует парсинг двух типов страниц с веб-сайта: результатов поиска и отдельных статей.
Парсинг результатов поиска
Наш первый парсер, определенный в файле reuters.py
, будет описывать, как извлекать информацию из результатов поиска Reuters. Учитывая конкретный поисковый запрос - те же слова, которые вы бы использовали при поиске на их веб-сайте - парсер создает URL для поиска и получает список статей с их заголовками, датами и темами. При запуске с помощью Scrapy он выведет CSV-файл со списком этих статей, и этот список будет использоваться для последующего парсинга каждой статьи по отдельности.
Парсинг конкретных статей
Наш второй парсер, определенный в reuters_article.py
, более специализированный, создан для погружения в глубины отдельных статей и извлечения конкретных данных, таких как заголовок статьи, дата, автор и текст из всех абзацев.
С помощью совместных усилий этих двух парсеров мы готовы и готовы добывать сокровища Reuters, находя каждый крупицу информации, которую мы можем найти. Первый парсер получает все соответствующие статьи для поискового запроса, а второй парсит эти статьи одну за другой. Так что держитесь крепче, пришло время начать копать!
Настройка наших инструментов
Мы начнем наше путешествие с небольшого исследования веб-сайта Reuters. Мы будем искать элементы, которые нас интересуют, и сохранять их в конфигурационных файлах JSON под названиями selectors_reuters.json
и selectors_reuters_article.json
. Затем эти JSON-файлы будут загружены в наши парсеры для извлечения соответствующей информации.
Вот краткий обзор selectors_reuters.json
:
{
"titles": "a[data-testid=\"Heading\"]::text",
"urls": "a[data-testid=\"Heading\"]::attr(href)",
"dates": "time[data-testid=\"Body\"]::attr(datetime)",
"topics": "span[data-testid=\"Text\"]::text"
}
И selectors_reuters_article.json
:
{
"title": "h1[data-testid=\"Heading\"]::text",
"date": "time > span:nth-child(2)::text",
"author": "div[class*=\"article-header__author\"]::text",
"paragraphs": "p[data-testid*=\"paragraph-\"]::text"
}
Внутри этих JSON-файлов у нас есть ряд CSS-селекторов. Они нацеливаются на конкретные HTML-элементы на веб-странице, которые содержат ценные данные, которые мы ищем. Взгляните на картинку ниже для более наглядного примера.
Каждый CSS-селектор захватывает определенный HTML-элемент и связанные с ним данные. Например, на изображении у нас input.form-control
- это селектор для каждого тега input
, имеющего класс form-control
.
В наших JSON-файлах еще один пример - "a[data-testid=\"Heading\"]::text"
, который нацеливает текст внутри тегов якоря <a>
, имеющих атрибут data-testid
со значением "Heading".
Я не буду углубляться в то, как использовать CSS-селекторы здесь, но существует много ресурсов, которые очень хорошо объясняют их, поэтому, если вам интересно, ознакомьтесь со статьей ниже.
CSS Селекторы - Памятка по классам, именам и списку дочерних селекторов
CSS селекторы позволяют выбирать и стилизовать HTML элементы. В частности, CSS селекторы позволяют выбирать...
Теперь давайте взглянем на код, чтобы увидеть, как эти селекторы работают с нашими парсерами для сбора данных!
Шаг 1: Определение Парсера и Загрузка Селекторов
Давайте начнем с определения структуры скелета Scrapy парсеров и чтения определенной выше конфигурации. Структура файлов нашего проекта будет выглядеть следующим образом:
Папка config содержит нашу конфигурацию селекторов, папка spiders содержит различные парсеры, которые мы собираемся определить через минуту, а остальные файлы содержат вспомогательные функции (postprocessing.py и preprocessing.py) или специфичные для Scrapy настройки.py, где содержатся все настройки проекта, такие как активация конвейеров, промежуточных обработчиков и т.д.
При первом запуске работы с парсером Scrapy, нам по крайней мере нужно определить функции start_requests
и parse
- первая для определения URL-адресов, которые будут парситься, и вторая для определения информации, которую нужно извлечь после получения ответа.
В обоих Python парсерах мы открываем соответствующий JSON файл и используем json.load()
для преобразования CSS селекторов в словарь Python. Тогда наш скелет поискового парсера будет выглядеть так:
class ReutersSpider(scrapy.Spider):
""" Парсер, который обходит список результатов поиска статей Reuters. """
name = 'reuters'
with open("config/selectors_reuters.json") as selector_file:
selectors = json.load(selector_file)
def start_requests(self):
pass
def parse(self, response: TextResponse, **kwargs):
pass
А скелет парсера статьи будет выглядеть так (обратите внимание на list_of_articles
, который указывает на файл вывода поискового парсера).
class ReutersArticleSpider(scrapy.Spider):
""" Парсер, который обходит список статей по отдельности, основанный на парсере поиска Reuters. Для отображения страниц продуктов не требуется JS. """
name = 'reuters_article'
list_of_articles = "output/reuters.csv"
with open("config/selectors_reuters_article.json") as selector_file:
selectors = json.load(selector_file)
def start_requests(self):
pass
def parse(self, response: TextResponse, **kwargs):
pass
Теперь наши парсеры и селекторы готовы к действию, нам просто нужно определить логику парсинга.
Шаг 2: Создание запросов
Метод start_requests
в каждом парсере определяет начальную точку нашего поиска сокровищ. Это обязательная функция для создания списка start_urls
, которые Scrapy использует в качестве целей парсинга. Сначала нам нужно определить несколько вспомогательных функций для наших парсеров. Одна для создания URL-адреса поиска, одна для создания URL-адреса прокси ScrapeOps, одна для получения всех URL-адресов из списка словарей и одна для чтения списка словарей из CSV-файла, полученного от парсера поиска.
Небольшое замечание - вы можете пропустить использование прокси ScrapeOps, хотя у них есть щедрая бесплатная версия, если вы хотите столкнуться с проблемами парсинга самостоятельно, но имейте в виду, что вы можете столкнуться с проблемами, которые не описаны в этой статье.
def create_reuters_search_url(query):
return f"https://www.reuters.com/site-search/?query={query.replace(' ', '+')}"
def create_scrapeops_url(url, js=False, wait=False):
key = os.getenv("SCRAPEOPS_API_KEY")
scraping_url = f"https://proxy.scrapeops.io/v1/?api_key={key}&url={url}"
if js:
scraping_url += "&render_js=true"
if wait:
scraping_url += f"&wait_for={wait}"
return scraping_url
def get_urls_from_dict(list_of_dicts):
urls = []
for dict_with_url in list_of_dicts:
if dict_with_url.get("url"):
urls.append(dict_with_url.get("url"))
return urls
def read_csv(csv_path: str):
items = []
with open(csv_path) as csvfile:
reader = csv.DictReader(csvfile, delimiter=',')
for row in reader:
items.append(row)
return items
В парсере поиска, определенном в reuters.py
, мы создаем URL-адрес поиска с определенным запросом, используя первые две вспомогательные функции, которые мы определили выше. Здесь мы хотим искать ключевые слова "украинская война":
def start_requests(self):
start_urls = [
create_scrapeops_url(create_reuters_search_url("украинская война"), wait="time")
]
for url in start_urls:
yield scrapy.Request(url=url, callback=self.parse)
В reuters_article.py
мы получаем все URL-адреса из CSV-файла, полученного от парсера поиска, и поочередно обрабатываем статьи, используя первые две вспомогательные функции, которые мы определили выше.
def start_requests(self):
start_urls = get_urls_from_dict(read_csv(self.path_for_urls))
for url in start_urls:
yield scrapy.Request(url=create_scrapeops_url(url), callback=self.parse)
Шаг 3: Парсинг ответа
Теперь мы достигли пункта назначения и пришло время найти сокровище. Наши пауки используют селекторы для извлечения данных из HTML-ответа. В частности, метод parse
будет использовать CSS-селекторы, которые мы загрузили из нашего конфигурационного файла, чтобы по одному извлекать важные элементы из HTML-ответа.
В поисковом пауке, определенном в reuters.py
, мы будем извлекать заголовки, URL-адреса, даты и темы статей с помощью CSS-селекторов и создавать JSON-список элементов статей, содержащих эти данные:
def parse(self, response: TextResponse, **kwargs):
titles = [clean(title, remove_special=False) for title in response.css(self.selectors["titles"]).getall()]
urls = ["https://www.reuters.com" + url for url in response.css(self.selectors["urls"]).getall()]
dates = [clean(description) for description in response.css(self.selectors["dates"]).getall()]
topics = [clean(metadata) for metadata in response.css(self.selectors["topics"]).getall()]
articles = []
for title, url, date, topic in zip(titles, urls, dates, topics):
article = {
"title": title,
"url": url,
"date": date,
"topic": topic,
}
articles.append(article)
return articles
В специфическом пауке для статей, определенном в reuters_article.py
, мы будем извлекать для каждой отдельной статьи ее заголовок, текст, дату публикации и автора, и создавать JSON-элемент, содержащий эти данные:
def parse(self, response: TextResponse, **kwargs):
title = clean(response.css(self.selectors["title"]).get(), remove_special=False)
paragraphs = response.css(self.selectors["paragraphs"]).getall()
text = " ".join([clean(paragraph) for paragraph in paragraphs])
date = clean(response.css(self.selectors["date"]).get())
author = clean(response.css(self.selectors["author"]).get())
url = get_source_url_from_scraping_url(response.request.url)
return {
"title": title,
"url": url,
"date": date,
"author": author,
"text": text,
}
Шаг 4: Запуск пауков
Когда наши пауки закодированы и готовы исследовать просторы веба, пришло время выпустить их на свободу. В этом разделе мы узнаем, как сделать это с помощью командной строки Scrapy (CLI).
Для запуска наших пауков мы будем использовать следующую команду:
scrapy crawl reuters -o output/reuters.csv & scrapy crawl reuters_article -o output/reuters_article.csv
Вот краткое описание того, что происходит в этой команде:
И вуаля! С помощью простой команды мы запустили двух умных веб-пауков в их экспедицию по сбору данных. Пока они перемещаются по вебу, они будут собирать ценные данные и аккуратно организовывать их в CSV-файлах, готовых для анализа. Счастливого парсинга!
Заключение
И вот мы подошли к концу - пошаговое руководство по созданию собственной экспедиции по парсингу веб-страниц с помощью Scrapy для извлечения данных с сайта Reuters. Мы изучили мощь Scrapy и CSS-селекторов, а затем отправили пауков в дебри веба. Невероятно, что можно достичь с помощью сочетания Python и библиотеки Scrapy в сборе и обработке данных.
Но наше приключение не заканчивается здесь. Истинная сила парсинга веб-страниц заключается в его универсальности и масштабируемости. С вашим новым пониманием вы можете настроить своих пауков для исследования других веб-сайтов, изучения различных типов статей или поиска другой информации.
Так что оснаститесь этими новыми навыками и продолжайте исследовать богатые, неизведанные природные запасы веб-данных.
Но помните, как и в любом приключении, всегда будьте уважительны к местным правилам (в данном случае файлу robots.txt веб-сайта). Парсите только те данные, к которым у вас есть разрешение на доступ. Счастливой охоты за данными!