Пришло время для анонимного парсинга веб-страниц. В этом тебе поможет мощная команда: 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/
Комментарии
Запустил, но не меняются ip. В чем может быть проблема?
Есть ли подобное решение для windows?
Полезно. Спасибо!
Чисто в теории - хорошо. Но есть один нюанс: если тор-ноду побанили - ничего не сделать. Парсинг рухнет при проверке капчи.