Python3 搭建免费代理池
2018-12-07 本文已影响1人
未不明不知不觉
最近需要抓取一些数据,但是苦于没有代理,之前写过一个简单的代理,最近重构一下,这里写下思路,希望大家多提意见
基本思路
思路很简单,我就不画框架图了:
1. 从免费代理网站获取免费代理
2. 对免费代理进行校检存储
3. 愉快的开始使用吧
开发环境和开发包
软件
- Visual Studio Code
- Python3.7
- Windows 10
- Redis
包
aiohttp
dummy_useragent
aredis
logzero
cuckoopy
项目结构
使用tree /F
来获取当前的目录结构,得到下面的输出
C:.
│ .gitignore
│ LICENSE
│ README.rst
│ requirement.txt
│ setup.py
│
├─freeproxy_cn
│ │ __init__.py
│ │
│ ├─core
│ │ channel.py
│ │ engine.py
│ │ http.py
│ │ __init__.py
│ │
│ ├─site
│ │ cool.py
│ │ crossin.py
│ │ eight9.py
│ │ I337.py
│ │ ip3366.py
│ │ iphai.py
│ │ ipjiang.py
│ │ kuai.py
│ │ proxydocker.py
│ │ seofang.py
│ │ super.py
│ │ threeone.py
│ │ xiaosu.py
│ │ xici.py
│ │ xroxy.py
│ │ __init__.py
│ │
│ ├─site2
│ │ freecz.py
│ │ idcloak.py
│ │ myproxy.py
│ │ nova.py
│ │ __init__.py
│ │
│ └─util
│ pipe.py
│ __init__.py
│
└─test
test_bug.py
test_hkong.py
test_site.py
__init__.py
- test: 测试脚本
- site: 不需要设置代理就能抓取的网站
- site2: 需要代理才能抓取的国外网站
- core: 核心包
- util: 工具包,含常用函数
- setup.py: python 打包程序
核心代码
在抓取的过程中我发现,很多代理的网站存储是一个表格结构,每个table的第一个tr为表头可以忽略,
示例一 示例二
但是我们可以观察到代理的host和port的位置可能不同,为了提取代理的host和port,我们可以用下面的代码提取
# channel.py
class Channel(object):
def __init__(self, proxy=None, *arg, **kwargs):
self.http = Http()
self.start_pos = 2
self.td_idx = [1, 2]
async def handle(self, url):
doc = await self.http.get(url) >> to_doc
items = doc.xpath("//table//tr[position()>=%s]" % self.start_pos)
proxies = []
for item in items:
try:
host = item >> extra_head(
"./td[position()=%s]//text()" % self.td_idx[0])
port = item >> extra_head(
"./td[position()=%s]//text()" % self.td_idx[1])
except Exception:
continue
if len(port) > 5:
continue
proxies.append((host, port))
另外有些网站的有抓取模板,我们可能需要抓取免费代理的前几页,例如对于西刺
# xici.py
class XiCi(Channel):
def __init__(self):
super(XiCi, self).__init__()
self.name = "xici"
self.url_plt = [
"http://www.xicidaili.com/wn/%s",
"http://www.xicidaili.com/wt/%s",
"http://www.xicidaili.com/nn/%s",
"http://www.xicidaili.com/nt/%s",
]
self.td_idx = [2, 3]
async def boostrap(self):
urls = []
for i in range(1, 3):
urls += [plt % i for plt in self.url_plt]
self.funcmap = {self.handle: urls}
在上面的boostrap
函数里面我们初始化了抓取url的列表,默认为抓代理的前两页,至于funcmap
下面会用到
http模块的
useragent
我使用了自己写的一个dummy_useragent包,和faker_useragent类似,但是省略了初始化阶段(网络问题,国内使用faker_useragent初始化总会报错)
engin
是代理抓取调度的核心,核心代码如下
#engin.py
class Engin(object):
async def _run(self):
tasks = []
for site in self.sites:
tasks.append(asyncio.ensure_future(self.site_run(site)))
await asyncio.gather(*tasks)
async def site_run(self, site):
async with aiohttp.ClientSession() as session:
site.set_http(Http(session))
logger.debug("start grab site {}".format(site.name))
await site.boostrap()
funcs = site.funcmap.keys()
for zp in zip(*site.funcmap.values()):
for func_param in zip(funcs, zp):
func, param = func_param
coro = func(param)
await coro
await asyncio.sleep(2) # 并发抓取 容易封禁ip
async def run(self):
while True:
logger.debug("开始新一轮的抓取")
await self._run()
await asyncio.sleep(60 * 20)
上面代码很简单,主要是20分钟抓取一次代理,对于每个网站的抓取间隔为两秒,因为抓取使用的是本地 IP,并发太快会被封。这个地方可以改进:
- 若没有代理池,还是需要抓取间隔
- 若已抓取到一部分代理,我们可以利用抓取到的代理进行并发,取消抓取间隔
项目地址
代码很简单,也有很多不足之处,适合初学者练手,一起学习交流。求start ⭐