代理池Demo版,可供学习,不能用于生产

  PS:本人第一次发随笔,文笔不好,请多多包涵。项目代买 github:https://github.com/liangxu789/ProxyPoolDemo

  什么是代理池呢?代理池就是一个装着很多代理的容器(好像说的是废话,哈哈哈)。总之就是一个容器,那好,我就在想用什么来当这个容器呢,思来想去,我觉的用Redis,为什么要用Redis呢?大家可以百度一下。因为工作用的语言是java,c#。所以我觉得我这个代理池就用python来写吧(因为想练习练习,语法有点快忘记了)。

  前后用了一天,写出了个小小的demo。

  下面这个是文件目录:

  

 

  我就不全部介绍了,主要就介绍两个模块:获取代理 和 检查代理(其实也就这两个模块)

  1 获取代理模块

  一共找到了4个网站,他们是有免费代理的。

  分别是:http://www.66ip.cn/  http://www.xsdaili.com/dayProxy/ip/   https://www.xicidaili.com   http://www.nimadaili.com

  有了免费代理的网站。就需要将把这些网站上的代理给弄下来。怎么弄呢?就需要爬虫了。我用的是requets + pyquery

  具体的代码在下面(就展示第一个吧):

  

from pyquery import PyQuery as pq
import time, json, requests, multiprocessing
import log.logModel as lm
import redisitem.RedisOperation as ro
import tools.randomCode as rc
import tools.GetProxyIP as gip

# 代理网页主页地址
Proxy_url = "http://www.66ip.cn/"

# 浏览器信息
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/77.0.3865.90 Safari/537.36 '
}


#获取网页内容
def get_page_content(url, proxy):
    try:
        if proxy:
            r = requests.get(url, headers=headers, timeout=10, proxies=proxy)
        else:
            r = requests.get(url, headers=headers, timeout=10)
        if r.status_code == 200:
            lm.log_info("获取" + url + "的页面数据成功")
            return r.text
        else:
            lm.log_warning("获取" + url + "的页面数据失败 正在换代理获取重新获取。。。")
            proxy_list = {
                "http": gip.GetProxyIP(),
            }
            get_page_content(url, proxy_list)
    except:
        lm.log_error(url + "链接错误 取消此次链接")
        get_page_content(url, None)


#获取页码信息
def parse_page_num(url):
    doc = pq(get_page_content(url, None))
    return int(int(doc(".style7 span").text()) / 7)


#循环获取页面信息
def get_html(interval):
    page_num = parse_page_num(Proxy_url)
    for i in range(page_num):
        try:
            new_url = Proxy_url + str(i + 1) + ".html"
            lm.log_info("正在获取" + new_url + "的信息")
            html = get_page_content(new_url, None)
            parse_html(html)
            time.sleep(interval)
        except Exception as e:
            lm.log_error(e.args[0])
            continue

#获取代理,将其存到Redis中
def parse_html(html):
    doc = pq(html.encode('iso-8859-1').decode('gbk'))
    for i in doc("#main table tr"):
        i_html = pq(i)
        result_dic = {"ip": i_html("td").eq(0).text(), "port": i_html("td").eq(1).text()}
        if result_dic["ip"] != "ip":
            result = json.dumps(result_dic)
            ro.setDic(rc.getRandomCode(), result)
            lm.log_info(str(result_dic) + "已经存到Redis中")


def start_up():
    p = multiprocessing.Process(target=get_html, args=(3,))
    p.start()

  上面就是代码,其实很简单,一下子就能看懂了,主要的逻辑就是:先获取页码(parse_page_num) ----> 然后通过页码循环获取每页的信息 ----> 通过pyquery获取代理信息  ---->  将其存到Redis中。

  其中需要注意的一点是,有些代理网站会监测是否是爬虫,会封ip。所以我在获取页面内容的哪里加上了使用代理,如果他封了ip,我就使用代理获取,继续封,我就换代理.....

  获取代理就很容易,之前不是在Redis存了代理,随机取出一个,就先用,发现不能用,就再随机换一个代理。如此循环往复。

  获取代理的代码如下:

import json
import random
import redisitem.RedisOperation as ro


def GetProxyIP():
    # 获取Redis对象
    r = ro.getRedisObj()
    # 随机取出一个代理
    i = random.sample(ro.getDicKeys(), 1)[0]
    # 将json字符串转为字典
    ip_info = json.loads(r.hget("proxy_ip", i))
    # 返回代理
    return "http://" + ip_info["ip"] + ":" + ip_info["post"]

  2 代理检查模块

  代理检查其实就两步,一个是获取代理,一个是检查代理。

  下面是代码:

  

import win32api, win32con, telnetlib, random, json
from concurrent.futures.thread import ThreadPoolExecutor
import redisitem.RedisOperation as ro
import log.logModel as lm

# 浏览器信息
head_data = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                  'Chrome/77.0.3865.90 Safari/537.36 ',
}


# 获取代理ip
def GetProxyIP():
    # 获取redis对象
    r = ro.getRedisObj()
    # 随机获取一条数据
    i = random.sample(ro.getDicKeys(), 1)[0]
    if i:
        # 得到数据的value值,为字典类型的json字符串,将其转为字典类型
        ip_info = json.loads(r.hget("proxy_ip", i))
        try:
            # 测试代理是否可用
            telnetlib.Telnet(ip_info["ip"], ip_info["post"], timeout=2)
            # 记录日志
            lm.log_info(ip_info["ip"] + ":" + ip_info["post"] + "监测为可用代理")
            # 将可用代理写入桌面txt文件
            with open(get_desktop() + "/可用代理.txt", 'a', encoding='utf-8') as f:
                f.write(ip_info["ip"] + " " + ip_info["post"] + "\r\n")

        except Exception as e:
            # 代理不可用,在redis中将其删除
            r.delete(i)
            lm.log_info(ip_info["ip"] + ":" + ip_info["post"] + "监测为不可用代理,已删除")


# 获取桌面路径
def get_desktop():
    # 获取注册表值
    key = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER,
                              r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders', 0, win32con.KEY_READ)
    # 返回桌面路径
    return win32api.RegQueryValueEx(key, 'Desktop')[0]


# 开始函数
def startup():
    while True:
        # 开启线程池
        pool = ThreadPoolExecutor(max_workers=20)
        for i in range(20):
            # 多线程运行检查函数
            pool.submit(GetProxyIP)
        # 回收线程池
        pool.shutdown()

 具体流程就是:先随机获取一个代理 ----> 然后检查代理是否可用,如果可用就存到一个桌面名叫“可用代理”的txt文件里面,如果不可用,就从redis中删除此代理。开启线程池进行获取。

 这样一个代理池的小小demo就完成了,虽然功能和逻辑还需要斟酌和完善(等以后闲下来的时候好好改改)。

 其他模块代码如下:

 日志模块

 

import logging

# 创建一个logging对象
logger = logging.getLogger()
# 创建一个屏幕对象
sh = logging.StreamHandler()
# 配置显示格式  可以设置两个配置格式  分别绑定到文件和屏幕上
formatter = logging.Formatter('%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s')
sh.setFormatter(formatter)

logger.addHandler(sh)
logger.setLevel(10)  # 总开关

# requests模块在运行过程中无法记录日志
logging.getLogger().setLevel(logging.INFO)
logging.getLogger("requests").setLevel(logging.WARNING)

# debug日志
def log_debug(message):
    logging.debug(message)

# info日志
def log_info(message):
    logging.info(message)

#warning日志
def log_warning(message):
    logging.warning(message)

#error日志
def log_error(message):
    logging.error(message)

  Redis操作

import redis
import redis_config as rc

pool = redis.ConnectionPool(host=rc.HOST, port=rc.POST)

r = redis.Redis(connection_pool=pool)


def setDic(key, value):
    r.hset("proxy_ip", key, value)


def getRedisObj():
    return r


def getDicKeys():
    return r.hkeys("proxy_ip")

 

希望大家能给我这个博客小白一点意见,我会继续努力的!加油!!

 


posted @ 2020-03-24 13:18  梁三  阅读(261)  评论(0)    收藏  举报