zoukankan      html  css  js  c++  java
  • ip代理池的爬虫编写、验证和维护

    打算法比赛有点累,比赛之余写点小项目来提升一下工程能力、顺便陶冶一下情操
    本来是想买一个服务器写个博客或者是弄个什么翻墙的东西
    最后刷知乎看到有一个很有意思的项目,就是维护一个「高可用低延迟的高匿IP代理池」
    于是就想自己把这个项目写一次,其中有些更改,有些没有实现
    (数据结构作业要写广义表,写项目时发现还没写 :)

    原知乎链接:https://www.zhihu.com/question/47464143 (作者:resolvewang)
    原项目github链接:https://github.com/SpiderClub/haipproxy
    在此感谢原作者 resolvewang

    本项目链接:https://github.com/TangliziGit/proxypool

    项目早已在服务器上运行,这篇随笔就是未完待续吧
    咕咕咕,鸽了

    大体思路

    • 用爬虫爬下网络上的免费代理ip
    • 对爬取的代理ip进行验证,过滤掉一些不可用、低速的、有网页跳转的代理
    • 编写调度器,对各个网站定时爬取、验证免费代理;并对数据库中以爬取的代理进行验证
    • 写一个web api,提供数据库中已有的代理ip

    大体框架


    (我就只是想画个图而已 -_-)

    项目第一阶段目录:

    proxypool/
    ├── booter.py
    ├── dump.rdb
    ├── place.txt
    ├── proxypool
    │   ├── __init__.py
    │   ├── items.py
    │   ├── logger.py
    │   ├── middlewares.py
    │   ├── pipelines.py
    │   ├── rules.py
    │   ├── settings.py
    │   ├── spiders
    │   │   ├── base_spider.py
    │   │   ├── common_spider.py
    │   │   ├── __init__.py
    │   │   └── __pycache__
    │   ├── task_queue.py
    │   ├── user_agent.py
    │   └── validators
    │       ├── baidu_validator.py
    │       ├── base_validator.py
    │       ├── __init__.py
    │       ├── __pycache__
    │       └── zhihu_validator.py
    ├── __pycache__
    ├── scheduler.py
    ├── scrapy.cfg
    └── testip.py
    
    7 directories, 24 files
    
    // wc -l `find . -name "*.py"`
        5 ./booter.py
      119 ./proxypool/middlewares.py
        5 ./proxypool/spiders/common_spider.py
       67 ./proxypool/spiders/base_spider.py
        9 ./proxypool/spiders/__init__.py
        7 ./proxypool/logger.py
       91 ./proxypool/settings.py
       11 ./proxypool/user_agent.py
       86 ./proxypool/rules.py
       57 ./proxypool/task_queue.py
        7 ./proxypool/validators/zhihu_validator.py
        7 ./proxypool/validators/baidu_validator.py
       56 ./proxypool/validators/base_validator.py
        5 ./proxypool/validators/__init__.py
       55 ./proxypool/pipelines.py
       21 ./proxypool/items.py
        0 ./proxypool/__init__.py
       59 ./scheduler.py
      667 total
    

    细节描述

    爬虫部分

    数据流向
    RedisTaskQueue获取链接
    Spider发出请求
    RandomUserAgentMiddware换UA
    通过规则解析response
    送至RedisRawProxyPipeline,未处理数据存入数据库

    1. 爬取规则的编写
      很多免费代理网站的结构都很相似,基本上就是这样的(取自西刺代理):
      <tr class="odd">
        <td class="country"><img src="http://fs.xicidaili.com/images/flag/cn.png" alt="Cn" /></td>
        <td>223.240.209.18</td>
        <td>18118</td>
        <td>安徽合肥</td>
        <td class="country">高匿</td>
        <td>HTTP</td>
          <td>1分钟</td>
        <td>不到1分钟</td>
      </tr>
      <tr class="">
        <td class="country"><img src="http://fs.xicidaili.com/images/flag/cn.png" alt="Cn" /></td>
        <td>183.23.73.49</td>
        <td>61234</td>
        <td>广东东莞</td>
        <td class="country">高匿</td>
        <td>HTTPS</td>
          <td>1小时</td>
        <td>不到1分钟</td>
      </tr>
    ...
    

    通过编写爬取规则,我们就可以很方便爬取多个网站:

    RULES = {
        "xici" : {
            "parser_type": "page",
            "prefix": "//tr",
            "detail": "td/text()",
            ...
        }
    }
    

    然后就可以类似这样做请求:

    [x.xpath(rule["detail"]) for x in response.xpath(rule["prefix"])]
    
    1. 设计RedisTaskQueue类,让爬虫从中取得要爬取的网站
      为啥不让爬虫自己从数据库里取任务呢?
      呃 这个本来是为了多进程做的考虑,但是发现scrapy的Spider已经满足时间上的需求了
      考虑到以后可能需要这个类来让调度器调度爬虫,于是就留下来了

    2. 设计基本爬虫BaseSpider
      主要是以后用来做爬虫种类的拓展,比如这个网页可能会用js做个动态加载
      后续就要考虑到编写JsSpider(BaseSpider)
      目前只有一个爬虫CommonSpider(BaseSpider),用来爬普通网页(普通网页或json)

    3. Scrapy框架方面
      RawProxyUrlItem, ProxyUrlItem
      RandomUserAgentMiddleware, TimerMiddleware
      RedisRawProxyPipeline, RedisProxyPipeline

    验证部分

    数据流向
    RedisProxyQueue获取ip
    Spider发出验证请求
    TimerMiddleware开始计时
    TimerMiddleware结束计时
    通过规则验证response
    验证通过,送至RedisRawProxyPipeline,验证后ip存入数据库

    1. 验证规则
      与爬取规则相同,我们可选许多网站来做验证(每个代理对各网站有不同的效率)
      为了方便管理,写验证规则
      为什么要验证?
      一是为了保证代理速度
      二是为了保证不会存在“调包”的情况(中间人偷偷改了回复)

    2. 代理记分方式
      简单的用请求时间来作为分数,存入Redis的有序集合

    数据库部分

    数据项 描述
    ProxyPool:RAW_IPPOOL 集合 存储未验证ip
    ProxyPool:IPPOOL 有序集合 存储验证通过ip 按分数排序
    ProxyPool:TASK_QUEUE 调度器暂时存入请求链接

    调度器部分

    这部分未完待续
    仅仅写了获取爬虫和验证爬虫的简单启动
    下一步是根据爬取规则的时间间隔来调度

    WebAPI部分

    这部分根本还没写
    不过这是项目里最简单的东西
    准备适当时间入一个服务器,用Flask简单写一写就好了


    总结要点

    1. 在项目里专门写一个配置文件,用以配置工程内所有信息,避免hardcode
    2. 未来可能需要更多相似的类时,编写基类是必须的,考虑到方便编写和复用性
    3. 给类中添加某一功能时,如果项目较复杂,写Mixin合适一点
    4. 若对大量(或后续可能大量)的网站做爬取时,最好抽象出爬取规则,便于处理添加更多爬取网站、更改爬取数据顺序等
    5. 验证代理ip,考虑代理速度和中间人“调包”的可能
    6. 使用无表的数据库(such as Redis)时,为了结构清晰,将键值写成"XXX:A:B"的形式

    实现细节&需要注意的

    1. 每一个scrapy.Spider里可以自定义设置
    2. 比如设置pipeline, middleware, DOWNLOAD_DELAY
        custom_settings = {
            'DOWNLOAD_TIMEOUT': 1,
            'CONCURRENT_REQUESTS': 50,
            'CONCURRENT_REQUESTS_PER_DOMAIN': 50,
            'RETRY_ENABLED': False,
            'DOWNLOADER_MIDDLEWARES': {
                'proxypool.middlewares.TimerMiddleware': 500,
            },
            'ITEM_PIPELINES': {
                'proxypool.pipelines.RedisProxyPipeline': 200,
            }
        }
    
    1. Python取数据库的数据后,要看看是不是byte类型
    2. scrapy.Request包括errback, dont_filter等很有用的参数
    3. scrapy通过CrawlerProcess方法不能重复启动爬虫,如有需要,用多进程即可

    **未完待续**
  • 相关阅读:
    <mvc:annotation-driven>新增标签
    关于Spring中的<context:annotation-config/>配置
    <mvc:default-servlet-handler/>的作用
    各种WEB服务器自带的默认Servlet名称
    常用邮件协议
    vue-cli 脚手架项目简介(一)
    CSS3的transition和transform
    Spring配置文件<context:property-placeholder>标签使用漫谈
    使用Spring JDBCTemplate简化JDBC的操作
    技术探索不易
  • 原文地址:https://www.cnblogs.com/tanglizi/p/8712632.html
Copyright © 2011-2022 走看看