def test_ips(self, ips_list): """ 测试爬取到的 IP 是否可用 """ timeout = urllib3.Timeout(connect=3, read=6) for ip in ips_list: # 将暂时可用的IP保存至 ips_ok.txt file = open(self.config["abs_dir"]+"/ips_ok.txt", "at") try: manager = urllib3.ProxyManager(ip, cert_reqs="CERT_REQUIRED", ca_certs=certifi.where(), timeout=timeout,) rep = manager.request("GET", self.config["test_domain"]) # 如果 response headers 的状态码为 200,则说明此 IP 可用 if rep.status == 200: self.ip_ok_lib.append(ip) file.write(ip+"\n") print("Test success and saved ip: {0}".format(ip)) # retry 次数过多则抛出此异常 except urllib3.exceptions.MaxRetryError: print("{0} Test failed ".format(ip)) # 处理其他未知异常 except Exception as e: print("Problems :{0}".format(e)) finally: file.close() toolBox.print_format("Test finished {0}") toolBox.count_ip(self.config["abs_dir"])
def connect_test(self): """ 测试要爬取的目标网站 此时 是否可用 """ manager = self.manager # 暂时支持以下三个网站,后续更新添加 # key 为目标网站在IPReaper类中的方法名称,以供后续 eval() base_ip_com = {"get_xici_ips": "http://www.xicidaili.com/", "get_66_ips": "http://www.66ip.cn/", "get_kuai_ips": "http://www.kuaidaili.com/"} # 存储 此时 可爬去的 IP 网站 self.ok_com = [] toolBox.print_format("Connection test") for name, url in base_ip_com.items(): # 对同一网站重复 3 次请求(实际上 urllib3 中的 request 已经有 retry 次数) # 以后会对此问题进行优化 for n in range(3): print("{0}th url:{1}".format(n+1, url)) try: rep = manager.request("GET", url) if rep.status == 200: # 状态码为 200,则测试成功,此网站可用 self.ok_com.append((name, url)) print("success") break except urllib3.exceptions.MaxRetryError: print("fail") toolBox.print_format("Test finished")
def generate_ips(self): """ 从文件中读取 ip ,以生成器的方式返回可用 ip """ path = self.config["abs_dir"] + "/ips_ok.txt" with open(path, "rt") as ips_file: for ip in ips_file: yield toolBox.strip(ip)
def run_reaper(self): """ 运行 reaper 爬虫 """ # 存储添加了可爬取网站对应方法的协程 func_list = [] for (func_name, domain_url) in self.ok_com: # 通过 eval() 将字符串的方法名 转换为 方法对象 func = eval("self."+func_name) # 使用协程 func_list.append(gevent.spawn(func)) print("function {0} is ready: {1}".format(func_name, domain_url)) toolBox.print_format("IPReaper running") print("IPReaper is getting proxy IPs......") print("......") print("...") print(".") # 将所有协程 join 并 运行 gevent.joinall(func_list)
def get_ips_from_file(self): """ 从 ips_ok.txt 中读取可用 ip :return: 存储可用 ip 的列表 """ path = self.config["abs_dir"] + "/ips_ok.txt" ips_ok = [] with open(path, "rt") as ips_file: for ip in ips_file: ip = toolBox.strip(ip) ips_ok.append(ip) return ips_ok
def load_config(): """ 读取加载文件:同级目录下的 config.txt 并赋值给类属性 config,以供其他方法共享、调用 """ config = {} # 读取配置文件中的参数的类型是字符串,但以下参数值得类型不应该是字符串 # 所以构建此列表,以供后续处理其中对应的值 need_eval = ["proxy", "connect_timeout", "read_timeout", "frequency"] config_file = os.path.abspath("config.txt") with open(config_file, "rt") as config_file: for item in config_file: item = toolBox.strip(item) key, value = item.split("=") if value == "": msg = "'" + key + "'" # 如果只获取到了 key 而没有获取到对应 value,则抛出解析错误异常 raise AnalysisError(msg) # 将字符串值转换为相应类型 config[key] = eval(value) if key in need_eval else value toolBox.print_format("Loading config") # 设置各可配置项的默认值 config.setdefault("proxy", False) config.setdefault("dir_name", "ips_lib/") config.setdefault("abs_dir", os.path.abspath(config["dir_name"])) # 暂时只支持 html.parser,后续加入 lxml 等解析器 config.setdefault("parser", "html.parser") config.setdefault("connect_timeout", 3) config.setdefault("read_timeout", 6) config.setdefault("frequency", 6) config.setdefault("test_domain", "https://book.douban.com/") toolBox.print_dict(config) return config
def get_66_ips(self): """ 获取 66网站 的 IP """ base_url = ["http://www.66ip.cn/nmtq.php?proxytype=0", # http "http://www.66ip.cn/nmtq.php?proxytype=1"] # https for n in range(10): url = random.choice(base_url) html = self.get_html(url, "gbk") tag = list(islice(html, 10, 49))[0::2] pre = "http://" if url.endswith("0") else "https://" for t in tag: if t is None: continue ip_path = pre + toolBox.strip(t) self._ip_cache_lib.add(ip_path) time.sleep(self.config["frequency"])