基本思路
思路很简单,我就不画框架图了:
1. 从免费代理网站获取免费代理 2. 对免费代理进行校检存储 3. 愉快的开始使用吧
开发环境和开发包
软件
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.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,并发太快会被封。这个地方可以改进:
若没有代理池,还是需要抓取间隔
若已抓取到一部分代理,我们可以利用抓取到的代理进行并发,取消抓取间隔
作者:未不明不知不觉
链接:https://www.jianshu.com/p/a721ecad4377
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦