当前位置: 首页 > 工具软件 > go_spider > 使用案例 >

python爬虫-实现代理池的爬虫模块(proxy_spider)

公冶翰池
2023-12-01

python编程快速上手(持续更新中…)

python爬虫热点项目(Flask )


1 爬虫模块的需求

抓取各个免费代理ip网站上的免费代理IP,如果可用保存数据库中,需要抓取代理ip页面如下:

2 爬虫模块的设计思路

通用爬虫: 通过指定URL列表, 分组XPATH和组内XPATH, 来提取不同网站的代理IP
具体爬虫: 用于抓取具体代理IP网站,通过继承通用爬虫实现
爬虫运行模块: 启动爬虫, 抓取代理IP, 进行检测, 如果可用, 就存储到数据库中;

3 实现通用爬虫(base_spider.py)

定义一个BaseSpider类, 继承object
提供三个类成员变量:urls, group_xpath, detail_xpath: ip, port, area
提供初始方法, 传入爬虫URL列表, 分组XPATH, 详情(组内)XPATH
对外提供一个获取代理IP的方法

import requests
from utils.http import get_request_headers
from lxml import etree
from domain import Proxy

"""
8.3 实现通用爬虫
目标: 实现可以指定不同URL列表, 分组的XPATH和详情的XPATH, 从不同页面上提取代理的IP,端口号和区域的通用爬虫;
步骤:

1. 在base_spider.py文件中,定义一个BaseSpider类, 继承object
2. 提供三个类成员变量:
    urls: 代理IP网址的URL的列表
    group_xpath: 分组XPATH, 获取包含代理IP信息标签列表的XPATH
    detail_xpath: 组内XPATH, 获取代理IP详情的信息XPATH, 格式为: {'ip':'xx', 'port':'xx', 'area':'xx'}
3. 提供初始方法, 传入爬虫URL列表, 分组XPATH, 详情(组内)XPATH
4. 对外提供一个获取代理IP的方法
    4.1 遍历URL列表, 获取URL
    4.2 根据发送请求, 获取页面数据
    4.3 解析页面, 提取数据, 封装为Proxy对象
    4.4 返回Proxy对象列表
"""

# 1. 在base_spider.py文件中,定义一个BaseSpider类, 继承object
class BaseSpider(object):

    # 2. 提供三个类成员变量:
    # urls: 代理IP网址的URL的列表
    urls = []
    # group_xpath: 分组XPATH, 获取包含代理IP信息标签列表的XPATH
    group_xpath = ''
    # detail_xpath: 组内XPATH, 获取代理IP详情的信息XPATH, 格式为: {'ip':'xx', 'port':'xx', 'area':'xx'}
    detail_xpath = {}

    # 3. 提供初始方法, 传入爬虫URL列表, 分组XPATH, 详情(组内)XPATH
    def __init__(self, urls=[], group_xpath='', detail_xpath={}):

        if urls:
            self.urls = urls

        if group_xpath:
            self.group_xpath = group_xpath

        if detail_xpath:
            self.detail_xpath = detail_xpath

    def get_page_from_url(self, url):
        """根据URL 发送请求, 获取页面数据"""
        response = requests.get(url, headers=get_request_headers())
        print(url)
        print(response.status_code)
        return response.content

    def get_first_from_list(self, lis):
        # 如果列表中有元素就返回第一个, 否则就返回空串
        return lis[0] if len(lis) != 0 else ''

    def get_proxies_from_page(self, page):
        """解析页面, 提取数据, 封装为Proxy对象"""
        element = etree.HTML(page)
        # 获取包含代理IP信息的标签列表
        trs = element.xpath(self.group_xpath)
        # 遍历trs, 获取代理IP相关信息
        for tr in trs:
            ip = self.get_first_from_list(tr.xpath(self.detail_xpath['ip']))
            port = self.get_first_from_list(tr.xpath(self.detail_xpath['port']))
            area = self.get_first_from_list(tr.xpath(self.detail_xpath['area']))
            proxy = Proxy(ip, port, area=area)
            # print(proxy)
            # 使用yield返回提取到的数据
            yield proxy

    def get_proxies(self):
        # 4. 对外提供一个获取代理IP的方法
        # 4.1 遍历URL列表, 获取URL
        for url in self.urls:
            # print(url)
            # 4.2 根据发送请求, 获取页面数据
            page = self.get_page_from_url(url)
            # 4.3 解析页面, 提取数据, 封装为Proxy对象
            proxies = self.get_proxies_from_page(page)
            # 4.4 返回Proxy对象列表
            yield from proxies

if __name__ == '__main__':

    config = {
        'urls': ['http://www.ip3366.net/free/?stype=1&page={}'.format(i) for i in range(1, 4)],
        'group_xpath': '//*[@id="list"]/table/tbody/tr',
        'detail_xpath': {
            'ip':'./td[1]/text()',
            'port':'./td[2]/text()',
            'area':'./td[5]/text()'
        }
    }

    spider = BaseSpider(**config)
    for proxy in spider.get_proxies():
        print(proxy)

4 实现具体爬虫(proxy_spiders.py)

继承通用爬虫,实现多个具体爬虫,分别从各个免费代理ip网站抓取代理IP

4.1.实现西刺代理爬虫:?http://www.xicidaili.com/nn/1

4.2.实现ip3366代理爬虫:?http://www.ip3366.net/free/?stype=1&page=1

4.3.实现快代理爬虫:?https://www.kuaidaili.com/free/inha/1/

4.4.实现proxylistplus代理爬虫:?https://list.proxylistplus.com/Fresh-HTTP-Proxy-List-1

import time
import random
import requests
import re
import js2py


from core.proxy_spider.base_spider import BaseSpider
from utils.http import get_request_headers

"""
1. 实现西刺代理爬虫: http://www.xicidaili.com/nn/1
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""

class XiciSpider(BaseSpider):
    # 准备URL列表
    urls = ['https://www.xicidaili.com/nn/{}'.format(i) for i in range(1, 11)]
    # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="ip_list"]/tr[position()>1]'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[2]/text()',
        'port':'./td[3]/text()',
        'area':'./td[4]/a/text()'
    }

"""
2. 实现ip3366代理爬虫: http://www.ip3366.net/free/?stype=1&page=1
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""
class Ip3366Spider(BaseSpider):
    # 准备URL列表
    urls = ['http://www.ip3366.net/free/?stype={}&page={}'.format(i, j) for i in range(1, 4, 2) for j in range(1, 8)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="list"]/table/tbody/tr'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[1]/text()',
        'port':'./td[2]/text()',
        'area':'./td[5]/text()'
    }

"""
3. 实现快代理爬虫: https://www.kuaidaili.com/free/inha/1/
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""
class KaiSpider(BaseSpider):
    # 准备URL列表
    urls = ['https://www.kuaidaili.com/free/inha/{}/'.format(i) for i in range(1, 6)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="list"]/table/tbody/tr'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[1]/text()',
        'port':'./td[2]/text()',
        'area':'./td[5]/text()'
    }

    # 当我们两个页面访问时间间隔太短了, 就报错了; 这是一种反爬手段.
    def get_page_from_url(self, url):
        # 随机等待1,3s
        time.sleep(random.uniform(1, 3))
        # 调用父类的方法, 发送请求, 获取响应数据
        return super().get_page_from_url(url)

"""
4. 实现proxylistplus代理爬虫: https://list.proxylistplus.com/Fresh-HTTP-Proxy-List-1
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""

class ProxylistplusSpider(BaseSpider):
    # 准备URL列表
    urls = ['https://list.proxylistplus.com/Fresh-HTTP-Proxy-List-{}'.format(i) for i in range(1, 7)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="page"]/table[2]/tr[position()>2]'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[2]/text()',
        'port':'./td[3]/text()',
        'area':'./td[5]/text()'
    }

"""
5. 实现66ip爬虫: http://www.66ip.cn/1.html
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
    由于66ip网页进行js + cookie反爬, 需要重写父类的get_page_from_url方法
"""

class Ip66Spider(BaseSpider):
    # 准备URL列表
    urls = ['http://www.66ip.cn/{}.html'.format(i) for i in range(1, 11)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="main"]/div/div[1]/table/tr[position()>1]'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[1]/text()',
        'port':'./td[2]/text()',
        'area':'./td[3]/text()'
    }

    # 重写方法, 解决反爬问题
    def get_page_from_url(self, url):
        headers = get_request_headers()
        response = requests.get(url, headers=headers)
        if response.status_code == 521:
            # 生成cookie信息, 再携带cookie发送请求
            # 生成 `_ydclearance` cookie信息
            # 1. 确定 _ydclearance 是从哪里来的;
            # 观察发现: 这个cookie信息不使用通过服务器响应设置过来的; 那么他就是通过js生成.
            # 2. 第一次发送请求的页面中, 有一个生成这个cookie的js; 执行这段js, 生成我们需要的cookie
            # 这段js是经过加密处理后的js, 真正js在 "po" 中.
            # 提取 `jp(107)` 调用函数的方法, 以及函数
            result = re.findall('window.οnlοad=setTimeout\("(.+?)", 200\);\s*(.+?)\s*</script> ', response.content.decode('GBK'))
            # print(result)
            # 我希望执行js时候, 返回真正要执行的js
            # 把 `eval("qo=eval;qo(po);")` 替换为 return po
            func_str = result[0][1]
            func_str = func_str.replace('eval("qo=eval;qo(po);")', 'return po')
            # print(func_str)
            # 获取执行js的环境
            context = js2py.EvalJs()
            # 加载(执行) func_str
            context.execute(func_str)
            # 执行这个方法, 生成我们需要的js
            # code = gv(50)
            context.execute('code = {};'.format(result[0][0]))
            # 打印最终生成的代码
            # print(context.code)
            cookie_str = re.findall("document.cookie='(.+?); ", context.code)[0]
            # print(cookie_str)
            headers['Cookie'] = cookie_str
            response = requests.get(url, headers=headers)
            return response.content.decode('GBK')
        else:
            return response.content.decode('GBK')


if __name__ == '__main__':
    # spider = XiciSpider() #1
    # spider = Ip3366Spider()#2
    # spider = KaiSpider()#3
    # spider = ProxylistplusSpider()#4

    # spider = Ip66Spider()
    # for proxy in spider.get_proxies():
    #     print(proxy)

    ## 5
    # print(Ip3366Spider.urls)

    ## 6
    # # 测试: http://www.66ip.cn/1.html
    # url = 'http://www.66ip.cn/1.html'
    # headers = {
    #     'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
    #     # 'Cookie': '_ydclearance=35fd4248c8889feb58597e27-a31e-4f84-9edc-f1a22c16a949-1546164684;'
    # }
    # response = requests.get(url, headers=headers)
    # print(response.status_code)
    # text = response.content.decode('GBK')
    #
    # # 生成 `_ydclearance` cookie信息
    # # 1. 确定 _ydclearance 是从哪里来的;
    # # 观察发现: 这个cookie信息不使用通过服务器响应设置过来的; 那么他就是通过js生成.
    # # 2. 第一次发送请求的页面中, 有一个生成这个cookie的js; 执行这段js, 生成我们需要的cookie
    # # 这段js是经过加密处理后的js, 真正js在 "po" 中.
    # # 提取 `jp(107)` 调用函数的方法, 以及函数
    # result = re.findall('window.οnlοad=setTimeout\("(.+?)", 200\);\s*(.+?)\s*</script> ' ,text)
    # # print(result)
    # # 我希望执行js时候, 返回真正要执行的js
    # # 把 `eval("qo=eval;qo(po);")` 替换为 return po
    # func_str = result[0][1]
    # func_str = func_str.replace('eval("qo=eval;qo(po);")', 'return po')
    # # print(func_str)
    # # 获取执行js的环境
    # context = js2py.EvalJs()
    # # 加载(执行) func_str
    # context.execute(func_str)
    # # 执行这个方法, 生成我们需要的js
    # # code = gv(50)
    # context.execute('code = {};'.format(result[0][0]))
    # # 打印最终生成的代码
    # # print(context.code)
    # cookie_str = re.findall("document.cookie='(.+?); ", context.code)[0]
    # # print(cookie_str)
    # headers['Cookie'] = cookie_str
    # response = requests.get(url, headers=headers)
    # print(response.content.decode('GBK'))
    pass

4.5.实现66ip爬虫:http://www.66ip.cn/1.html

核心: 通过加密的js, 生成需要cookie信息
1.从响应页面中, 提取: 1. 执行生成真正js语句 2. 生成真正js的函数.
2.网页中, 是通过eval执行真正js, 加载页面; 而我们要获取真正的js; 我们就需要把eval语句, 替换为 return, 把真正js返回.
3.使用js2py,获取执行js的环境, 使用js执行环境 加载这个函数
4.使用这个执行环境, 执行调用调用, 生成真正js, 赋值给一个变量

从真正的js代码中, 提取我们需要cookie信息.

import time
import random
import requests
import re
import js2py


from core.proxy_spider.base_spider import BaseSpider
from utils.http import get_request_headers

"""
1. 实现西刺代理爬虫: http://www.xicidaili.com/nn/1
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""

class XiciSpider(BaseSpider):
    # 准备URL列表
    urls = ['https://www.xicidaili.com/nn/{}'.format(i) for i in range(1, 11)]
    # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="ip_list"]/tr[position()>1]'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[2]/text()',
        'port':'./td[3]/text()',
        'area':'./td[4]/a/text()'
    }

"""
2. 实现ip3366代理爬虫: http://www.ip3366.net/free/?stype=1&page=1
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""
class Ip3366Spider(BaseSpider):
    # 准备URL列表
    urls = ['http://www.ip3366.net/free/?stype={}&page={}'.format(i, j) for i in range(1, 4, 2) for j in range(1, 8)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="list"]/table/tbody/tr'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[1]/text()',
        'port':'./td[2]/text()',
        'area':'./td[5]/text()'
    }

"""
3. 实现快代理爬虫: https://www.kuaidaili.com/free/inha/1/
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""
class KaiSpider(BaseSpider):
    # 准备URL列表
    urls = ['https://www.kuaidaili.com/free/inha/{}/'.format(i) for i in range(1, 6)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="list"]/table/tbody/tr'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[1]/text()',
        'port':'./td[2]/text()',
        'area':'./td[5]/text()'
    }

    # 当我们两个页面访问时间间隔太短了, 就报错了; 这是一种反爬手段.
    def get_page_from_url(self, url):
        # 随机等待1,3s
        time.sleep(random.uniform(1, 3))
        # 调用父类的方法, 发送请求, 获取响应数据
        return super().get_page_from_url(url)

"""
4. 实现proxylistplus代理爬虫: https://list.proxylistplus.com/Fresh-HTTP-Proxy-List-1
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
"""

class ProxylistplusSpider(BaseSpider):
    # 准备URL列表
    urls = ['https://list.proxylistplus.com/Fresh-HTTP-Proxy-List-{}'.format(i) for i in range(1, 7)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="page"]/table[2]/tr[position()>2]'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[2]/text()',
        'port':'./td[3]/text()',
        'area':'./td[5]/text()'
    }

"""
5. 实现66ip爬虫: http://www.66ip.cn/1.html
    定义一个类,继承通用爬虫类(BasicSpider)
    提供urls, group_xpath 和 detail_xpath
    由于66ip网页进行js + cookie反爬, 需要重写父类的get_page_from_url方法
"""

class Ip66Spider(BaseSpider):
    # 准备URL列表
    urls = ['http://www.66ip.cn/{}.html'.format(i) for i in range(1, 11)]
    # # 分组的XPATH, 用于获取包含代理IP信息的标签列表
    group_xpath = '//*[@id="main"]/div/div[1]/table/tr[position()>1]'
    # 组内的XPATH, 用于提取 ip, port, area
    detail_xpath = {
        'ip':'./td[1]/text()',
        'port':'./td[2]/text()',
        'area':'./td[3]/text()'
    }

    # 重写方法, 解决反爬问题
    def get_page_from_url(self, url):
        headers = get_request_headers()
        response = requests.get(url, headers=headers)
        if response.status_code == 521:
            # 生成cookie信息, 再携带cookie发送请求
            # 生成 `_ydclearance` cookie信息
            # 1. 确定 _ydclearance 是从哪里来的;
            # 观察发现: 这个cookie信息不使用通过服务器响应设置过来的; 那么他就是通过js生成.
            # 2. 第一次发送请求的页面中, 有一个生成这个cookie的js; 执行这段js, 生成我们需要的cookie
            # 这段js是经过加密处理后的js, 真正js在 "po" 中.
            # 提取 `jp(107)` 调用函数的方法, 以及函数
            result = re.findall('window.οnlοad=setTimeout\("(.+?)", 200\);\s*(.+?)\s*</script> ', response.content.decode('GBK'))
            # print(result)
            # 我希望执行js时候, 返回真正要执行的js
            # 把 `eval("qo=eval;qo(po);")` 替换为 return po
            func_str = result[0][1]
            func_str = func_str.replace('eval("qo=eval;qo(po);")', 'return po')
            # print(func_str)
            # 获取执行js的环境
            context = js2py.EvalJs()
            # 加载(执行) func_str
            context.execute(func_str)
            # 执行这个方法, 生成我们需要的js
            # code = gv(50)
            context.execute('code = {};'.format(result[0][0]))
            # 打印最终生成的代码
            # print(context.code)
            cookie_str = re.findall("document.cookie='(.+?); ", context.code)[0]
            # print(cookie_str)
            headers['Cookie'] = cookie_str
            response = requests.get(url, headers=headers)
            return response.content.decode('GBK')
        else:
            return response.content.decode('GBK')


if __name__ == '__main__':
    # spider = XiciSpider() #1
    # spider = Ip3366Spider()#2
    # spider = KaiSpider()#3
    # spider = ProxylistplusSpider()#4

    # spider = Ip66Spider()
    # for proxy in spider.get_proxies():
    #     print(proxy)

    ## 5
    # print(Ip3366Spider.urls)

    ## 6
    # # 测试: http://www.66ip.cn/1.html
    # url = 'http://www.66ip.cn/1.html'
    # headers = {
    #     'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36',
    #     # 'Cookie': '_ydclearance=35fd4248c8889feb58597e27-a31e-4f84-9edc-f1a22c16a949-1546164684;'
    # }
    # response = requests.get(url, headers=headers)
    # print(response.status_code)
    # text = response.content.decode('GBK')
    #
    # # 生成 `_ydclearance` cookie信息
    # # 1. 确定 _ydclearance 是从哪里来的;
    # # 观察发现: 这个cookie信息不使用通过服务器响应设置过来的; 那么他就是通过js生成.
    # # 2. 第一次发送请求的页面中, 有一个生成这个cookie的js; 执行这段js, 生成我们需要的cookie
    # # 这段js是经过加密处理后的js, 真正js在 "po" 中.
    # # 提取 `jp(107)` 调用函数的方法, 以及函数
    # result = re.findall('window.οnlοad=setTimeout\("(.+?)", 200\);\s*(.+?)\s*</script> ' ,text)
    # # print(result)
    # # 我希望执行js时候, 返回真正要执行的js
    # # 把 `eval("qo=eval;qo(po);")` 替换为 return po
    # func_str = result[0][1]
    # func_str = func_str.replace('eval("qo=eval;qo(po);")', 'return po')
    # # print(func_str)
    # # 获取执行js的环境
    # context = js2py.EvalJs()
    # # 加载(执行) func_str
    # context.execute(func_str)
    # # 执行这个方法, 生成我们需要的js
    # # code = gv(50)
    # context.execute('code = {};'.format(result[0][0]))
    # # 打印最终生成的代码
    # # print(context.code)
    # cookie_str = re.findall("document.cookie='(.+?); ", context.code)[0]
    # # print(cookie_str)
    # headers['Cookie'] = cookie_str
    # response = requests.get(url, headers=headers)
    # print(response.content.decode('GBK'))
    pass

5.实现运行爬虫模块(run_spiders.py)

5.1.提供一个运行爬虫的run方法, 作为运行爬虫的入口, 实现核心的处理逻辑

在run_spider.py中, 创建RunSpider类

修改 setting.py 增加 代理IP爬虫的配置信息

根据配置文件信息, 获取爬虫对象列表.

遍历爬虫对象列表, 获取爬虫对象, 遍历爬虫对象的get_proxies方法, 获取代理IP

检测代理IP(代理IP检测模块)

如果可用,写入数据库(数据库模块)

处理异常, 防止一个爬虫内部出错了, 影响其他的爬虫.

# 打猴子补丁
from gevent import monkey
monkey.patch_all()
# 导入协程池
from gevent.pool import Pool
import importlib
import schedule
import time

from settings import PROXIES_SPIDERS
from core.proxy_validate.httpbin_validator import check_proxy
from core.db.mongo_pool import MongoPool
from utils.log import logger
from settings import RUN_SPIDERS_INTERVAL

"""
8.5 实现运行爬虫模块
目标: 根据配置文件信息, 加载爬虫, 抓取代理IP, 进行校验, 如果可用, 写入到数据库中
思路:

1. 在run_spider.py中, 创建RunSpider类
2. 提供一个运行爬虫的run方法, 作为运行爬虫的入口, 实现核心的处理逻辑
    2.1 根据配置文件信息, 获取爬虫对象列表.
    2.2 遍历爬虫对象列表, 获取爬虫对象, 遍历爬虫对象的get_proxies方法, 获取代理IP
    2.3 检测代理IP(代理IP检测模块)
    2.4 如果可用,写入数据库(数据库模块)
    2.5 处理异常, 防止一个爬虫内部出错了, 影响其他的爬虫.
3. 使用异步来执行每一个爬虫任务, 以提高抓取代理IP效率
    3.1 在init方法中创建协程池对象
    3.2 把处理一个代理爬虫的代码抽到一个方法
    3.3 使用异步执行这个方法
    3.4 调用协程的join方法, 让当前线程等待 协程 任务的完成.
4. 使用schedule模块, 实现每隔一定的时间, 执行一次爬取任务
    4.1 定义一个start的类方法
    4.2 创建当前类的对象, 调用run方法
    4.3 使用schedule模块, 每隔一定的时间, 执行当前对象的run方法
"""

class RunSpider(object):

    def __init__(self):
        # 创建MongoPool对象
        self.mongo_pool = MongoPool()
        # 3.1 在init方法中创建协程池对象
        self.coroutine_pool = Pool()

    def get_spider_from_settings(self):
        """根据配置文件信息, 获取爬虫对象列表."""
        # 遍历配置文件中爬虫信息, 获取每个爬虫全类名
        for full_class_name in PROXIES_SPIDERS:
            # core.proxy_spider.proxy_spiders.Ip66Spider
            # 获取模块名 和 类名
            module_name, class_name = full_class_name.rsplit('.', maxsplit=1)
            # 根据模块名, 导入模块
            module = importlib.import_module(module_name)
            # 根据类名, 从模块中, 获取类
            cls = getattr(module, class_name)
            # 创建爬虫对象
            spider = cls()
            # print(spider)
            yield spider


    def run(self):
        # 2.1 根据配置文件信息, 获取爬虫对象列表.
        spiders = self.get_spider_from_settings()
        # 2.2 遍历爬虫对象列表, 获取爬虫对象, 遍历爬虫对象的get_proxies方法, 获取代理IP
        for spider in spiders:
            #  2.5 处理异常, 防止一个爬虫内部出错了, 影响其他的爬虫.
            # 3.3 使用异步执行这个方法
            # self.__execute_one_spider_task(spider)
            self.coroutine_pool.apply_async(self.__execute_one_spider_task,args=(spider, ))

        # 3.4 调用协程的join方法, 让当前线程等待 协程 任务的完成.
        self.coroutine_pool.join()

    def __execute_one_spider_task(self, spider):
        # 3.2 把处理一个代理爬虫的代码抽到一个方法
        # 用于处理一个爬虫任务的.
        try:
            # 遍历爬虫对象的get_proxies方法, 获取代理I
            for proxy in spider.get_proxies():
                # print(proxy)
                # 2.3 检测代理IP(代理IP检测模块)
                proxy = check_proxy(proxy)
                # 2.4 如果可用,写入数据库(数据库模块)
                # 如果speed不为-1, 就说明可用
                if proxy.speed != -1:
                    # 写入数据库(数据库模块)
                    self.mongo_pool.insert_one(proxy)
        except Exception as ex:
            logger.exception(ex)

    @classmethod
    def start(cls):
        # 4. 使用schedule模块, 实现每隔一定的时间, 执行一次爬取任务
        # 4.1 定义一个start的类方法
        # 4.2 创建当前类的对象, 调用run方法
        rs = RunSpider()
        rs.run()
        # 4.3 使用schedule模块, 每隔一定的时间, 执行当前对象的run方法
        # 4.3.1 修改配置文件, 增加爬虫运行时间间隔的配置, 单位为小时
        schedule.every(RUN_SPIDERS_INTERVAL).hours.do(rs.run)
        while True:
            schedule.run_pending()
            time.sleep(1)

if __name__ == '__main__':
    # rs = RunSpider()
    # rs.run()
    RunSpider.start()

    # 测试schedule
    # def task():
    #     print('呵呵')
    #
    # schedule.every(10).seconds.do(task)
    # while True:
    #     schedule.run_pending()
    #     time.sleep(1)

5.2.使用异步来执行每一个爬虫任务, 以提高抓取代理IP效率

在init方法中创建协程池对象

把处理一个代理爬虫的代码抽到一个方法\

使用异步执行这个方法

在循环外, 调用协程的join方法

# 打猴子补丁
from gevent import monkey
monkey.patch_all()
# 导入协程池
from gevent.pool import Pool
class RunSpider(object):

   def __init__(self):
    # 创建MongoPool对象
    self.mongo_pool = MongoPool()
    # 3.1 在init方法中创建协程池对象
    self.coroutine_pool = Pool()


def run(self):
    # 2.1 根据配置文件信息, 获取爬虫对象列表.
    spiders = self.get_spider_from_settings()
    # 2.2 遍历爬虫对象列表, 获取爬虫对象, 遍历爬虫对象的get_proxies方法, 获取代理IP
    for spider in spiders:
        #  2.5 处理异常, 防止一个爬虫内部出错了, 影响其他的爬虫.
        # 3.3 使用异步执行这个方法
        # self.__execute_one_spider_task(spider)
        self.coroutine_pool.apply_async(self.__execute_one_spider_task,args=(spider, ))

    # 3.4 调用协程的join方法, 让当前线程等待 协程 任务的完成.
    self.coroutine_pool.join()


5.3.使用schedule模块, 实现每隔一定的时间, 执行一次爬取任务

import schedule
def task():
    print('呵呵')

schedule.every(10).seconds.do(task)
while True:
    schedule.run_pending()
    time.sleep(1)

 类似资料: