利用Python实现简单HTTP代理验证

󰃭 2017-03-05

原创作品:首发u3v3, 转载请保留

地址:https://www.u3v3.com/ar/1274

作者ID:noomrevils

首发日期:2017.3.5

Python学习群:278529278 (欢迎交流)

前言

需求

相信用python写过爬虫的人都有听说或使用过代理的经历,网络上提供了很多免费的代理,也有很多收费代理提取接口,但是实际在使用这些代理的时候,往往会有速度慢,无法连接,非高匿,插入广告或统计代码等种种问题,本文的初衷就是提供一个验证代理的思路和简版的代码实现,通过定时的任务可以验证代理的有效性,维护一个可用的代理池。

思路

为了实现验证的功能,需要有一个页面能够展示通过代理的请求信息,比如说,ip地址,请求头。 这些信息可以帮助判断代理是否高匿,同时检查这个页面返回html, 可以知道,代理是否向其中添加了额外的信息(广告,统计)

然后在client端,我们通过代理请求这个页面,解析返回的页面内容,完成代理可用性的验证

验证接口&页面实现

处理请求函数

Python的Web框架选用的是Bottle, 简单但能够说明问题。


from bottle import request, route, run, template

@route('/')
def index():
    headers = {}
    for key in request.headers.keys():
        headers[key] = request.headers.get(key)

    ip  = request.environ.get('HTTP_X_FORWARDED_FOR') \
      or request.environ.get('REMOTE_ADDR')
    return template('proxy', headers=headers, ip=ip)

run(host='0.0.0.0', port=8000)

Bottle中requests.headers是类似字典的class,为了能够在模板中使用请求头信息,把它拿到读取到python字典对象中,作为传入模板的参数,然后通过request.environ,可以拿到真实请求的ip

请求信息页面

<html>
	<head>
		<title>proxy</title>
		<style type="text/css">
			body	{font-family:arial, sans-serif;}
			table	{border-width: 1px; border-color: black; border-style: solid;}
			.head	{background-color:#6666ff; color:#ffffff;font-size:12pt;}
			.http-header	{background-color:#ffffff; color:#000000; border: 1px solid black;}
			.meta	{background-color:#eeeeee; color:#000000; border: 1px solid black;}
			.small	{font-size:9pt;}
		</style>
	</head>
	<body>
        <table border="0" cellpadding="2">
            <tr>
                <td class='meta'>Remote IP Address</td>
                <td class='meta'><b>{{ip}}</b></td>
            </tr>
            <tr>
                <th class="head">HTTP Header<b><sup>*</sup></b></th>
                <th class="head">Value</th>
            </tr>
            % for key, value in headers.items():
            <tr>
                <td class='http-header'>{{ key }}</td>
                <td class='http-header'><b>{{ value }}</b></td>
            </tr>
            % end
        </table>

</html>

模板中我们只使用了table元素,没有额外的div,或者javascript src 引入,这是为了后面检查代理服务器是否对页面进行了修改。

    ...
            % for key, value in headers.items():
            <tr>
                <td class='http-header'>{{ key }}</td>
                <td class='http-header'><b>{{ value }}</b></td>
            </tr>
            % end
    ...

采用了Bottle自带模板语法,相信使用过django template 或者 jinja2的同学也不会陌生。

把服务跑起来

整个项目的结构,如下:

├── app.py
└── views
    └── proxy.tpl

在终端执行 `python app.py` 就可以浏览器输入 http://127.0.0.1:8000 看到页面效果

验证请求端实现

有了代理请求访问的页面,下面来实现代理验证部分的内容,我们采用requests库来写一个验证脚本。 本篇的内容不包含如何获取免费代理,代理存储维护等细节,但是欢迎大家提出自己的想法和实现, 也许我们可以一起实现一个更好的版本


import requests
import re

ip_addr = re.compile(r'[0-9]+(?:\.[0-9]+){3}')
ad_inject = re.compile(r'iframe|src|div')
valided_proxies = []

headers = {
    "Host": "http://example.com/proxy_verify/",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:50.0) Gecko/20100101 Firefox/50.0",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "en-US,en;q=0.5",
    "Accept-Encoding": "gzip, deflate, br",
    "Content-Type": "text/plain",
    "Connection": "keep-alive"
}

url='http://example.com/proxy_verify/'

proxies = [
    "http://120.26.143.53:80",
    "http://112.74.72.1:8080",
    "http://60.178.165.222:8998",
    "http://121.8.170.53:9797",
    "http://144.0.68.46:808"
] 


def run():
    s = requests.session()
    s.headers.update(headers)
    for proxy in proxies:
        s.proxies.update({'http': proxy})
        r = s.get(url)
        if r.status_code == requests.codes.ok:
            content = r.text
            proxy_ip = ip_addr.search(proxy).group()

            match = ip_addr.search(content)
            request_ip = match.group() if match else ''

            if request_ip and proxy_ip == request_ip:
                if not ad_inject.search(content):
                    valided_proxies.append(proxy)

    print (valided_proxies)


if __name__ == '__main__':
    run()

脚本中proxies是写死的,可以动态的可以从文件,缓存,数据库,接口等别的形式加载,下面来看下处理部分的逻辑

...
    content = r.text
    proxy_ip = ip_addr.search(proxy).group()

    match = ip_addr.search(content)
    request_ip = match.group() if match else ''

    if request_ip and proxy_ip == request_ip:
        if not ad_inject.search(content):
            valided_proxies.append(proxy)
...

首先使用正则re.compile(r'[0-9]+(?:\.[0-9]+){3}')从代理地址中提取代理的地址。 这个地址是我们预期被接口显示的地址, 然后再从http请求返回的html中解析出来服务端真实记录的ip,将这两个ip相比较我们就可以知道代理是否高匿。

然后我们再通过正则 ad_inject = re.compile(r'iframe|src|div') 来判断页面是否被插入了可疑的element。 完成这两个步骤后,我们可以将这个代理记录到有效代理的列表中。后续可以用来更新原有存储介质。

TODO

上面的方案,我们只是给出的思路和一个乞丐版的实现。可以探索的点非常多。可以以此为基础 探寻下去,相信能够学习到更多的知识。

  • 使用异步的请求的框架,比如aiohttp,并行的验证大量代理地址。
  • 使用tornadoaiohttp等支持异步IO的web框架,提高接口页面的并发能力。
  • 验证代理的请求头信息,和计算代理的连接速度。
  • 做一个代理展示界面。把有效的代理可视化的展示出来。
  • 爬取免费代理,去除重复,存在合适的地方,redis or mongodb?
  • 支持https代理验证
  • 通过cron等工具设置定时任务

欢迎大家留言讨论,或者加入我们的Python的QQ学习群(278529278)讨论,给出自己的实现。