Skip to content

Commit 737998f

Browse files
authored
解决bug: redis有序集合变化后导致顺序重排,以至于重复test (#78)
* pip使用镜像 * 获取代理时设置timeout 因为在获取某些网站公布的代理ip时,由于该网站被墙或者其他原因,导致需要几分钟才能反馈。 * 第5页之后的代理ip都是两三天之后的信息,质量很差 * 解决bug: redis有序集合变化后导致顺序重排,以至于重复test 解决bug: #73 因为redis的有序集合是按照分数进行变化的。 当修改了分数之后,再继续遍历时,会导致一部分重复遍历,一部分没有取到。 因此改为通过游标进行遍历 * 保证redis中不存在分数为0的数据 因为之前的逻辑是要么减分,要么删除。 也就是说,如果减完分之后,分数是0,那么还存在于redis中。 现在对这个逻辑进行了优化。减完分之后再和PROXY_SCORE_MIN的值进行判断。 * 解决bug:设置LOG_DIR后没有效果 #62 * 删除没用的import * 修改pip源 * 修改BUG:获取分数和判断分数的逻辑 * 删除不能使用的代理iphai和xicidaili, 解决bug:zhandaye * 解决bug:只获取了一次zhandaye的目录 * 解决bug:因为zhandaye的crawl没有返回值导致报错 * 恢复iphai和xicidaili。同时对这两个类增加了ignore属性 * 增加MAX_PAGE变量 * 设置pip源 * 恢复Dockerfile
1 parent def06b1 commit 737998f

10 files changed

Lines changed: 59 additions & 49 deletions

File tree

proxypool/crawlers/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import pkgutil
22
from .base import BaseCrawler
3-
from .public.zhandaye import ZhandayeDetailCrawler
43
import inspect
54

5+
66
# load classes subclass of BaseCrawler
77
classes = []
88
for loader, name, is_pkg in pkgutil.walk_packages(__path__):
@@ -13,3 +13,4 @@
1313
and not getattr(value, 'ignore', False):
1414
classes.append(value)
1515
__all__ = __ALL__ = classes
16+

proxypool/crawlers/base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from retrying import retry
22
import requests
33
from loguru import logger
4+
from proxypool.setting import GET_TIMEOUT
45

56

67
class BaseCrawler(object):
@@ -9,8 +10,11 @@ class BaseCrawler(object):
910
@retry(stop_max_attempt_number=3, retry_on_result=lambda x: x is None, wait_fixed=2000)
1011
def fetch(self, url, **kwargs):
1112
try:
13+
kwargs.setdefault('timeout', GET_TIMEOUT)
14+
kwargs.setdefault('verify', False)
1215
response = requests.get(url, **kwargs)
1316
if response.status_code == 200:
17+
response.encoding = 'utf-8'
1418
return response.text
1519
except requests.ConnectionError:
1620
return
@@ -23,7 +27,6 @@ def crawl(self):
2327
for url in self.urls:
2428
logger.info(f'fetching {url}')
2529
html = self.fetch(url)
26-
print('html', html)
2730
for proxy in self.parse(html):
2831
logger.info(f'fetched proxy {proxy.string()} from {url}')
2932
yield proxy

proxypool/crawlers/public/iphai.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class IPHaiCrawler(BaseCrawler):
1010
iphai crawler, http://www.iphai.com/
1111
"""
1212
urls = [BASE_URL]
13-
13+
ignore = True
1414

1515
def parse(self, html):
1616
"""
@@ -32,3 +32,4 @@ def parse(self, html):
3232
crawler = IPHaiCrawler()
3333
for proxy in crawler.crawl():
3434
print(proxy)
35+

proxypool/crawlers/public/kuaidaili.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55

66

77
BASE_URL = 'https://www.kuaidaili.com/free/inha/{page}/'
8+
MAX_PAGE = 5
89

910

1011
class KuaidailiCrawler(BaseCrawler):
1112
"""
1213
kuaidaili crawler, https://www.kuaidaili.com/
1314
"""
14-
urls = [BASE_URL.format(page=page) for page in range(1, 200)]
15+
urls = [BASE_URL.format(page=page) for page in range(1, MAX_PAGE + 1)]
1516

1617
def parse(self, html):
1718
"""

proxypool/crawlers/public/xicidaili.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class XicidailiCrawler(BaseCrawler):
1111
xididaili crawler, https://www.xicidaili.com/
1212
"""
1313
urls = [BASE_URL]
14+
ignore = True
1415

1516
headers = {
1617
'User-Agent': 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'
@@ -48,3 +49,4 @@ def parse(self, html):
4849
crawler = XicidailiCrawler()
4950
for proxy in crawler.crawl():
5051
print(proxy)
52+

proxypool/crawlers/public/zhandaye.py

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from proxypool.schemas.proxy import Proxy
33
from proxypool.crawlers.base import BaseCrawler
44
from loguru import logger
5+
import re
56

67

78
BASE_URL = 'https://www.zdaye.com/dayProxy/{page}.html'
@@ -11,50 +12,47 @@ class ZhandayeCrawler(BaseCrawler):
1112
"""
1213
zhandaye crawler, https://www.zdaye.com/dayProxy/
1314
"""
14-
urls = [BASE_URL.format(page=page) for page in range(1, MAX_PAGE)]
15-
15+
urls_catalog = [BASE_URL.format(page=page) for page in range(1, MAX_PAGE)]
1616
headers = {
1717
'User-Agent': 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.61 Safari/537.36'
1818
}
19+
urls = []
1920

2021
def crawl(self):
21-
for url in self.urls:
22+
self.crawl_catalog()
23+
yield from super().crawl()
24+
25+
def crawl_catalog(self):
26+
for url in self.urls_catalog:
2227
logger.info(f'fetching {url}')
2328
html = self.fetch(url, headers=self.headers)
24-
self.parse(html)
29+
self.parse_catalog(html)
2530

26-
def parse(self, html):
31+
def parse_catalog(self, html):
2732
"""
2833
parse html file to get proxies
2934
:return:
3035
"""
3136
doc = pq(html)
3237
for item in doc('#J_posts_list .thread_item div div p a').items():
33-
post = 'https://www.zdaye.com' + item.attr('href')
34-
logger.info(f'get detail url: {post}')
35-
ZhandayeDetailCrawler(post).crawl()
36-
37-
38-
class ZhandayeDetailCrawler(BaseCrawler):
39-
urls = []
40-
ignore = True
41-
42-
def __init__(self, url):
43-
self.urls.append(url)
44-
super().__init__()
38+
url = 'https://www.zdaye.com' + item.attr('href')
39+
logger.info(f'get detail url: {url}')
40+
self.urls.append(url)
4541

4642
def parse(self, html):
4743
doc = pq(html)
4844
trs = doc('.cont br').items()
4945
for tr in trs:
5046
line = tr[0].tail
51-
host = line.split(':')[0]
52-
port = line.split(':')[1][:4]
53-
yield Proxy(host=host, port=port)
54-
47+
match = re.search(r'(\d+\.\d+\.\d+\.\d+):(\d+)', line)
48+
if match:
49+
host = match.group(1)
50+
port = match.group(2)
51+
yield Proxy(host=host, port=port)
5552

5653

5754
if __name__ == '__main__':
5855
crawler = ZhandayeCrawler()
5956
for proxy in crawler.crawl():
6057
print(proxy)
58+

proxypool/processors/getter.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ def run(self):
3333
if self.is_full():
3434
return
3535
for crawler in self.crawlers:
36+
logger.info(f'crawler {crawler} to get proxy')
3637
for proxy in crawler.crawl():
3738
self.redis.add(proxy)
3839

3940

4041
if __name__ == '__main__':
4142
getter = Getter()
42-
getter.run()
43+
getter.run()

proxypool/processors/tester.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,15 @@ def run(self):
7474
logger.info('stating tester...')
7575
count = self.redis.count()
7676
logger.debug(f'{count} proxies to test')
77-
for i in range(0, count, TEST_BATCH):
78-
# start end end offset
79-
start, end = i, min(i + TEST_BATCH, count)
80-
logger.debug(f'testing proxies from {start} to {end} indices')
81-
proxies = self.redis.batch(start, end)
82-
tasks = [self.test(proxy) for proxy in proxies]
83-
# run tasks using event loop
84-
self.loop.run_until_complete(asyncio.wait(tasks))
77+
cursor = 0
78+
while True:
79+
logger.debug(f'testing proxies use cursor {cursor}, count {TEST_BATCH}')
80+
cursor, proxies = self.redis.batch(cursor, count=TEST_BATCH)
81+
if proxies:
82+
tasks = [self.test(proxy) for proxy in proxies]
83+
self.loop.run_until_complete(asyncio.wait(tasks))
84+
if not cursor:
85+
break
8586

8687

8788
if __name__ == '__main__':

proxypool/setting.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
CYCLE_TESTER = env.int('CYCLE_TESTER', 20)
5454
# definition of getter cycle, it will get proxy every CYCLE_GETTER second
5555
CYCLE_GETTER = env.int('CYCLE_GETTER', 100)
56+
GET_TIMEOUT = env.int('GET_TIMEOUT', 10)
5657

5758
# definition of tester
5859
TEST_URL = env.str('TEST_URL', 'http://www.baidu.com')
@@ -75,5 +76,6 @@
7576
ENABLE_GETTER = env.bool('ENABLE_GETTER', True)
7677
ENABLE_SERVER = env.bool('ENABLE_SERVER', True)
7778

78-
logger.add(env.str('LOG_RUNTIME_FILE', 'runtime.log'), level='DEBUG', rotation='1 week', retention='20 days')
79-
logger.add(env.str('LOG_ERROR_FILE', 'error.log'), level='ERROR', rotation='1 week')
79+
logger.add(env.str('LOG_RUNTIME_FILE', join(LOG_DIR, 'runtime.log')), level='DEBUG', rotation='1 week', retention='20 days')
80+
logger.add(env.str('LOG_ERROR_FILE', join(LOG_DIR, 'error.log')), level='ERROR', rotation='1 week')
81+

proxypool/storages/redis.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -67,17 +67,15 @@ def decrease(self, proxy: Proxy) -> int:
6767
:param proxy: proxy
6868
:return: new score
6969
"""
70-
score = self.db.zscore(REDIS_KEY, proxy.string())
71-
# current score is larger than PROXY_SCORE_MIN
72-
if score and score > PROXY_SCORE_MIN:
73-
logger.info(f'{proxy.string()} current score {score}, decrease 1')
74-
if IS_REDIS_VERSION_2:
75-
return self.db.zincrby(REDIS_KEY, proxy.string(), -1)
76-
return self.db.zincrby(REDIS_KEY, -1, proxy.string())
77-
# otherwise delete proxy
70+
if IS_REDIS_VERSION_2:
71+
self.db.zincrby(REDIS_KEY, proxy.string(), -1)
7872
else:
73+
self.db.zincrby(REDIS_KEY, -1, proxy.string())
74+
score = self.db.zscore(REDIS_KEY, proxy.string())
75+
logger.info(f'{proxy.string()} score decrease 1, current {score}')
76+
if score <= PROXY_SCORE_MIN:
7977
logger.info(f'{proxy.string()} current score {score}, remove')
80-
return self.db.zrem(REDIS_KEY, proxy.string())
78+
self.db.zrem(REDIS_KEY, proxy.string())
8179

8280
def exists(self, proxy: Proxy) -> bool:
8381
"""
@@ -112,17 +110,19 @@ def all(self) -> List[Proxy]:
112110
"""
113111
return convert_proxy_or_proxies(self.db.zrangebyscore(REDIS_KEY, PROXY_SCORE_MIN, PROXY_SCORE_MAX))
114112

115-
def batch(self, start, end) -> List[Proxy]:
113+
def batch(self, cursor, count) -> List[Proxy]:
116114
"""
117115
get batch of proxies
118-
:param start: start index
119-
:param end: end index
116+
:param cursor: scan cursor
117+
:param count: scan count
120118
:return: list of proxies
121119
"""
122-
return convert_proxy_or_proxies(self.db.zrevrange(REDIS_KEY, start, end - 1))
120+
cursor, proxies = self.db.zscan(REDIS_KEY, cursor, count=count)
121+
return cursor, convert_proxy_or_proxies([i[0] for i in proxies])
123122

124123

125124
if __name__ == '__main__':
126125
conn = RedisClient()
127126
result = conn.random()
128127
print(result)
128+

0 commit comments

Comments
 (0)