Як виконати ротацію проксі під час скрапінгу

Коментарі: 0

Скрапінг даних часто сприймається веб-сайтами як небажана дія, що може призвести до блокування вашої IP-адреси. Проте існують ефективні способи мінімізації можливих наслідків, наприклад, використання проксі-серверів.

Проксі-сервери дають змогу приховувати справжню IP-адресу, роблячи скрапінг менш підозрілим для системи безпеки сервісу. Застосування декількох проксі та їхня ротація може значно зменшити ймовірність блокування, тому що взаємодія з вебсайтами виглядатиме природнішою.

Як приклад ми розглянемо використання цього методу на сайті інтернет-магазину книг. Скрапер збиратиме таку інформацію про книги, як назва, ціна та доступність. Основний акцент у цьому посібнику буде зроблено на техніці ротації проксі, а не на обробці та організації отриманих даних.

Підготовка робочого середовища та інтеграція проксі

Перед тим як приступити до написання функцій для ротації проксі та скрапінгу веб-сайту, важливо підготувати робоче середовище, встановивши та імпортувавши необхідні модулі Python. Три з п'яти необхідних модулів Python для цього скрипта встановлюються за допомогою такої команди:

pip install requests beautifulSoup4 lxml

Requests дає нам змогу надсилати HTTP-запити на вебсайт, beautifulsoup4 дає змогу витягувати інформацію з HTML-сторінки, яку надає requests, а lxml є парсером HTML.

Крім того, нам також будуть потрібні вбудований модуль threading для множинного тестування проксі на працездатність і json для читання з JSON-файлу:

import requests
import threading
from requests.auth import HTTPProxyAuth
import json
from bs4 import BeautifulSoup
import lxml
import time

url_to_scrape = "https://books.toscrape.com"
valid_proxies = []
book_names = []
book_price = []
book_availability = []
next_button_link = ""

Крок 1: Перевірка проксі зі списку

Щоб ефективно реалізувати скрапінг із ротацією проксі, необхідно мати список проксі, який можна використовувати для зміни IP-адрес у процесі роботи. Найкращий підхід до цього процесу - завантажити інформацію про наші проксі в окремий JSON-файл, організований таким чином:

[
  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  },

  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  },
  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  },
  {
    "proxy_address": "XX.X.XX.X:XX",
    "proxy_username": "",
    "proxy_password": ""
  }
]

У рядку "proxy_address" вказуємо IP-адресу і порт через двокрапку, у рядках "proxy_username" і "proxy_password" логін і пароль для авторизації.

Розглянемо приклад змісту JSON-файлу з чотирма проксі, які можна використовувати для скрапінгу. У цьому прикладі, деякі проксі вимагають аутентифікації - ім'я користувача та пароль, тоді як інші - ні.

def verify_proxies(proxy:dict):
    try:
        if proxy['proxy_username'] != "" and  proxy['proxy_password'] != "":
            proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
            res = requests.get(
                url_to_scrape,
                auth = proxy_auth,
                proxies={
                "http" : proxy['proxy_address']
                }
            )
        else:
            res = requests.get(url_to_scrape, proxies={
                "http" : proxy['proxy_address'],
            })
        
        if res.status_code == 200:
            valid_proxies.append(proxy)
            print(f"Proxy Validated: {proxy['proxy_address']}")
            
    except:
        print("Proxy Invalidated, Moving on")

Щоб переконатися в працездатності проксі зі списку, необхідно перевірити кожен із них, надсилаючи GET-запити на обраний веб-сайт. Якщо сервер відповість статус-кодом 200, проксі вважається активним і додається до списку діючих проксі. У разі невдачі скрипт продовжує перевірку наступних проксі.

Крок 2: Надсилання запиту для веб-скрапінгу

Для успішного скрапінгу даних з використанням бібліотеки BeautifulSoup, необхідно спочатку отримати HTML-код веб-сайту. Реалізація функції "request_function()", яка надсилає HTTP-запит через обраний проксі, дає змогу отримати цей HTML-код. Функція приймає URL веб-сайту і проксі, через який буде відправлено запит, забезпечуючи тим самим ротацію проксі для мінімізації ризику блокування.

def request_function(url, proxy):
    try:
        if proxy['proxy_username'] != "" and  proxy['proxy_password'] != "":
            proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
            response = requests.get(
                url,
                auth = proxy_auth,
                proxies={
                "http" : proxy['proxy_address']
                }
            )
        else:
            response = requests.get(url, proxies={
                "http" : proxy['proxy_address']
            })
        
        if response.status_code == 200:
            return response.text

    except Exception as err:
        print(f"Switching Proxies, URL access was unsuccessful: {err}")
        return None

Крок 3: Витяг даних із цільового веб-сайту

Функція "data_extract()" витягує необхідні дані з наданого HTML-коду. Вона збирає HTML-елемент, що містить інформацію про книгу, таку як назва, ціна та наявність. Також функція витягує посилання на наступну сторінку.

Функція тепер повертає не тільки списки назв книг, цін та інформації про наявність, а й повний URL наступної сторінки, що дає змогу скрипту перейти до наступної сторінки каталогу для продовження скрапінгу.

def data_extract(response):
    soup = BeautifulSoup(response, "lxml")
    books = soup.find_all("li", class_="col-xs-6 col-sm-4 col-md-3 col-lg-3")
    next_button_link = soup.find("li", class_="next").find('a').get('href')
    next_button_link=f"{url_to_scrape}/{next_button_link}" if "catalogue" in next_button_link else f"{url_to_scrape}/catalogue/{next_button_link}"

    for each in books:
        book_names.append(each.find("img").get("alt"))
        book_price.append(each.find("p", class_="price_color").text)
        book_availability.append(each.find("p", class_="instock availability").text.strip())

    return next_button_link

Крок 4: Інтеграція всіх компонентів

Щоб зв'язати все разом, нам необхідно виконати такі дії:

  1. Завантаження даних проксі з JSON-файлу. Запускаємо потік для кожного проксі, використовуючи "threading.Thread()". Це допоможе нам тестувати кілька проксі одночасно. Валідні проксі додаються в "valid_proxies()".
  2. Завантаження сторінки джерела. Спробуйте завантажити цільову сторінку з використанням робочих проксі. Якщо проксі виявляється непрацездатним, перейдіть на наступний, щоб гарантувати успішне завантаження сторінки.
  3. Використовуйте функцію "request_function()" для надсилання GET-запитів через активні проксі. Зберіть дані з сайту, використовуючи попередньо визначену функцію "data_extract()".
  4. Після збору даних, виведіть їх у консоль для перегляду або подальшої обробки.
with open("proxy-list.json") as json_file:
    proxies = json.load(json_file)
    for each in proxies:
        threading.Thread(target=verify_proxies, args=(each, )).start() 


time.sleep(4)

for i in range(len(valid_proxies)):
    response = request_function(url_to_scrape, valid_proxies[i])
    if response != None:
        next_button_link = data_extract(response)
        break
    else:
        continue

for proxy in valid_proxies:
   print(f"Using Proxy: {proxy['proxy_address']}")
   response = request_function(next_button_link, proxy)
   if response is not None:
       next_button_link = data_extract(response)
   else:
       continue


for each in range(len(book_names)):
    print(f"No {each+1}: Book Name: {book_names[each]} Book Price: {book_price[each]} and Availability {book_availability[each]}")

Готовий код

import requests
import threading
from requests.auth import HTTPProxyAuth
import json
from bs4 import BeautifulSoup
import time

url_to_scrape = "https://books.toscrape.com"
valid_proxies = []
book_names = []
book_price = []
book_availability = []
next_button_link = ""


def verify_proxies(proxy: dict):
   try:
       if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
           proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
           res = requests.get(
               url_to_scrape,
               auth=proxy_auth,
               proxies={
                   "http": proxy['proxy_address'],
               }
           )
       else:
           res = requests.get(url_to_scrape, proxies={
               "http": proxy['proxy_address'],
           })

       if res.status_code == 200:
           valid_proxies.append(proxy)
           print(f"Proxy Validated: {proxy['proxy_address']}")

   except:
       print("Proxy Invalidated, Moving on")


# Отримайте HTML-елемент сторінки
def request_function(url, proxy):
   try:
       if proxy['proxy_username'] != "" and proxy['proxy_password'] != "":
           proxy_auth = HTTPProxyAuth(proxy['proxy_username'], proxy['proxy_password'])
           response = requests.get(
               url,
               auth=proxy_auth,
               proxies={
                   "http": proxy['proxy_address'],
               }
           )
       else:
           response = requests.get(url, proxies={
               "http": proxy['proxy_address'],
           })

       if response.status_code == 200:
           return response.text

   except Exception as err:
       print(f"Switching Proxies, URL access was unsuccessful: {err}")
       return None


# Процес скрапінгу
def data_extract(response):
   soup = BeautifulSoup(response, "lxml")
   books = soup.find_all("li", class_="col-xs-6 col-sm-4 col-md-3 col-lg-3")
   next_button_link = soup.find("li", class_="next").find('a').get('href')
   next_button_link = f"{url_to_scrape}/{next_button_link}" if "catalogue" in next_button_link else f"{url_to_scrape}/catalogue/{next_button_link}"

   for each in books:
       book_names.append(each.find("img").get("alt"))
       book_price.append(each.find("p", class_="price_color").text)
       book_availability.append(each.find("p", class_="instock availability").text.strip())

   return next_button_link


# Отримайте проксі з файлу JSON

with open("proxy-list.json") as json_file:
   proxies = json.load(json_file)
   for each in proxies:
       threading.Thread(target=verify_proxies, args=(each,)).start()

time.sleep(4)

for i in range(len(valid_proxies)):
   response = request_function(url_to_scrape, valid_proxies[i])
   if response is not None:
       next_button_link = data_extract(response)
       break
   else:
       continue

for proxy in valid_proxies:
   print(f"Using Proxy: {proxy['proxy_address']}")
   response = request_function(next_button_link, proxy)
   if response is not None:
       next_button_link = data_extract(response)
   else:
       continue

for each in range(len(book_names)):
   print(
       f"No {each + 1}: Book Name: {book_names[each]} Book Price: {book_price[each]} and Availability {book_availability[each]}")

Фінальний результат

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

1.png

2.png

3.png

4.png

Таким чином, використання декількох проксі під час веб-скрапінгу дає змогу збільшити кількість запитів на цільовий ресурс і обійти блокування. Щоб забезпечити стабільність процесу скрапінгу, рекомендується використовувати IP-адреси з високою швидкістю і траст-фактором, як-от статичні ISP і динамічні резидентські проксі-сервери. Функціональність наданого скрипта може бути легко розширена для задоволення інших потреб під час скрапінгу даних.

Коментарії:

0 Коментаріїв