Пришло время для анонимного парсинга веб-страниц. В этом тебе поможет мощная команда: TOR, Privoxy, Python, Linux и наша статья.
Где можно использовать? В случаях, когда нужно изменить свой IP-адрес при выполнении нескольких запросов за единицу времени без блокировки соединения.
Настройка
Для успешной разработки парсинг-агента на прокси-сервере нужно установить Linux с такими инструментами:
- TOR: распределенная, анонимная и зашифрованная сеть, в которой данные пользователей и сами пользователи никогда не будут раскрыты.
- Stem: контроллер Python для TOR.
- Privoxy: не кеширующий веб-прокси с фильтрацией, повышенной конфиденциальностью и возможностью изменения данных веб-страниц и заголовков HTTP.
TOR (установка и настройка)
Установи TOR через терминал:
sudo apt-get update sudo apt-get install tor sudo /etc/init.d/tor restart
Далее:
- включи слушатель "ControlPort" для TOR по порту 9051– в нем TOR будет слушать все сообщения, направленные контроллеру;
- создай хэш нового пароля, предотвращающий случайный доступ к порту от внешних агентов;
- реализуй аутентификацию по cookie.
Пароль создаем так:
tor --hash-password my_password
Для примера 1234 превратится в:
16:9529EB03A306DE6F60171DE514EA2FCD49235BAF1E1E55897209679683
Отредактируй или раскоментируй файл /etc/tor/torrc следующим образом:
ControlPort 9051 # hashed password below is obtained via `tor --hash-password my_password` HashedControlPassword 16:9529EB03A306DE6F60171DE514EA2FCD49235BAF1E1E55897209679683 CookieAuthentication 1
Перезагрузимся:
sudo /etc/init.d/tor restart
Если всплыли какие-либо проблемы, используй ключ --controlport:
tor --controlport 9051 &
Python-Stem
Данный модуль используется для взаимодействия с контроллером Tor и программного отправления/получения команд управления.
sudo apt-get install python-stem
Privoxy
Установка:
sudo apt-get install privoxy
Укажи ему использовать TOR для маршрутизации всего трафика через серверы SOCKS в localhost по порту 9050:
sudo vim /etc/privoxy/configforward-socks5 / 127.0.0.1:9050
Перезагрузись:
sudo /etc/init.d/privoxy restart
Парсинг
После настройки прокси беремся за разработку агента для парсинга страниц с изменением IP-адреса каждые n-запросов. Класс назовем ConnectionManager.py и вставим в него следующую реализацию:
# -*- coding: utf-8 -*- import time import urllib2 from stem import Signal from stem.control import Controller class ConnectionManager: def __init__(self): self.new_ip = "0.0.0.0" self.old_ip = "0.0.0.0" self.new_identity() @classmethod def _get_connection(self): """ TOR new connection """ with Controller.from_port(port=9051) as controller: controller.authenticate(password="1234") controller.signal(Signal.NEWNYM) controller.close() @classmethod def _set_url_proxy(self): """ Request to URL through local proxy """ proxy_support = urllib2.ProxyHandler({"http": "127.0.0.1:8118"}) opener = urllib2.build_opener(proxy_support) urllib2.install_opener(opener) @classmethod def request(self, url): """ TOR communication through local proxy :param url: web page to parser :return: request """ try: self._set_url_proxy() request = urllib2.Request(url, None, { 'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) " "AppleWebKit/535.11 (KHTML, like Gecko) " "Ubuntu/10.10 Chromium/17.0.963.65 " "Chrome/17.0.963.65 Safari/535.11"}) request = urllib2.urlopen(request) return request except urllib2.HTTPError, e: return e.message def new_identity(self): """ new connection with new IP """ # First Connection if self.new_ip == "0.0.0.0": self._get_connection() self.new_ip = self.request("http://icanhazip.com/").read() else: self.old_ip = self.new_ip self._get_connection() self.new_ip = self.request("http://icanhazip.com/").read() seg = 0 # Если мы получим тот же ip-адрес, ждем 5 секунд, чтобы запросить новый IP-адрес пока self.old_ip == self.new_ip: time.sleep(5) seg += 5 print ("Ожидаем получения нового IP: %s секунд" % seg) self.new_ip = self.request("http://icanhazip.com/").read() print ("Новое подключение с IP: %s" % self.new_ip)
Примечание 1: это не совсем нормальная практика "жестко" программировать элементы в коде (IP, порт, пароль и т. д.), хотя в этом учебном случае полезно для понимания.
Примечание 2: на сайте ican можно увидеть IP-адрес клиента, делающего запрос. При выполнении запросов через сеть TOR наш IP скрывается, а TOR назначает нам другой, показанный на ican.
В конструкторе класса два атрибута (два IP) – текущий и новый IP, который нужно получить для TOR. Если TOR выдает тот же IP, отбрасываем и запрашиваем новый.
Также в коде есть два публичных метода: request(url) и new_identity(). Первый будет использоваться для обращения к странице, которая передается в качестве параметра, а второй – для назначения нового IP. Приватные методы _get_connection() и _set_url_proxy() нужны для установки нового соединения в сети TOR и отправки запроса через прокси.
Приведем пример изменения IP-адреса после выполнения 3 запросов к странице:
# -*- coding: utf-8 -*- from ConnectionManager import ConnectionManager cm = ConnectionManager() for j in range(5): for i in range(3): print ("\t\t" + cm.request("http://icanhazip.com/").read()) cm.new_identity()
При выполнении 3 запросов к веб-странице "icanhazip" запрашивается изменение IP вызовом метода new_identity(). Результат выполнения этого кода:
Новое подключение с IP: 185.38.14.171 185.38.14.171 185.38.14.171 185.38.14.171 Ожидаем получения нового IP: 5 секунд Ожидаем получения нового IP: 10 секунд Новое подключение с IP: 94.23.173.249 94.23.173.249 94.23.173.249 94.23.173.249 Ожидаем получения нового IP: 5 секунд Новое подключение с IP: 144.217.99.46 144.217.99.46 144.217.99.46 144.217.99.46 Ожидаем получения нового IP: 5 секунд Ожидаем получения нового IP: 10 секунд Новое подключение с IP: 62.210.129.246 62.210.129.246 62.210.129.246 62.210.129.246 Ожидаем получения нового IP: 5 секунд Ожидаем получения нового IP: 10 секунд Новое подключение с IP: 185.34.33.2 185.34.33.2 185.34.33.2 185.34.33.2
А теперь более реальный пример. В процессе анонимного парсинга обрабатывается сайт со статьями. IP-адрес перезапрашивается через каждые 5 запросов к странице:анонимного парсинга
-*- coding: utf-8 -*- from bs4 import BeautifulSoup from ConnectionManager import ConnectionManager URL_BASE = "http://jarroba.com/" MAX_PAGES = 30 counter_post = 0 cm = ConnectionManager() for i in range(1, MAX_PAGES): # Build URL if i > 1: url = "%spage/%d/" % (URL_BASE, i) else: url = URL_BASE print (url) # Do the request req = cm.request(url) status_code = req.code if req != '' else -1 if status_code == 200: html = BeautifulSoup(req.read(), "html.parser") posts = html.find_all('div', {'class': 'col-md-4 col-xs-12'}) for post in posts: counter_post += 1 title = post.find('span', {'class': 'tituloPost'}).getText() author = post.find('span', {'class': 'autor'}).getText() date = post.find('span', {'class': 'fecha'}).getText() print ( str(counter_post) + ' - ' + title + ' | ' + author + ' | ' + date) else: # если код состояния не 200 break # получаем новый ip, если 5 запросов уже были сделаны if i % 5 == 0: cm.new_identity()
Результат работы программы:
Новое подключение с IP: 192.42.116.16 http://jarroba.com/ 1 - Bit | Por: Ramón Invarato | 02-Abr-2017 ........... http://jarroba.com/page/5/ 37 - Multitarea e Hilos en Java con ejemplos II (Runnable & Executors) | Por: Ricardo Moya | 06-Dic-2014 ........... 45 - MEAN (Mongo-Express-Angular-Node) Desarrollo Full Stack JavaScript (Parte I) | Por: Ricardo Moya | 09-Jul-2014 Ожидаем получения нового IP: 5 секунд Новое подключение с IP: 178.175.131.194 http://jarroba.com/page/6/ 46 - Atributos para diseñadores Android (tools:xxxxx) | Por: Ramón Invarato | 26-May-2014 ........... http://jarroba.com/page/10/ 82 - Error Android – java.lang.NoClassDefFoundError sin motivo aparente | Por: Ramón Invarato | 30-May-2013 ........... 90 - ArrayList en Java, con ejemplos | Por: Ricardo Moya | 28-Mar-2013 Новое подключение с IP: 216.239.90.19 http://jarroba.com/page/11/ 91 - Intent – Pasar datos entre Activities – App Android (Video) | Por: Ricardo Moya | 03-Mar-2013 ........... http://jarroba.com/page/15/ 127 - Modelo “4+1” vistas de Kruchten (para Dummies) | Por: Ricardo Moya | 31-Mar-2012 ........... 135 - Aprender a programar conociendo lo que es un Entorno de Desarrollo Integrado (IDE) | Por: Ramón Invarato | 14-Feb-2012 Ожидаем получения нового IP: 5 секунд Новое подключение с IP: 144.217.99.46 http://jarroba.com/page/16/ 136 - Instalación del XAMPP para Windows | Por: Ricardo Moya | 13-Feb-2012 ........... 151 - Error Android – Aplicación no especifica el nivel de la API | Por: Ramón Invarato | 12-Dic-2011 http://jarroba.com/page/18/
Комментарии