分析反(反爬虫) 用不完的遍布世界的毫秒级代理IP

目标网站:

http://www.goubanjia.com/

说实话,爬了什么多ip网站,这个网站的ip可用率是非常高的

但同时反爬虫机制也是稍微高端一点的

如果说用同一个IP进行频繁方位该网站会被Ban的

爬取目标

这些ip看似简单 就是一些数字

可能你认为直接爬取页面进行,re解析就可以

但是re完全行不通

我们来分析一下他的源码

但是你仔细分析分析一下你就会发现,卧槽 什么玩意 完全没有任何规律

不管是拆分也好长短也好,都是没有任何规律的

每一条数据由一个tr组成,tr的class分3种

每个tr里有8个td,td里面又分别记录着不同的div、span、p、a标记

只看一个就下定论的话,那就太没有说服力了

我们多拿几个源码分析一下

你会发现:排版没有任何规律可言

标签任意分布、

ip随机裁开、

通标签不同属性、

空标签、

每个ip的tr内的td的内容长短各不相同

如果再进一步分析

去掉空标签,然后将每个div、span、p标签进行解析后

出来的数字、原点,将重复的去掉,然后在拼接回去。你会发现 竟然对了

欧耶 可以了!可能会有人说这还不简单吗,直接爬取网页,然后在进行re解析

将所有的数字解析

先来他一套正则表达式: .*?(\d*\\.*\d*).*?

将所有匹配结果去重、去空、然后在strip回去,大功告成

是的不错,这样确实可以匹配成功,但是成功率不超过百分之30

继续分析

我觉得现在不是怎么去分析源码的问题了

应该是去分析人,去分析这个处理模板的人

比如说他会用什么样的方式来防御你,也就是换位思考

如果你是开发这个网站的人你会怎么样去反爬虫,处理模板时

即使你处理的再好也不要忘了,你最终还是要显示在网页上,最后还是会让用户去看的

不管你用多么高级算法去处理,最终还是要回到原点

那么我们就从原点出发,以作者的身份去分析

回到源码中,你就会发现原来一切是这么的简单

就好比之去重复的时候,你会发现如果出现重复数字,并且是不同标签

不管是re解析也好,xpath也好,还是Beautiful Soup,返回的list 都是每个标签的的内容占一个元素

如果返回字符串,拆分后 则无法进行去重

即使去重也是不合理的去重

就拿这个 1.10.186.228:61463 这个ip来说,当你去重的时候你会发现

不仅将重复的去掉了,但是 正确的也去掉了,因为有相邻的两个div的内容都是2

并且两个都是合法的,但是在list的他是两个元素,一旦去重 那么这个ip就废掉了

多分析几个,将重复无用内容拿出来

你会发现 这些内容不管以什么样的随机排列,都是这三个元素不会变,那文章肯定就出在元素上

那么我们就对这些重复内容进行分析,到底哪些是真正重复、哪些是迷惑的

将所有的标签统计处理,每一个 所有的p元素内容都出现过

将所有的p元素过滤掉然后再进行拼接和原ip一样

这样就达到了100%的匹配率

接下来就是解析了

用xpath进行匹配,不仅不用去重而且通过节点的方式进行

进行匹配更准确



        # xpath解析响应页面
        lx = etree.HTML(html)
        L1 = lx.xpath('//tbody//tr//td[contains(@class, "ip")]//span|//tbody//tr//td[contains(@class, "ip")]//div')
        L2 = lx.xpath('//tbody//tr//td')
        L3 = lx.xpath('//tbody//tr//a')
        # 提取未解析ip
        agency_list_all = self.parse_element(L1)
        # 提取未解析详情
        info_list_all = self.parse_element(L2)
        # 提取未解析协议
        deal_list_all = self.parse_element(L3)
        self.parse_all(agency_list_all, info_list_all, deal_list_all)

xpath解析出来的列表中 存储了所有的分解ip **

有时7、8、9、12、13元素 组成一个ip

但是端口是默认80端口,或者全是4位及多位的端口

由于ip的点分十进制,最高255的性质,所以不可能出现4位的主机或服务器

利用端口进行分割,是80的或者是超过4位的都是端口

将前面的元素进行拼接得到的就是ip了,虽然有可能出现80,但是很少 我没遇到过

爬取成功率是99.99%



        agency_list = []
        info_list = []
        deal_list = []
        for _ in range(20):
            for ip in agency_list_all:
                if ip == "80" or len(ip) >= 4:
                    agency = agency_list_all[:agency_list_all.index(ip) + 1]
                    agency.insert(-1, ":")
                    agency = "".join(agency)
                    agency_list.append(agency)
                    del agency_list_all[:agency_list_all.index(ip) + 1]
                    break

别看这个网站只有20条ip,但是每一次刷新网页都会刷新ip

也就是刷新一次,会刷新20条免费ip,多爬几次ip就有了

你以为这样就完了吗 不不不 当你爬的时候你就会发现

我去什么鬼明明都匹配对了,但是 不能用是什么情况

当你拿到ip的时候你会发现,你用浏览器刷新拿到的完全不一样

浏览器刷新的可以用,爬到就不能用,难道还有更高深的反爬虫武功?

表面上看不出,实际上是一个他是一个js加载的动态页面

又不像其他的动态页面,他没有后续加载,是网页刷新后1秒内进行js加载

所以爬到的网页是没有被js过的源码,即使解析对了 但是得到的ip也是无效的

所以需要用selenium.webdirver 的方式进行抓取js后的页面

完整代码



import random
import time
import requests
import json
import linecache
import pymysql
import pymongo
import warnings
from selenium import webdriver
from lxml import etree


class MyUserAndIP(object):

    def __init__(self):
        self.base_url = "http://www.goubanjia.com/"
        self.proxies = self.rand_ip()
        self.headers = self.rand_user()
        # Mongo
        self.connect = pymongo.MongoClient("localhost", 27017)
        self.db = self.connect.Myip
        self.My_set = self.db.carwl_ip
        # MySQL
        self.DB = pymysql.connect("localhost", "root", "ParisPython", charset="UTF8Mb4")
        self.cursor = self.DB.cursor()
        warnings.filterwarnings("ignore")
        try:
            self.cursor.execute('create database if not exists Myip;')
            self.cursor.execute('use Myip;')
            self.cursor.execute('create table if not exists carwl_ip(id int primary key auto_increment,ip varchar(80))charset=utf8;')
        except Warning:
            pass

        self.ip_list = []

    # 爬取ip信息   1
    def carwl_ip(self):

        headers = {
            "Host": "www.goubanjia.com",
            "Connection": "keep-alive",
            "Cache-Control": "max-age=0",
            "Upgrade-Insecure-Requests": "1",
            "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36",
            "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
            "Accept-Encoding": "gzip, deflate",
            "Accept-Language": "zh-CN,zh;q=0.9",
            "Cookie": "UM_distinctid=166ae0f9a7ed2b-0091945b9ddbf5-1f396652-1aeaa0-166ae0f9a7f163; JSESSIONID=B79E1850D3F8527268FA46346C931912; CNZZDATA1253707717=64889890-1540704336-%7C1540709760"
        }
        print("正在分析收集代理IP信息~")
        # 响应对象
        opt = webdriver.ChromeOptions()
        opt.set_headless()
        driver = webdriver.Chrome(options=opt)
        driver.get(self.base_url)
        time.sleep(1)
        driver.execute_script('''
        window.scrollTo(0, document.body.scrollHeight)
        ''')
        time.sleep(5)
        html = driver.page_source
        driver.quit()
        # 爬取的HTML  和  网页的源码不相同

        # xpath解析响应页面
        lx = etree.HTML(html)
        L1 = lx.xpath('//tbody//tr//td[contains(@class, "ip")]//span|//tbody//tr//td[contains(@class, "ip")]//div')
        L2 = lx.xpath('//tbody//tr//td')
        L3 = lx.xpath('//tbody//tr//a')
        # 提取未解析ip
        agency_list_all = self.parse_element(L1)
        # 提取未解析详情
        info_list_all = self.parse_element(L2)
        # 提取未解析协议
        deal_list_all = self.parse_element(L3)
        self.parse_all(agency_list_all, info_list_all, deal_list_all)

    # 解析节点对象   2
    def parse_element(self, lists):
        lis = []
        for i in lists:
            if i.text:
                lis.append(str(i.text).strip())
        del lists
        return lis

    # 解析ip 详情  协议  3
    def parse_all(self, agency_list_all, info_list_all, deal_list_all):
        '''
        :param agency_list_all:
            匹配后 解析前 的代理ip端口
        :param info_list_all:
            匹配后 解析前 的详细信息
        :param deal_list_all:
            匹配后 解析前 的协议等信息
        :return:
            None
        '''
        agency_list = []
        info_list = []
        deal_list = []
        for _ in range(20):
            for ip in agency_list_all:
                if ip == "80" or len(ip) >= 4:
                    agency = agency_list_all[:agency_list_all.index(ip) + 1]
                    agency.insert(-1, ":")
                    agency = "".join(agency)
                    agency_list.append(agency)
                    del agency_list_all[:agency_list_all.index(ip) + 1]
                    break

            # 解析代理的详细信息
            x = info_list_all[1:4]
            tem = deal_list_all[0:4]
            ip = agency_list[_]
            ip = ip.split(":")
            info = """
                恭喜您~
            IP: %s   
            端口: %s

            代理地区: %s-%s    代理身份: %s
            协议类型: %s
            目前响应速度: %s  
            是否可用验证: %s  
            已经稳定使用: %s
            详细信息:
            http://www.ip138.com/ips138.asp?ip=%s&action=2
            """ % (ip[0], ip[1], tem[2], tem[3], tem[0], tem[1], x[0], x[1], x[2], ip[0])
            info_list.append(info)
            # print(info)
            del info_list_all[:4]

            # 解析代理ip的协议
            deal_list.append(tem[1])
            del deal_list_all[:6]

        del deal_list_all
        del agency_list_all
        del info_list_all

        self.inspect_request(agency_list, info_list, deal_list)


    # 检测ip 4
    def inspect_request(self, agency_list, info_list, deal_list):
        '''
        检测爬取代理ip的可用性 正确性
        :param agency_list:
            代理ip列表 每个元素格式:
                120.55.49.41:8498 | IP:d端口
        :param info_list:
            代理详细信息列表 元素格式:
                详细信息字符串
        :param deal_list:
            代理IP协议类型
        :return
            None
        '''
        is_ip = []
        for ip in agency_list:
            idx = agency_list.index(ip)
            # {'https': 'https://120.55.49.41:8498'}
            dic = {
                deal_list[idx]: deal_list[idx] + "://" + ip
            }
            try:
                response = requests.get("http://www.baidu.com/",
                                        proxies=dic,
                                        headers={"User-Agent": "Mozilla/5.0"},
                                        timeout=1)
                if response.status_code == 200:
                    in_ip = ip.split(":")
                    if in_ip[0] not in is_ip:
                        is_ip.append(in_ip[0])
                        self.ip_list.append(dic)
                        print(info_list[idx])
            except Exception:
                continue
    # on
    def update_ip(self):
        '''
        将所有可用IP写入到本地文件
        若想重新更新随机使用的代理ip请调用此方法
        每次调用将随机爬取ip 并写入文件 方便下次读取
        '''
        try:
            for i in range(20):
                self.carwl_ip()
            cmd = input('''
            **********是否永久保存**********
                默认:临时保存为ip.ini文件
             每次更新ip.ini文件随之清空并更新
                1.MongoDB     2.MySQL
                  3.保存为.txt 文件
                 (其他任意键代表默认)
            *****************************
                     请选择:''')
            cmd = cmd.strip()
            if cmd == "1":
                for ip in self.ip_list:
                    dic = {"ip": json.dumps(ip)}
                    self.My_set.insert(dic)
            elif cmd == "2":
                for ip in self.ip_list:
                    self.carwl_ip('insert into carwl_ip(ip) values(%s);' % (json.dumps(ip)))
                    self.DB.commit()
            elif cmd == "3":
                cmd = input("请输入文件名: ").strip()
                if cmd[-4:] == ".txt":
                    cmd = cmd
                else:
                    cmd += ".txt"
                with open(cmd, "a", encoding="utf-8") as f:
                    for ip in self.ip_list:
                        f.write(json.dumps(ip) + "\n")
            else:
                with open("ip.ini", "w", encoding="utf-8") as f:
                    for ip in self.ip_list:
                        f.write(json.dumps(ip) + "\n")
        except IndexError:
            self.DB.close()
            self.cursor.close()
        finally:
            # self.DB.close()
            self.cursor.close()

    # on
    def rand_ip(self):
        '''
        每次随机读取ip.ini配置文件的一行内容 相当于每次随机换一次
        爬取到的随机ip
        :return:
            返回一个字典格式如下  并且 可以直接放在requests中
            作为proxies的参数
                {'https': 'https://87.11.208.125:8587'}
        '''
        for _ in range(1, 20):
            a = random.randint(0, 223)
            theline = linecache.getline(r"ip.ini", a)
            if not theline:
                continue
            # print(type(json.loads(theline)))
            return json.loads(theline)
    # on
    def rand_user(self):
        headers_list = [
            {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60"},
            {"User-Agent": "Opera/8.0 (Windows NT 5.1; U; en)"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 5.1; U; en; rv:1.8.1) Gecko/20061208 Firefox/2.0.0 Opera 9.50"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:34.0) Gecko/20100101 Firefox/34.0"},
            {"User-Agent": "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/534.57.2 (KHTML, like Gecko) Version/5.1.7 Safari/534.57.2"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36"},
            {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11"},
            {"User-Agent": "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 "},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER"},
            {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0"},
            {"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Maxthon/4.4.3.4000 Chrome/30.0.1599.101 Safari/537.36"},
            {"User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 UBrowser/4.0.3214.0 Safari/537.36"},
            {"User-Agent": "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"},
            {"User-Agent": "Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"},
            {"User-Agent": "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5"},
            {"User-Agent": "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5"},
            {"User-Agent": "Mozilla/5.0 (Linux; U; Android 2.2.1; zh-cn; HTC_Wildfire_A3333 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"},
            {"User-Agent": "Mozilla/5.0 (Linux; U; Android 2.2.1; zh-cn; HTC_Wildfire_A3333 Build/FRG83D) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"},
            {"User-Agent": "Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10"},
            {"User-Agent": "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13"},
            {"User-Agent": "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+"},
            {"User-Agent": "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0"},
            {"User-Agent": "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0"},
            {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)"},
            {"User-Agent": "UCWEB7.0.2.37/28/999"},
            {"User-Agent": "NOKIA5700/ UCWEB7.0.2.37/28/999"},
            {"User-Agent": "Openwave/ UCWEB7.0.2.37/28/999"},
        ]
        return random.choice(headers_list)


# 调试输出()
# req = MyUserAndIP()
# req.update_ip()
# for _ in range(1, 10):
#     sip = req.rand_ip()
#     ip = sip.get("http", "-1")
#     if ip == "-1":
#         ip = sip.get("https", "http://192.168.200.133:80")
#     print(ip)


# chromeOptions = webdriver.ChromeOptions()
# # 设置代理
# chromeOptions.add_argument("--proxy-server=" + ip)
# # 一定要注意,=两边不能有空格,不能是这样--proxy-server = http://202.20.16.82:10152
# browser = webdriver.Chrome(chrome_options=chromeOptions)

测试效果

完结...

5 条评论

Watch Jav Free HD Watch Jav Free HD

Simply desire to say your article is as surprising.
The clarity in your post is just nice and i could assume you're an expert on this subject.
Well with your permission allow me to grab your feed to keep up to date with
forthcoming post. Thanks a million and please carry on the gratifying work.

回复
Vape Mods Sale Vape Mods Sale

Hi, I think your site might be having browser compatibility issues.
When I look at your website in Opera, it looks
fine but when opening in Internet Explorer, it has some overlapping.
I just wanted to give you a quick heads up! Other then that, fantastic blog!

回复
python with flask example python with flask example

An outstanding share! I have just forwarded this onto a co-worker who was conducting
a little research on this. And he actually ordered me lunch simply because I discovered it for him...

So let me reword this.... Thank YOU for the meal!! But yeah, thanx for spending the time to discuss this subject here on your blog.

回复

发表评论

邮箱地址不会被公开。 必填项已用*标注