Высокопроизводительный веб-скрапинг

Данные стали чем-то очень важным. Одним из самых мощных средств сбора данных является парсинг/сканирование веб-страниц. Иногда прибыльный всегда веселый процесс.

Языком выбора для большинства парсеров является Python из-за его простоты использования и отличной поддержки библиотек, таких как Beautiful Soup 4, библиотека запросов и Selenium. Процесс веб-скрейпинга можно разбить на 3 этапа.

1. Разведка

Предполагая, что у вас есть целевой сайт/сайты, процесс начинается в консоли Chrome Dev. Возьмем чисто гипотетический пример: Reuters.

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

«Вы не можете использовать какие-либо роботы, программы-пауки, сценарии, сервисы, программное обеспечение или ручные или автоматические устройства, инструменты или процессы, предназначенные для извлечения данных или извлечения Контента, данных или информации из Сервиса, или иным образом получать доступ или собирать Контент, данные или информацию из Сервиса с использованием автоматизированных средств».

Условия использования Reuters.

В любом случае. Первым шагом было бы подумать о том, как статья структурирована. Он разбит на абзацы. Если мы внимательно посмотрим на HTML, каждый тег ‹p› имеет класс «Абзац-абзац-2Bgue».

Любой предыдущий опыт веб-разработки очень полезен на этом этапе, но далеко не обязателен. Нам также могут понадобиться некоторые метаданные, такие как время публикации и тому подобное. Может показаться заманчивым взломать модуль datetime и очистить строку со страницы, но если мы вместо этого обратим внимание на тег head и поищем какие-то метаданные, мы найдем их в формате ISO 8601, как мило.

Теперь, когда у нас есть все необходимое, давайте приступим к программированию.

2. Кодирование

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

import requests

r = requests.get(url)

Чтобы получить необработанный HTML, мы можем использовать атрибуты .content или .text. Затем мы загружаем его в BS4, чтобы начать парсинг:

import requests
from bs4 import BeautifulSoup
def scrape(url)
  r = requests.get(url)
  soup = BeautifulSoup(r.content)
  text = ""
  
  for p in soup.find_all("p", attrs={"class": "Paragraph-paragraph-2Bgue"}):
    text = text + p.text
  
  date = soup.find('meta', attrs={"name": "analyticsAttributes.articleDate"})["content"]
  return text, date

Убедившись, что он работает, мы можем превратить его в цикл, и все готово:

import requests
from bs4 import BeautifulSoup
urls = [the urls to scrape]
results = []
for url in urls:
  r = requests.get(url)
  soup = BeautifulSoup(r.content)
  text = ""
  
  for p in soup.find_all("p", attrs={"class": "Paragraph-paragraph-2Bgue"}):
    text = text + p.text
  date = soup.find('meta', attrs={"name": "analyticsAttributes.articleDate"})["content"]
  info = {
    "text": text,
    "date": date
  }
  results.append(info)

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

3. Оптимизация

Если нам нужно очистить тысячи или десятки тысяч страниц, этот код будет выполняться долго…

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

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

  • GRequests — основан на Gevent и сам по себе превосходен
  • request-futures — основано на concurrent.futures, а также превосходно

С их помощью мы можем запускать несколько запросов одновременно, я буду использовать GRequests для иллюстрации:

import grequests
urls = [the urls to send]
rs = (grequests.get(u) for u in urls)
grequests.map(rs)

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

import grequests
import threading

urls = [the urls to send]
rs = (grequests.get(u) for u in urls)
responses = grequests.map(rs) # returns a list of fulfilled responses

def scrape(r):
  
  soup = BeautifulSoup(r.content)
  text = ""
  
  for p in soup.find_all("p", attrs={"class": "Paragraph-paragraph-2Bgue"}):
    text = text + p.text
  
  date = soup.find('meta', attrs={"name": "analyticsAttributes.articleDate"})["content"]
  return text, date

with ThreadPool as pool:
  for result in pool.map(scrape, responses):
    print(result)

Только. Нравиться. Что. Теперь они будут обрабатываться одновременно, и мы можем увидеть значительный прирост производительности. Фантастика.

Веб-скрапинг может быть очень интересным и, хотя иногда незаконным, он может быть важной частью сбора данных, например, если вы занимаетесь машинным обучением. Это самое приложение тоже в основном сделано на Python :). Это также может быть способом быстро заработать пару дополнительных долларов на Upwork и подобных сервисах. Просто посмотрите на эту статью для некоторой мотивации. Я надеюсь, что вы нашли эту статью интересной и информативной. Любая обратная связь очень ценится.