Skip to content

Commit 63dae0d

Browse files
author
lenarsaitov
committed
modify the functionality of adding proxies
1 parent 8c6ec57 commit 63dae0d

File tree

5 files changed

+109
-12
lines changed

5 files changed

+109
-12
lines changed

README.md

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Total number of parced announcements: 56. Average price per month: 236 426 rub
8080
* __is_latin__ - необходимо ли преобразывание любой встрещающейся __кириллицы__ в __латиницу__, по умолчанию _False_
8181
* __is_express_mode__ - необходимо ли <ins>ускорение</ins> (___в 5-10 раз___) сбор данных (<ins>__но без трех полей__</ins>, см примечание), по умолчанию _True_
8282
* __is_by_homeowner__ - необходимо ли собирать данные с объявлений, созданных только собственниками, по умолчанию _False_
83-
* __proxies__ - прокси (см раздел __Cloudflare, CloudScraper, proxies__), по умолчанию _None_
83+
* __proxies__ - прокси (см раздел __Cloudflare, CloudScraper, Proxy__), по умолчанию _None_
8484

8585
Если имеется желание __собрать данные со всех страниц__, то можно пропустить аргументы __start_page__ и __end_page__.
8686
В проекте предусмотрен функционал корректного завершения в случае окончания страниц. По данному моменту, следует изучить раздел __Ограничения__
@@ -140,21 +140,39 @@ cian_parsing_result_rent_long_1_2_moskva_04_Feb_2023_06_58_21_765479.csv
140140
| Capital Mars | real_estate_agent | https://www.cian.ru/rent/flat/282506328/ | Москва | rent | flat | 5 | 9 | 2 | 89.0 | 180000 | 2022 | 0 | 2006 | 53.0 | 15.0 | +79660619653 | Хамовники | 3-я Фрунзенская | Спортивная
141141
| MERSI | real_estate_agent | https://www.cian.ru/rent/flat/281562376/ | Москва | rent | flat | 8 | 16 | 2 | 80.0 | 200000 | 2500 | 0 | 2012 | -1 | -1 | +79652455850 | Замоскворечье | Мытная | Октябрьская
142142

143-
### Cloudflare, CloudScraper, proxies
143+
### Cloudflare, CloudScraper, Proxy
144144
Для обхода блокировки в проекте задействован **CloudScraper** (библиотека **cloudscraper**), который позволяет успешно обходить защиту **Cloudflare**.
145145

146146
Вместе с тем, это не гарантирует отсутствие возможности появления _у некоторых пользователей_ теста **CAPTCHA** при долговременном непрерывном использовании.
147147

148-
Поэтому была предоставлена возможность проставлять прокси, используя аргумент **proxies**, ожидаемая структура значения которой выглядит следующим образом
148+
#### Proxy
149+
Поэтому была предоставлена возможность проставлять прокси, используя аргумент **proxies** (_список прокси протокола HTTP_)
150+
151+
Пример:
149152

150153
```python
151-
proxies = {
152-
'http': '',
153-
'https': ''
154-
}
154+
proxies = [
155+
'85.26.146.169:80',
156+
'178.140.177.145:8889',
157+
'95.66.138.21:8880',
158+
'93.123.226.23:81',
159+
'46.47.197.210:3128',
160+
'213.184.153.66:8080',
161+
'62.33.207.201:3128',
162+
]
155163
```
156164

157-
То есть полностью аналогичная описанному в [документации библиотеки **requests** в разделе **session** главы __**proxy**__](https://requests.readthedocs.io/en/latest/user/advanced/#proxies).
165+
В процессе запуска утилита проходится по всем из них, пытаясь определить подходящий, то есть тот,
166+
который может, во первых, делать запросы, во вторых, не иметь тест **_CAPTCHA_**
167+
168+
Пример лога, в котором представлено все три возможных кейса
169+
170+
```
171+
The process of checking the proxies... Search an available one among them...
172+
1 | proxy 46.47.197.210:3128: unavailable.. trying another
173+
2 | proxy 213.184.153.66:8080: there is captcha.. trying another
174+
3 | proxy 95.66.138.21:8880: available.. stop searching
175+
```
158176

159177
### Ограничения
160178
Сайт выдает списки с объявлениями <ins>__лишь до 54 странцы включительно__</ins>. Это примерно _28 * 54 = 1512_ объявлений.

cianparser/helpers.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import urllib.request
2+
import urllib.error
3+
from bs4 import BeautifulSoup
4+
5+
16
def define_rooms_count(description):
27
if "1-комн" in description or "Студия" in description:
38
rooms_count = 1
@@ -23,3 +28,29 @@ def define_id_url(url: str):
2328
return url_path_elements[-2]
2429

2530
return "-1"
31+
32+
33+
def is_available_proxy(url, pip):
34+
try:
35+
proxy_handler = urllib.request.ProxyHandler({'http': pip})
36+
opener = urllib.request.build_opener(proxy_handler)
37+
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
38+
urllib.request.install_opener(opener)
39+
req = urllib.request.Request(url)
40+
html = urllib.request.urlopen(req)
41+
42+
try:
43+
soup = BeautifulSoup(html, 'lxml')
44+
except:
45+
soup = BeautifulSoup(html, 'html.parser')
46+
47+
if soup.text.find("Captcha") > 0:
48+
return False, True
49+
50+
return True, False
51+
except urllib.error.HTTPError as e:
52+
print('Error code: ', e.code)
53+
return not e.code, False
54+
except Exception as detail:
55+
print("Error:", detail)
56+
return False, False

cianparser/parser.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import pathlib
1010
from datetime import datetime
1111
import math
12+
import random
13+
import socket
1214

1315
from cianparser.constants import *
1416
from cianparser.helpers import *
@@ -20,9 +22,12 @@ def __init__(self, deal_type: str, accommodation_type: str, city_name: str, loca
2022
is_by_homeowner=False, proxies=None):
2123
self.session = cloudscraper.create_scraper()
2224
self.session.headers = {'Accept-Language': 'en'}
25+
2326
if proxies is not None:
24-
self.session.proxies = proxies
27+
if len(proxies) == 0:
28+
proxies = None
2529

30+
self.proxy_pool = proxies
2631
self.is_saving_csv = is_saving_csv
2732
self.is_latin = is_latin
2833
self.is_express_mode = is_express_mode
@@ -100,8 +105,41 @@ def build_url(self):
100105

101106
def load_page(self, number_page=1):
102107
self.url = self.build_url().format(number_page, self.location_id)
108+
109+
socket.setdefaulttimeout(10)
110+
was_proxy = self.proxy_pool is not None
111+
set_proxy = False
112+
self.url = self.build_url().format(number_page, self.location_id)
113+
114+
if was_proxy:
115+
print("The process of checking the proxies... Search an available one among them...")
116+
117+
ind = 0
118+
while self.proxy_pool is not None and set_proxy is False:
119+
ind += 1
120+
proxy = random.choice(self.proxy_pool)
121+
122+
available, is_captcha = is_available_proxy(self.url, proxy)
123+
if not available or is_captcha:
124+
if is_captcha:
125+
print(f" {ind} | proxy {proxy}: there is captcha.. trying another")
126+
else:
127+
print(f" {ind} | proxy {proxy}: unavailable.. trying another..")
128+
129+
self.proxy_pool.remove(proxy)
130+
if len(self.proxy_pool) == 0:
131+
self.proxy_pool = None
132+
else:
133+
print(f" {ind} | proxy {proxy}: available.. stop searching")
134+
self.session.proxies = {"http": proxy, "https": proxy}
135+
set_proxy = True
136+
137+
if was_proxy and set_proxy is False:
138+
return None
139+
103140
res = self.session.get(url=self.url)
104141
res.raise_for_status()
142+
105143
return res.text
106144

107145
def parse_page(self, html: str, number_page: int, count_of_pages: int, attempt_number: int):
@@ -114,7 +152,13 @@ def parse_page(self, html: str, number_page: int, count_of_pages: int, attempt_n
114152
print(f"The page from which the collection of information begins: \n {self.url}")
115153

116154
if soup.text.find("Captcha") > 0:
117-
print(f"\r{number_page} page: there is CAPTCHA... failed to parse page... ending...")
155+
print(f"\r{number_page} page: there is CAPTCHA... failed to parse page...")
156+
157+
if self.proxy_pool is not None:
158+
proxy = random.choice(self.proxy_pool)
159+
print(f"\r{number_page} page: new attempt with proxy {proxy}...")
160+
self.session.proxies = {"http": proxy}
161+
return False, attempt_number + 1, False
118162

119163
return False, attempt_number + 1, True
120164

@@ -722,6 +766,10 @@ def save_results(self):
722766

723767
def load_and_parse_page(self, number_page, count_of_pages, attempt_number):
724768
html = self.load_page(number_page=number_page)
769+
770+
if html is None:
771+
return False, attempt_number + 1, True
772+
725773
return self.parse_page(html=html, number_page=number_page, count_of_pages=count_of_pages,
726774
attempt_number=attempt_number)
727775

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = cianparser
3-
version = 0.4.23
3+
version = 0.4.24
44
description = Parser information from Cian website
55
url = https://github.com/lenarsaitov/cianparser
66
author = Lenar Saitov

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name='cianparser',
9-
version='0.4.23',
9+
version='0.4.24',
1010
description='Parser information from Cian website',
1111
url='https://github.com/lenarsaitov/cianparser',
1212
author='Lenar Saitov',

0 commit comments

Comments
 (0)