手记

Python3 搭建免费代理池

基本思路

思路很简单,我就不画框架图了:

1. 从免费代理网站获取免费代理
2. 对免费代理进行校检存储
3. 愉快的开始使用吧

开发环境和开发包

软件

  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.pyclass 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.pyclass 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.pyclass 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,并发太快会被封。这个地方可以改进:

  1. 若没有代理池,还是需要抓取间隔

  2. 若已抓取到一部分代理,我们可以利用抓取到的代理进行并发,取消抓取间隔



作者:未不明不知不觉
链接:https://www.jianshu.com/p/a721ecad4377


0人推荐
随时随地看视频
慕课网APP